1 module armos.graphics.image;
2 
3 import armos.graphics;
4 import armos.math;
5 /++
6     画像のファイルフォーマットを表します
7 +/
8 enum ImageFormat{
9     BMP     = 0,
10     ICO     = 1,
11     JPEG    = 2,
12     JNG     = 3,
13     KOALA   = 4,
14     LBM     = 5,
15     IFF     = LBM,
16     MNG     = 6,
17     PBM     = 7,
18     PBMRAW  = 8,
19     PCD     = 9,
20     PCX     = 10,
21     PGM     = 11,
22     PGMRAW  = 12,
23     PNG     = 13,
24     PPM     = 14,
25     PPMRAW  = 15,
26     RAS     = 16,
27     TARGA   = 17,
28     TIFF    = 18,
29     WBMP    = 19,
30     PSD     = 20,
31     CUT     = 21,
32     XBM     = 22,
33     XPM     = 23,
34     DDS     = 24,
35     GIF     = 25,
36     HDR     = 26,
37     FAXG3   = 27,
38     SGI     = 28,
39     EXR     = 29,
40     J2K     = 30,
41     JP2     = 31,
42     PFM     = 32,
43     PICT    = 33,
44     RAW     = 34
45 }
46 
47 
48 import derelict.freeimage.freeimage;
49 
50 /++
51     画像を表すクラスです.
52     load()で画像を読み込み,draw()で表示することができます.
53 +/
54 class Image {
55     public{
56         this(){
57             if(!isInitializedFreeImage){
58                 DerelictFI.load();
59                 FreeImage_Initialise();
60                 isInitializedFreeImage = true;
61             }
62             _material = (new DefaultMaterial);
63             _material.uniform("diffuse", Vector4f(1, 1, 1, 1));
64         }
65 
66         /++
67             Load image.
68 
69             画像を読み込みます.
70         +/
71         Image load(string pathInDataDir){
72             import std..string;
73             FIBITMAP * freeImageBitmap = null;
74             _bitmap = Bitmap!(char)();
75 
76             import armos.utils;
77             string fileName = absolutePath(pathInDataDir);
78 
79             FREE_IMAGE_FORMAT freeImageFormat = FIF_UNKNOWN;
80             freeImageFormat = FreeImage_GetFileType(fileName.toStringz , 0);
81 
82             if(freeImageFormat == FIF_UNKNOWN) {
83                 freeImageFormat = FreeImage_GetFIFFromFilename(fileName.toStringz);
84             }
85             if((freeImageFormat != FIF_UNKNOWN) && FreeImage_FIFSupportsReading(freeImageFormat)) {
86                 freeImageBitmap = FreeImage_Load(freeImageFormat, fileName.toStringz, 0);
87                 if (freeImageBitmap != null){
88                     _isLoaded = true;
89                 }
90             }
91 
92             if ( _isLoaded ){
93                 //TODO: bring freeImageBitmap to Bitmap
94                 bitmap(freeImageBitmap);
95             }
96 
97             if (freeImageBitmap != null){
98                 FreeImage_Unload(freeImageBitmap);
99             }
100 
101             allocate;
102             _material.texture("tex0", this._texture);
103             return this;
104         }
105 
106 
107         Image drawCropped(T)(
108                 in T x, in T y,
109                 in T startX, in T startY,
110                 in T endX, in T endY
111                 ){
112             drawCropped(x, y, T(0), startX, startY, endX, endY);
113             return this;
114         }
115 
116         Image drawCropped(T)(
117                 in T x, in T y, in T z,
118                 in T startX, in T startY,
119                 in T endX, in T endY
120                 ){
121             if(_isLoaded){
122                 import std.conv;
123                 _rect.texCoords[0] = Vector4f(startX.to!float/_texture.width, startY.to!float/_texture.height, 0, 1);
124                 _rect.texCoords[1] = Vector4f(startX.to!float/_texture.width, endY.to!float/_texture.height,   0, 1);
125                 _rect.texCoords[2] = Vector4f(endX.to!float/_texture.width,   endY.to!float/_texture.height,   0, 1);
126                 _rect.texCoords[3] = Vector4f(endX.to!float/_texture.width,   startY.to!float/_texture.height, 0, 1);
127                 
128                 // _rect.texCoords[0] = Vector4f(0, 0, 0, 1);
129                 // _rect.texCoords[1] = Vector4f(0, 1, 0, 1);
130                 // _rect.texCoords[2] = Vector4f(1,   1,   0, 1);
131                 // _rect.texCoords[3] = Vector4f(1,   ,   0, 1);
132 
133                 _rect.vertices[0] = Vector4f(0f,          0f,          0f, 1f);
134                 _rect.vertices[1] = Vector4f(0f,          endY-startY, 0f, 1f);
135                 _rect.vertices[2] = Vector4f(endX-startX, endY-startY, 0f, 1f);
136                 _rect.vertices[3] = Vector4f(endX-startX, 0f,          0f, 1f);
137 
138                 pushMatrix;
139                 translate(x, y, z);
140                 _material.begin;
141                 _rect.drawFill();
142                 _material.end;
143                 popMatrix;
144             }
145             
146             return this;
147         }
148 
149         /++
150             Draw image data which loaded in advance.
151 
152             読み込んだ画像データを画面に描画します.
153         +/
154         Image draw(T)(in T x, in T y, in T z = T(0)){
155             drawCropped(x, y, z, 0, 0, bitmap.width, bitmap.height);
156             return this;
157         }
158 
159         /++
160             Draw image data which loaded in advance.
161 
162             読み込んだ画像データを画面に描画します.
163         +/
164         Image draw(T)(in T position)const{
165             static if(position.data.length == 2)
166                 draw(position[0], position[1]);
167             else if(position.data.length == 3)
168                 draw(position[0], position[1], position[2]);
169             else
170                 static assert(0, "arg is invalid dimention");
171             return this;
172         }
173 
174         /++
175             Return image size.
176 
177             画像のサイズを返します.
178         +/
179         Vector2i size()const{return _size;}
180 
181         /++
182             Return width.
183 
184             画像の幅を返します.
185         +/
186         int width()const{return _size[0];}
187 
188         /++
189             Return height.
190 
191             画像の高さを返します.
192         +/
193         int height()const{return _size[1];}
194 
195         /++
196             Return bitmap pointer.
197 
198             画像のビットマップデータを返します.
199         +/
200         Bitmap!(char) bitmap(){return _bitmap;}
201 
202         /++
203             Set bitmap
204         +/
205         Image bitmap(Bitmap!(char) data){
206             _bitmap = data;
207             allocate();
208             _isLoaded = true;
209             _material.texture("tex0", this._texture);
210             return this;
211         }
212 
213         /++
214             Generate a image from the aligned pixels
215 
216             一次元配列からImageを生成します
217         +/
218         Image setFromAlignedPixels(T)(T* pixels, int width, int height, ColorFormat format){
219             _bitmap.setFromAlignedPixels(cast(char*)pixels, width, height, format);
220             allocate;
221             _isLoaded = true;
222             _material.texture("tex0", this._texture);
223             return this;
224         }
225 
226         /++
227             与えられたbitmapを元にtextureとrectを生成します
228         +/
229         Image allocate(){
230             _texture = new Texture;
231             _texture.allocate(_bitmap);
232             _rect = new Mesh;
233             _rect.primitiveMode = PrimitiveMode.TriangleStrip ;
234             float x = _bitmap.width;
235             float y = _bitmap.height;
236             
237             _rect.vertices = [
238                 Vector4f(0.0, 0.0, 0.0, 1.0f),
239                 Vector4f(0.0, y,   0.0, 1.0f),
240                 Vector4f(x,   y,   0.0, 1.0f),
241                 Vector4f(x,   0.0, 0.0, 1.0f),
242             ];
243             
244             _rect.texCoords0= [
245                 Vector4f(0f, 0f,  0f, 1f),
246                 Vector4f(0,                                1f*_bitmap.height/_texture.height,  0f, 1f),
247                 Vector4f(1f*_bitmap.width/_texture.width,  1f*_bitmap.height/_texture.height,  0f, 1f),
248                 Vector4f(1f*_bitmap.width/_texture.width,  0,  0f, 1f),
249             ];
250 
251             _rect.indices = [
252                 0, 1, 2,
253                 2, 3, 0,
254             ];
255             return this;
256         }
257 
258         /++
259             Retun true if the image was loaded.
260 
261             画像が読み込まれている場合trueを,そうでない場合はfalseを返します.
262         +/
263         bool isLoaded()const{
264             return _isLoaded;
265         }
266 
267         /++
268         +/
269         Texture texture(){
270             return _texture;
271         }
272 
273         /++
274         +/
275         Image minMagFilter(in TextureMinFilter minFilter, in TextureMagFilter magFilter){
276             _texture.minMagFilter(minFilter, magFilter);
277             return this;
278         }
279 
280         /++
281         +/
282         Image minFilter(in TextureMinFilter filter){
283             _texture.minFilter(filter);
284             return this;
285         }
286         
287         ///
288         Image magFilter(in TextureMagFilter filter){
289             _texture.magFilter(filter);
290             return this;
291         }
292         
293         ///
294         Material material(){
295             return _material;
296         }
297     }//public
298 
299     private{
300         static bool isInitializedFreeImage = false;
301         Vector2i _size;
302         Bitmap!(char) _bitmap;
303         Texture _texture;
304         Mesh _rect;
305         bool _isLoaded = false;
306         Material _material;
307 
308         /++
309             ImageのbitmapにfreeImageのbitmapを指定します.
310         +/
311         void bitmap(FIBITMAP* freeImageBitmap, bool swapForLittleEndian = true){
312             FREE_IMAGE_TYPE imageType = FreeImage_GetImageType(freeImageBitmap);
313 
314             uint bits = char.sizeof;
315 
316             import std.stdio;
317             FIBITMAP* bitmapConverted;
318             bitmapConverted = FreeImage_ConvertTo32Bits(freeImageBitmap);
319             freeImageBitmap = bitmapConverted;
320 
321             uint width = FreeImage_GetWidth(freeImageBitmap);
322             uint height = FreeImage_GetHeight(freeImageBitmap);
323             uint bpp = FreeImage_GetBPP(freeImageBitmap);
324             uint channels = (bpp / bits) / 8;
325             uint pitch = FreeImage_GetPitch(freeImageBitmap);
326 
327             ColorFormat armosColorFormat;
328             switch (channels) {
329                 case 1:
330                     armosColorFormat = ColorFormat.Gray;
331                     break;
332                 case 2:
333                     armosColorFormat = ColorFormat.Gray;
334                     break;
335                 case 3:
336                     armosColorFormat = ColorFormat.RGB;
337                     break;
338                 case 4:
339                     armosColorFormat = ColorFormat.RGBA;
340                     break;
341                 default:
342                     break;
343             }
344 
345             FreeImage_FlipVertical(freeImageBitmap);
346             char* bitmapBits = cast(char*)FreeImage_GetBits(freeImageBitmap);
347 
348             _bitmap.setFromAlignedPixels(bitmapBits, width, height, armosColorFormat);
349             _bitmap.swapRAndB;
350             _size = _bitmap.size;
351         }
352     }//private
353 }//class Image