1 /*
2 DSFML - The Simple and Fast Multimedia Library for D
3 
4 Copyright (c) 2013 - 2015 Jeremy DeHaan (dehaan.jeremiah@gmail.com)
5 
6 This software is provided 'as-is', without any express or implied warranty.
7 In no event will the authors be held liable for any damages arising from the use of this software.
8 
9 Permission is granted to anyone to use this software for any purpose, including commercial applications,
10 and to alter it and redistribute it freely, subject to the following restrictions:
11 
12 1. The origin of this software must not be misrepresented; you must not claim that you wrote the original software.
13 If you use this software in a product, an acknowledgment in the product documentation would be appreciated but is not required.
14 
15 2. Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software.
16 
17 3. This notice may not be removed or altered from any source distribution
18 */
19 
20 module dsfml.graphics.image;
21 
22 import dsfml.system.vector2;
23 
24 import dsfml.graphics.color;
25 import dsfml.system.inputstream;
26 
27 import dsfml.graphics.rect;
28 
29 import dsfml.system.err;
30 
31 /++
32  + Class for loading, manipulating and saving images.
33  + 
34  + Image is an abstraction to manipulate images as bidimensional arrays of pixels.
35  + 
36  + The class provides functions to load, read, write and save pixels, as well as many other useful functions.
37  + 
38  + Image can handle a unique internal representation of pixels, which is RGBA 32 bits. This means that a pixel must be composed of 8 bits red, green, blue and alpha channels – just like a Color. All the functions that return an array of pixels follow this rule, and all parameters that you pass to Image functions (such as loadFromPixels) must use this representation as well.
39  + 
40  + A Image can be copied, but it is a heavy resource and if possible you should always use [const] references to pass or return them to avoid useless copies.
41  + 
42  + Authors: Laurent Gomila, Jeremy DeHaan
43  + See_Also: http://www.sfml-dev.org/documentation/2.0/classsf_1_1Image.php#details
44  +/
45 class Image
46 {
47 	package sfImage* sfPtr;
48 	
49 	this()
50 	{
51 		sfPtr = sfImage_construct();
52 	}
53 	
54 	package this(sfImage* image)
55 	{
56 		sfPtr = image;
57 	}
58 	
59 	~this()
60 	{
61 		import dsfml.system.config;
62 		mixin(destructorOutput);
63 		sfImage_destroy(sfPtr);
64 	}
65 
66 	/**
67 	 * Create the image and fill it with a unique color.
68 	 * 
69 	 * Params:
70 	 * 		width	= Width of the image
71 	 * 		height	= Height of the image
72 	 * 		color	= Fill color
73 	 * 
74 	 */
75 	void create(uint width, uint height, Color color)
76 	{
77 
78 		sfImage_createFromColor(sfPtr, width, height,color.r, color.b, color.g, color.a);
79 	}
80 	
81 	/**
82 	 * Create the image from an array of pixels.
83 	 * 
84 	 * The pixel array is assumed to contain 32-bits RGBA pixels, and have the given width and height. If not, this is an undefined behaviour. If pixels is null, an empty image is created.
85 	 * 
86 	 * Params:
87 	 * 		width	= Width of the image
88 	 * 		height	= Height of the image
89 	 * 		pixels	= Array of pixels to copy to the image
90 	 * 
91 	 */
92 	void create(uint width, uint height, const ref ubyte[] pixels)
93 	{
94 		sfImage_createFromPixels(sfPtr, width, height,pixels.ptr);
95 	}
96 
97 	/**
98 	 * Load the image from a file on disk.
99 	 * 
100 	 * The supported image formats are bmp, png, tga, jpg, gif, psd, hdr and pic. Some format options are not supported, like progressive jpeg. If this function fails, the image is left unchanged.
101 	 * 
102 	 * Params:
103 	 * 		filename	= Path of the image file to load
104 	 * 
105 	 * Returns: True if loading succeeded, false if it failed
106 	 */
107 	bool loadFromFile(string fileName)
108 	{
109 		import dsfml.system..string;
110 
111 		bool ret = sfImage_loadFromFile(sfPtr, toStringz(fileName));
112 
113 		if(!ret)
114 		{
115 			err.write(dsfml.system..string.toString(sfErr_getOutput()));
116 		}
117 
118 		return ret;
119 	}
120 
121 	/**
122 	 * Load the image from a file in memory.
123 	 * 
124 	 * The supported image formats are bmp, png, tga, jpg, gif, psd, hdr and pic. Some format options are not supported, like progressive jpeg. If this function fails, the image is left unchanged.
125 	 * 
126 	 * Params:
127 	 * 		data	= Data file in memory to load
128 	 * 
129 	 * Returns: True if loading succeeded, false if it failed
130 	 */
131 	bool loadFromMemory(const(void)[] data)
132 	{
133 		import dsfml.system..string;
134 
135 		bool ret = sfImage_loadFromMemory(sfPtr, data.ptr, data.length);
136 		if(!ret)
137 		{
138 			err.write(dsfml.system..string.toString(sfErr_getOutput()));
139 		}
140 		
141 		return ret;
142 	}
143 
144 	/**
145 	 * Load the image from a custom stream.
146 	 * 
147 	 * The supported image formats are bmp, png, tga, jpg, gif, psd, hdr and pic. Some format options are not supported, like progressive jpeg. If this function fails, the image is left unchanged.
148 	 * 
149 	 * Params:
150 	 * 		stream	= Source stream to read from
151 	 * 
152 	 * Returns: True if loading succeeded, false if it failed
153 	 */
154 	bool loadFromStream(InputStream stream)
155 	{
156 		import dsfml.system..string;
157 
158 		bool ret = sfImage_loadFromStream(sfPtr, new imageStream(stream));
159 		if(!ret)
160 		{
161 			err.write(dsfml.system..string.toString(sfErr_getOutput()));
162 		}
163 
164 		return ret;
165 	}
166 
167 	/**
168 	 * Get the color of a pixel
169 	 * 
170 	 * This function doesn't check the validity of the pixel coordinates; using out-of-range values will result in an undefined behaviour.
171 	 * 
172 	 * Params:
173 	 * 		x	= X coordinate of the pixel to get
174 	 * 		y	= Y coordinate of the pixel to get
175 	 * 
176 	 * Returns: Color of the pixel at coordinates (x, y)
177 	 */
178 	Color getPixel(uint x, uint y)
179 	{ 
180 		Color temp;
181 		sfImage_getPixel(sfPtr, x,y, &temp.r, &temp.b, &temp.g, &temp.a);
182 		return temp;
183 	}
184 
185 	/**
186 	 * Get the read-only array of pixels that make up the image.
187 	 * 
188 	 * The returned value points to an array of RGBA pixels made of 8 bits integers components. The size of the array is width * height * 4 (getSize().x * getSize().y * 4). Warning: the returned pointer may become invalid if you modify the image, so you should never store it for too long.
189 	 * 
190 	 * Returns: Read-only array of pixels that make up the image.
191 	 */
192 	const(ubyte)[] getPixelArray()
193 	{
194 		Vector2u size = getSize();
195 		int length = size.x * size.y * 4;
196 		
197 		if(length!=0)
198 		{
199 			return sfImage_getPixelsPtr(sfPtr)[0..length];
200 		}
201 		else
202 		{
203 			err.writeln("Trying to access the pixels of an empty image");
204 			return [];
205 		}
206 	}
207 
208 	/**
209 	 * Return the size (width and height) of the image.
210 	 * 
211 	 * Returns: Size of the image, in pixels.
212 	 */
213 	Vector2u getSize()
214 	{
215 		Vector2u temp;
216 		sfImage_getSize(sfPtr,&temp.x, &temp.y);
217 		return temp;
218 	}
219 
220 	/**
221 	 * Change the color of a pixel.
222 	 * 
223 	 * This function doesn't check the validity of the pixel coordinates, using out-of-range values will result in an undefined behaviour.
224 	 * 
225 	 * Params:
226 	 * 		x		= X coordinate of pixel to change
227 	 * 		y		= Y coordinate of pixel to change
228 	 * 		color	= New color of the pixel
229 	 */
230 	void setPixel(uint x, uint y, Color color)
231 	{
232 		sfImage_setPixel(sfPtr, x,y,color.r, color.b,color.g, color.a);
233 	}
234 
235 	/**
236 	 * Copy pixels from another image onto this one.
237 	 * 
238 	 * This function does a slow pixel copy and should not be used intensively. It can be used to prepare a complex static image from several others, but if you need this kind of feature in real-time you'd better use RenderTexture.
239 	 * 
240 	 * If sourceRect is empty, the whole image is copied. If applyAlpha is set to true, the transparency of source pixels is applied. If it is false, the pixels are copied unchanged with their alpha value.
241 	 * 
242 	 * Params:
243 	 * 		source		= Source image to copy
244 	 * 		destX		= X coordinate of the destination position
245 	 * 		destY		= Y coordinate of the destination position
246 	 * 		sourceRect	= Sub-rectangle of the source image to copy
247 	 * 		applyAlpha	= Should the copy take the source transparency into account?
248 	 */
249 	void copyImage(const ref Image source, uint destX, uint destY, IntRect sourceRect = IntRect(0,0,0,0), bool applyAlpha = false)
250 	{
251 		sfImage_copyImage(sfPtr, source.sfPtr, destX, destY,sourceRect.left, sourceRect.top, sourceRect.width, sourceRect.height, applyAlpha);//:sfImage_copyImage(sfPtr, source.sfPtr, destX, destY, temp, sfFalse);
252 	}
253 
254 	/**
255 	 * Create a transparency mask from a specified color-key.
256 	 * 
257 	 * This function sets the alpha value of every pixel matching the given color to alpha (0 by default) so that they become transparent.
258 	 * 
259 	 * Params:
260 	 * 		color	= Color to make transparent
261 	 * 		alpha	= Alpha value to assign to transparent pixels
262 	 */
263 	void createMaskFromColor(Color maskColor, ubyte alpha = 0)
264 	{
265 		sfImage_createMaskFromColor(sfPtr,maskColor.r,maskColor.b, maskColor.g, maskColor.a, alpha);
266 	}
267 	
268 	@property
269 	Image dup() const
270 	{
271 		return new Image(sfImage_copy(sfPtr));
272 	}
273 
274 	/// Flip the image horizontally (left <-> right)
275 	void flipHorizontally()
276 	{
277 		sfImage_flipHorizontally(sfPtr);
278 	}
279 
280 	/// Flip the image vertically (top <-> bottom)
281 	void flipVertically()
282 	{
283 		sfImage_flipVertically(sfPtr);
284 	}
285 
286 	/**
287 	 * Save the image to a file on disk.
288 	 * 
289 	 * The format of the image is automatically deduced from the extension. The supported image formats are bmp, png, tga and jpg. The destination file is overwritten if it already exists. This function fails if the image is empty.
290 	 * 
291 	 * Params:
292 	 * 		filename	= Path of the file to save
293 	 * 
294 	 * Returns: True if saving was successful
295 	 */
296 	bool saveToFile(string fileName)
297 	{
298 		import dsfml.system..string;
299 		bool toReturn = sfImage_saveToFile(sfPtr, toStringz(fileName));
300 		err.write(dsfml.system..string.toString(sfErr_getOutput()));
301 		return toReturn;
302 	}
303 }
304 
305 unittest
306 {
307 	version(DSFML_Unittest_Graphics)
308 	{
309 		import std.stdio;
310 
311 		writeln("Unit test for Image");
312 
313 		auto image = new Image();
314 
315 		image.create(100,100,Color.Blue);
316 
317 		assert(image.getPixel(0,0) == Color.Blue);
318 
319 		image.setPixel(0,0,Color.Green);
320 
321 		assert(image.getPixel(0,0) == Color.Green);
322 
323 
324 		image.flipHorizontally();
325 
326 		assert(image.getPixel(99,0) == Color.Green);
327 
328 		image.flipVertically();
329 
330 		assert(image.getPixel(99,99) == Color.Green);
331 
332 		assert(image.getSize() == Vector2u(100,100));
333 
334 		writeln();
335 	}
336 }
337 
338 
339 private extern(C++) interface imageInputStream
340 {
341 	long read(void* data, long size);
342 	
343 	long seek(long position);
344 	
345 	long tell();
346 	
347 	long getSize();
348 }
349 
350 
351 private class imageStream:imageInputStream
352 {
353 	private InputStream myStream;
354 	
355 	this(InputStream stream)
356 	{
357 		myStream = stream;
358 	}
359 	
360 	extern(C++)long read(void* data, long size)
361 	{
362 		return myStream.read(data[0..cast(size_t)size]);
363 	}
364 	
365 	extern(C++)long seek(long position)
366 	{
367 		return myStream.seek(position);
368 	}
369 	
370 	extern(C++)long tell()
371 	{
372 		return myStream.tell();
373 	}
374 	
375 	extern(C++)long getSize()
376 	{
377 		return myStream.getSize();
378 	}
379 }
380 
381 
382 package extern(C) struct sfImage;
383 
384 private extern(C):
385 
386 //Construct a new image
387 sfImage* sfImage_construct();
388 
389 void sfImage_create(sfImage* image, uint width, uint height);
390 
391 /// \brief Create an image and fill it with a unique color
392 void sfImage_createFromColor(sfImage* image, uint width, uint height, ubyte r, ubyte b, ubyte g, ubyte a);
393 
394 /// \brief Create an image from an array of pixels
395 void sfImage_createFromPixels(sfImage* image, uint width, uint height, const(ubyte)* pixels);
396 
397 /// \brief Create an image from a file on disk
398 bool sfImage_loadFromFile(sfImage* image, const(char)* filename);
399 
400 /// \brief Create an image from a file in memory
401 bool sfImage_loadFromMemory(sfImage* image, const(void)* data, size_t size);
402 
403 /// \brief Create an image from a custom stream
404 bool sfImage_loadFromStream(sfImage* image, imageInputStream stream);
405 
406 /// \brief Copy an existing image
407 sfImage* sfImage_copy(const(sfImage)* image);
408 
409 /// \brief Destroy an existing image
410 void sfImage_destroy(sfImage* image);
411 
412 /// \brief Save an image to a file on disk
413 bool sfImage_saveToFile(const sfImage* image, const char* filename);
414 
415 /// \brief Return the size of an image
416 void sfImage_getSize(const sfImage* image, uint* width, uint* height);
417 
418 /// \brief Create a transparency mask from a specified color-key
419 void sfImage_createMaskFromColor(sfImage* image, ubyte r, ubyte b, ubyte g, ubyte a, ubyte alpha);
420 
421 /// \brief Copy pixels from an image onto another
422 void sfImage_copyImage(sfImage* image, const(sfImage)* source, uint destX, uint destY, int sourceRectTop, int sourceRectLeft, int sourceRectWidth, int sourceRectHeight, bool applyAlpha);
423 
424 /// \brief Change the color of a pixel in an image
425 void sfImage_setPixel(sfImage* image, uint x, uint y, ubyte r, ubyte b, ubyte g, ubyte a);
426 
427 /// \brief Get the color of a pixel in an image
428 void sfImage_getPixel(const sfImage* image, uint x, uint y, ubyte* r, ubyte* b, ubyte* g, ubyte* a);
429 
430 /// \brief Get a read-only pointer to the array of pixels of an image
431 const(ubyte)* sfImage_getPixelsPtr(const sfImage* image);
432 
433 /// \brief Flip an image horizontally (left <-> right)
434 void sfImage_flipHorizontally(sfImage* image);
435 
436 /// \brief Flip an image vertically (top <-> bottom)
437 void sfImage_flipVertically(sfImage* image);
438 
439 const(char)* sfErr_getOutput();