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