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();