1 module caLib_util.image;
2 
3 import std.exception : Exception, enforce;
4 import std.file : thisExePath;
5 import std.stdio : writeln;
6 import std.conv : to;
7 import std.string : split;
8 import core.stdc.stdlib : exit;
9 import caLib_util.build : os, arch;
10 
11 public import derelict.freeimage.freeimage;
12 public import caLib_util.structs : Color;
13 
14 
15 
16 shared static this()
17 {
18 	DerelictFI.load();
19 
20     FreeImage_SetOutputMessage(&FreeImageErrorHandler);
21 }
22 
23 
24 
25 class Image
26 {
27 
28 private:
29 
30 	FIBITMAP* fiBitmap;
31 	
32 	int width;
33 	int height;
34 
35 public:
36 
37 	this(int width, int height)
38 	{
39 		this(width, height, 24);
40 	}
41 
42 	this(int width, int height, int bpp)
43 	in
44 	{
45 		assert(width >= 0);
46 		assert(height >= 0);
47 		assert(bpp == 24 || bpp == 32);
48 	}
49 	body
50 	{
51 		this.width = width;
52 		this.height = height;
53 
54 		fiBitmap = FreeImage_Allocate(width, height, bpp,
55 			0x00FF0000, 0x0000FF00, 0x000000FF);
56 
57 		enforce(fiBitmap != null, "Fatal error. Unknown error occured when"
58 			~ "createing the internal bitmap");
59 	}
60 
61 	this(FIBITMAP* fiBitmap)
62 	in
63 	{ assert(fiBitmap != null); }
64 	body
65 	{
66 		width = FreeImage_GetWidth(fiBitmap);
67 		height = FreeImage_GetHeight(fiBitmap);
68 
69 		if (FreeImage_GetBPP(fiBitmap) != 32)
70 		    fiBitmap = FreeImage_ConvertTo32Bits(fiBitmap);
71 
72 		this.fiBitmap = fiBitmap;
73 	}
74 
75 	static Image fromFile(string path)
76 	{
77 		FREE_IMAGE_FORMAT format = FIF_UNKNOWN;
78 
79 		format = FreeImage_GetFileType(cast(const char*) path, 0);
80 
81 		if(format == FIF_UNKNOWN)
82 			format = FreeImage_GetFIFFromFilename(cast(const char*) path);
83 
84 		enforce(format != FIF_UNKNOWN, "Unknown image format for file " ~ path);
85 
86 		enforce(FreeImage_FIFSupportsReading(format),
87 			"No suitable decoder for format file " ~ path);
88 
89 		FIBITMAP* fiBitmap = FreeImage_Load(format, cast(const char*) path, 0);
90 
91 		enforce(fiBitmap != null, "The image could not be read");
92 
93 		return new Image(fiBitmap);
94 	}
95 
96 	static Image fromColors(Color* colors, int width, int height)
97 	in
98 	{ assert(width >= 0 && height >= 0); }
99 	body
100 	{
101 		Image image = new Image(width, height);
102 
103 		foreach(col ; 0..width)
104 		{
105 			foreach(row ; 0..height)
106 			{
107 				image.setPixel(col, row, colors[row * width + col]);
108 			}
109 		}
110 
111 		return image;
112 	}
113 
114 	static Image fromColorValues(uint* values, int width, int height)
115 	in
116 	{ assert(width >= 0 && height >= 0); }
117 	body
118 	{
119 		Image image = new Image(width, height);
120 
121 		foreach(col ; 0..width)
122 		{
123 			foreach(row ; 0..height)
124 			{
125 				image.setPixel(col, row, Color(values[row * width + col]));
126 			}
127 		}
128 
129 		return image;
130 	}
131 
132 	~this()
133 	{
134 		FreeImage_Unload(fiBitmap);
135 	}
136 
137 	void saveToFile(string path)
138 	{
139 		enforce(path[path.length-1] != '.',
140 				"Error. Can't save as image. invalid path: \"" ~ path ~ "\"");
141 
142 		FREE_IMAGE_FORMAT format =
143 			FreeImage_GetFIFFromFilename(cast(const char*) path);
144 
145 		if(format == FIF_UNKNOWN)
146 			format = getFIFFromExstention(path);
147 
148 		enforce(format != FIF_UNKNOWN, "Error. Can't save as image."
149 			~ " Unknown image format or invalied path: " ~ "\"" ~ path ~ "\"");
150 
151 		int err = 
152 			FreeImage_Save(format, fiBitmap, cast(const char*) path, 0);
153 
154 		enforce(err == 1, "Error. Could not save image Error code: "
155 			~ to!string(err));
156 	}
157 
158 	int getWidth()
159 	{
160 		return width;
161 	}
162 
163 	int getHeight()
164 	{
165 		return height;
166 	}
167 
168 	Color getPixel(int x, int y)
169 	in 
170 	{ assert(x >= 0 && y >= 0); }
171 	body
172 	{
173 		RGBQUAD* fiColor = new RGBQUAD();
174 
175 		uint err = FreeImage_GetPixelColor(fiBitmap, x, height-y-1, fiColor);
176 
177 		assert(err == 1, "Fatal error. The internal bitmap structure is unable
178 			to read its pixel. Error code: "
179 			~ to!string(err));
180 
181 		return Color(fiColor.rgbRed, fiColor.rgbGreen, fiColor.rgbBlue,
182 			fiColor.rgbReserved);
183 	}
184 
185 	void setPixel(int x, int y, Color newColor)
186 	in
187 	{ assert(x >= 0 && y >= 0); }
188 	body
189 	{
190 		RGBQUAD fiColor = RGBQUAD();
191 		fiColor.rgbRed = newColor.r;
192 		fiColor.rgbGreen = newColor.g;
193 		fiColor.rgbBlue = newColor.b;
194 		fiColor.rgbReserved = newColor.a;
195 
196 		FreeImage_SetPixelColor(fiBitmap, x, height-y-1, &fiColor);
197 	}
198 
199 	void rescale(int newWidth, int newHeight)
200 	in
201 	{ assert(newWidth >= 0 && newHeight >= 0); }
202 	body
203 	{
204 		fiBitmap = FreeImage_Rescale(fiBitmap, newWidth, newHeight, FILTER_BOX);
205 
206 		enforce(fiBitmap != null, "Fatal error. The internal bitmap could not"
207 			~ "be rescaled.\nThis may be caused by a bit-depth that can't be"
208 			~ " handeled, or more likely: Not enough memory");
209 
210 		width = FreeImage_GetWidth(fiBitmap);
211 		height = FreeImage_GetHeight(fiBitmap);
212 	}
213 
214 	FIBITMAP* getFiBitmap()
215 	{
216 		return fiBitmap;
217 	}
218 
219 	invariant
220     {
221     	assert(width >= 0);
222     	assert(height >= 0);
223         assert(fiBitmap != null);
224     }
225 }
226 
227 
228 
229 private FREE_IMAGE_FORMAT getFIFFromExstention(string path)
230 {
231 	string exstention = path.split(".")[path.split.length];
232 	return
233 	[
234 		"png" : FIF_PNG,
235 	].get(exstention, FIF_UNKNOWN);
236 }
237 
238 
239 
240 extern (C) void FreeImageErrorHandler(FREE_IMAGE_FORMAT fif, const(char)* message) nothrow
241 {
242 	try
243 	{
244 		writeln("FreeImage error:", message);
245 	}
246 	catch(Exception e)
247 	{
248 		exit(-99);
249 	}
250 }