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