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