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.texture; 21 22 23 import dsfml.graphics.rect; 24 import dsfml.graphics.image; 25 import dsfml.graphics.renderwindow; 26 27 import dsfml.window.window; 28 29 import dsfml.system.inputstream; 30 import dsfml.system.vector2; 31 import dsfml.system.err; 32 33 /++ 34 + Image living on the graphics card that can be used for drawing. 35 + 36 + Texture stores pixels that can be drawn, with a sprite for example. 37 + 38 + A texture lives in the graphics card memory, therefore it is very fast to draw a texture to a render target, or copy a render target to a texture (the graphics card can access both directly). 39 + 40 + Being stored in the graphics card memory has some drawbacks. A texture cannot be manipulated as freely as a Image, you need to prepare the pixels first and then upload them to the texture in a single operation (see Texture::update). 41 + 42 + Texture makes it easy to convert from/to Image, but keep in mind that these calls require transfers between the graphics card and the central memory, therefore they are slow operations. 43 + 44 + A texture can be loaded from an image, but also directly from a file/memory/stream. The necessary shortcuts are defined so that you don't need an image first for the most common cases. However, if you want to perform some modifications on the pixels before creating the final texture, you can load your file to a Image, do whatever you need with the pixels, and then call Texture::loadFromImage. 45 + 46 + Since they live in the graphics card memory, the pixels of a texture cannot be accessed without a slow copy first. And they cannot be accessed individually. Therefore, if you need to read the texture's pixels (like for pixel-perfect collisions), it is recommended to store the collision information separately, for example in an array of booleans. 47 + 48 + Like Image, Texture 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. 49 + 50 + Authors: Laurent Gomila, Jeremy DeHaan 51 + See_Also: http://www.sfml-dev.org/documentation/2.0/classsf_1_1Texture.php#details 52 +/ 53 class Texture 54 { 55 package sfTexture* sfPtr; 56 57 this() 58 { 59 sfPtr = sfTexture_construct(); 60 } 61 62 package this(sfTexture* texturePointer) 63 { 64 sfPtr = texturePointer; 65 } 66 67 ~this() 68 { 69 import dsfml.system.config; 70 mixin(destructorOutput); 71 sfTexture_destroy( sfPtr); 72 } 73 74 /** 75 * Load the texture from a file on disk. 76 * 77 * The area argument can be used to load only a sub-rectangle of the whole image. If you want the entire image then leave the default value (which is an empty IntRect). If the area rectangle crosses the bounds of the image, it is adjusted to fit the image size. 78 * 79 * The maximum size for a texture depends on the graphics driver and can be retrieved with the getMaximumSize function. 80 * 81 * If this function fails, the texture is left unchanged. 82 * 83 * Params: 84 * filename = Path of the image file to load 85 * area = Area of the image to load 86 * 87 * Returns: True if loading was successful, false otherwise. 88 */ 89 bool loadFromFile(string filename, IntRect area = IntRect() ) 90 { 91 import dsfml.system..string; 92 93 bool ret = sfTexture_loadFromFile(sfPtr, toStringz(filename) ,area.left, area.top,area.width, area.height); 94 if(!ret) 95 { 96 err.write(dsfml.system..string.toString(sfErr_getOutput())); 97 } 98 99 return ret; 100 } 101 102 //TODO: Can this be done with a slice of bytes rather than a const(void)*? 103 /** 104 * Load the texture from a file in memory. 105 * 106 * The area argument can be used to load only a sub-rectangle of the whole image. If you want the entire image then leave the default value (which is an empty IntRect). If the area rectangle crosses the bounds of the image, it is adjusted to fit the image size. 107 * 108 * The maximum size for a texture depends on the graphics driver and can be retrieved with the getMaximumSize function. 109 * 110 * If this function fails, the texture is left unchanged. 111 * 112 * Params: 113 * data = Image in memory 114 * size = Size of the data to load, in bytes. 115 * area = Area of the image to load 116 * 117 * Returns: True if loading was successful, false otherwise. 118 */ 119 bool loadFromMemory(const(void)[] data, IntRect area = IntRect()) 120 { 121 import dsfml.system..string; 122 123 124 bool ret = sfTexture_loadFromMemory(sfPtr, data.ptr, data.length,area.left, area.top,area.width, area.height); 125 if(!ret) 126 { 127 err.write(dsfml.system..string.toString(sfErr_getOutput())); 128 } 129 130 return ret; 131 } 132 133 /** 134 * Load the texture from a custom stream. 135 * 136 * The area argument can be used to load only a sub-rectangle of the whole image. If you want the entire image then leave the default value (which is an empty IntRect). If the area rectangle crosses the bounds of the image, it is adjusted to fit the image size. 137 * 138 * The maximum size for a texture depends on the graphics driver and can be retrieved with the getMaximumSize function. 139 * 140 * If this function fails, the texture is left unchanged. 141 * 142 * Params: 143 * stream = Source stream to read from 144 * area = Area of the image to load 145 * 146 * Returns: True if loading was successful, false otherwise. 147 */ 148 bool loadFromStream(InputStream stream, IntRect area = IntRect()) 149 { 150 import dsfml.system..string; 151 152 bool ret = sfTexture_loadFromStream(sfPtr, new textureStream(stream), area.left, area.top,area.width, area.height); 153 if(!ret) 154 { 155 err.write(dsfml.system..string.toString(sfErr_getOutput())); 156 } 157 158 return ret; 159 } 160 161 /** 162 * Load the texture from an image. 163 * 164 * The area argument can be used to load only a sub-rectangle of the whole image. If you want the entire image then leave the default value (which is an empty IntRect). If the area rectangle crosses the bounds of the image, it is adjusted to fit the image size. 165 * 166 * The maximum size for a texture depends on the graphics driver and can be retrieved with the getMaximumSize function. 167 * 168 * If this function fails, the texture is left unchanged. 169 * 170 * Params: 171 * image = Image to load into the texture 172 * area = Area of the image to load 173 * 174 * Returns: True if loading was successful, false otherwise. 175 */ 176 bool loadFromImage(Image image, IntRect area = IntRect()) 177 { 178 import dsfml.system..string; 179 180 bool ret = sfTexture_loadFromImage(sfPtr, image.sfPtr, area.left, area.top,area.width, area.height); 181 if(!ret) 182 { 183 err.write(dsfml.system..string.toString(sfErr_getOutput())); 184 } 185 186 return ret; 187 } 188 189 /** 190 * Get the maximum texture size allowed. 191 * 192 * This Maximum size is defined by the graphics driver. You can expect a value of 512 pixels for low-end graphics card, and up to 8192 pixels or more for newer hardware. 193 * 194 * Returns: Maximum size allowed for textures, in pixels. 195 */ 196 static uint getMaximumSize() 197 { 198 return sfTexture_getMaximumSize(); 199 } 200 201 /** 202 * Return the size of the texture. 203 * 204 * Returns: Size in pixels. 205 */ 206 Vector2u getSize() const 207 { 208 Vector2u temp; 209 sfTexture_getSize(sfPtr, &temp.x, &temp.y); 210 return temp; 211 } 212 213 /** 214 * Enable or disable the smooth filter. 215 * 216 * When the filter is activated, the texture appears smoother so that pixels are less noticeable. However if you want the texture to look exactly the same as its source file, you should leave it disabled. The smooth filter is disabled by default. 217 * 218 * Params: 219 * smooth = True to enable smoothing, false to disable it. 220 */ 221 void setSmooth(bool smooth) 222 { 223 sfTexture_setSmooth(sfPtr, smooth);//:sfTexture_setSmooth(sfPtr, sfFalse); 224 } 225 226 /** 227 * Enable or disable repeating. 228 * 229 * Repeating is involved when using texture coordinates outside the texture rectangle [0, 0, width, height]. In this case, if repeat mode is enabled, the whole texture will be repeated as many times as needed to reach the coordinate (for example, if the X texture coordinate is 3 * width, the texture will be repeated 3 times). 230 * 231 * If repeat mode is disabled, the "extra space" will instead be filled with border pixels. Warning: on very old graphics cards, white pixels may appear when the texture is repeated. With such cards, repeat mode can be used reliably only if the texture has power-of-two dimensions (such as 256x128). Repeating is disabled by default. 232 * 233 * Params: 234 * repeated = True to repeat the texture, false to disable repeating 235 */ 236 void setRepeated(bool repeated) 237 { 238 sfTexture_setRepeated(sfPtr, repeated);//:sfTexture_setRepeated(sfPtr, sfFalse); 239 } 240 241 /** 242 * Bind a texture for rendering. 243 * 244 * This function is not part of the graphics API, it mustn't be used when drawing SFML entities. It must be used only if you mix Texture with OpenGL code. 245 * 246 * Params: 247 * texture = The texture to bind. Can be null to use no texture. 248 */ 249 static void bind(Texture texture) 250 { 251 (texture is null)?sfTexture_bind(null):sfTexture_bind(texture.sfPtr); 252 } 253 254 /** 255 * Create the texture. 256 * 257 * If this function fails, the texture is left unchanged. 258 * 259 * Params: 260 * width = Width of the texture 261 * height = Height of the texture 262 * 263 * Returns: True if creation was successful, false otherwise. 264 */ 265 bool create(uint width, uint height) 266 { 267 import dsfml.system..string; 268 269 bool ret = sfTexture_create(sfPtr, width, height); 270 if(!ret) 271 { 272 err.write(dsfml.system..string.toString(sfErr_getOutput())); 273 } 274 275 return ret; 276 } 277 278 /** 279 * Copy the texture pixels to an image. 280 * 281 * This function performs a slow operation that downloads the texture's pixels from the graphics card and copies them to a new image, potentially applying transformations to pixels if necessary (texture may be padded or flipped). 282 * 283 * Returns: Image containing the texture's pixels. 284 */ 285 Image copyToImage() const 286 { 287 return new Image(sfTexture_copyToImage(sfPtr)); 288 } 289 290 /** 291 * Creates a new texture from the same data (this means copying the entire set of pixels). 292 * 293 * Returns: New texture data. 294 */ 295 @property 296 Texture dup() const 297 { 298 return new Texture(sfTexture_copy(sfPtr)); 299 } 300 301 /** 302 * Tell whether the texture is repeated or not. 303 * 304 * Returns: True if repeat mode is enabled, false if it is disabled. 305 */ 306 bool isRepeated() const 307 { 308 return (sfTexture_isRepeated(sfPtr));// == sfTrue)?true:false; 309 } 310 311 /** 312 * Tell whether the smooth filter is enabled or not. 313 * 314 * Returns: True if something is enabled, false if it is disabled. 315 */ 316 bool isSmooth() const 317 { 318 return (sfTexture_isSmooth(sfPtr));// == sfTrue)?true:false; 319 } 320 321 /** 322 * Update the texture from an image. 323 * 324 * Although the source image can be smaller than the texture, this function is usually used for updating the whole texture. The other overload, which has (x, y) additional arguments, is more convenient for updating a sub-area of the texture. 325 * 326 * No additional check is performed on the size of the image, passing an image bigger than the texture will lead to an undefined behaviour. 327 * 328 * This function does nothing if the texture was not previously created. 329 * 330 * Params: 331 * image = Image to copy to the texture. 332 */ 333 void updateFromImage(Image image, uint x, uint y) 334 { 335 sfTexture_updateFromImage(sfPtr, image.sfPtr, x, y); 336 } 337 338 /** 339 * Update part of the texture from an array of pixels. 340 * 341 * The size of the pixel array must match the width and height arguments, and it must contain 32-bits RGBA pixels. 342 * 343 * No additional check is performed on the size of the pixel array or the bounds of the area to update, passing invalid arguments will lead to an undefined behaviour. 344 * 345 * This function does nothing if pixels is null or if the texture was not previously created. 346 * 347 * Params: 348 * pixels = Array of pixels to copy to the texture. 349 * width = Width of the pixel region contained in pixels 350 * height = Height of the pixel region contained in pixels 351 * x = X offset in the texture where to copy the source pixels 352 * y = Y offset in the texture where to copy the source pixels 353 */ 354 void updateFromPixels(const(ubyte)[] pixels, uint width, uint height, uint x, uint y) 355 { 356 sfTexture_updateFromPixels(sfPtr,pixels.ptr,width, height, x,y); 357 } 358 359 //TODO: Get this working via inheritance?(so custom window classes can do it too) 360 /** 361 * Update a part of the texture from the contents of a window. 362 * 363 * No additional check is performed on the size of the window, passing an invalid combination of window size and offset will lead to an undefined behaviour. 364 * 365 * This function does nothing if either the texture or the window was not previously created. 366 * 367 * Params: 368 * window = Window to copy to the texture 369 * x = X offset in the texture where to copy the source window 370 * y = Y offset in the texture where to copy the source window 371 */ 372 void updateFromWindow(Window window, uint x, uint y) 373 { 374 sfTexture_updateFromWindow(sfPtr, RenderWindow.windowPointer(window), x, y); 375 } 376 377 //Is this even safe? RenderWindow inherits from Window, so what happens? Is this bottom used or the top? 378 /** 379 * Update a part of the texture from the contents of a window. 380 * 381 * No additional check is performed on the size of the window, passing an invalid combination of window size and offset will lead to an undefined behaviour. 382 * 383 * This function does nothing if either the texture or the window was not previously created. 384 * 385 * Params: 386 * window = Window to copy to the texture 387 * x = X offset in the texture where to copy the source window 388 * y = Y offset in the texture where to copy the source window 389 */ 390 void updateFromWindow(RenderWindow window, uint x, uint y) 391 { 392 sfTexture_updateFromRenderWindow(sfPtr, window.sfPtr, x, y); 393 } 394 } 395 396 unittest 397 { 398 version(DSFML_Unittest_Graphics) 399 { 400 import std.stdio; 401 402 writeln("Unit test for Texture"); 403 404 auto texture = new Texture(); 405 406 assert(texture.loadFromFile("res/TestImage.png")); 407 408 //do things with the texture 409 410 writeln(); 411 } 412 } 413 414 private extern(C++) interface textureInputStream 415 { 416 long read(void* data, long size); 417 418 long seek(long position); 419 420 long tell(); 421 422 long getSize(); 423 } 424 425 426 private class textureStream:textureInputStream 427 { 428 private InputStream myStream; 429 430 this(InputStream stream) 431 { 432 myStream = stream; 433 } 434 435 extern(C++)long read(void* data, long size) 436 { 437 return myStream.read(data[0..cast(size_t)size]); 438 } 439 440 extern(C++)long seek(long position) 441 { 442 return myStream.seek(position); 443 } 444 445 extern(C++)long tell() 446 { 447 return myStream.tell(); 448 } 449 450 extern(C++)long getSize() 451 { 452 return myStream.getSize(); 453 } 454 } 455 456 457 458 package extern(C) struct sfTexture; 459 460 private extern(C): 461 462 //Construct a new texture 463 sfTexture* sfTexture_construct(); 464 465 //Create a new texture 466 bool sfTexture_create(sfTexture* texture, uint width, uint height); 467 468 //Create a new texture from a file 469 bool sfTexture_loadFromFile(sfTexture* texture, const(char)* filename, int left, int top, int width, int height); 470 471 //Create a new texture from a file in memory 472 bool sfTexture_loadFromMemory(sfTexture* texture, const(void)* data, size_t sizeInBytes, int left, int top, int width, int height); 473 474 //Create a new texture from a custom stream 475 bool sfTexture_loadFromStream(sfTexture* texture, textureInputStream stream, int left, int top, int width, int height); 476 477 //Create a new texture from an image 478 bool sfTexture_loadFromImage(sfTexture* texture, const(sfImage)* image, int left, int top, int width, int height); 479 480 //Copy an existing texture 481 sfTexture* sfTexture_copy(const(sfTexture)* texture); 482 483 //Destroy an existing texture 484 void sfTexture_destroy(sfTexture* texture); 485 486 //Return the size of the texture 487 void sfTexture_getSize(const(sfTexture)* texture, uint* x, uint* y); 488 489 //Copy a texture's pixels to an image 490 sfImage* sfTexture_copyToImage(const sfTexture* texture); 491 492 //Update a texture from an array of pixels 493 void sfTexture_updateFromPixels(sfTexture* texture, const ubyte* pixels, uint width, uint height, uint x, uint y); 494 495 //Update a texture from an image 496 void sfTexture_updateFromImage(sfTexture* texture, const sfImage* image, uint x, uint y); 497 498 //Update a texture from the contents of a window 499 void sfTexture_updateFromWindow(sfTexture* texture, const(void)* window, uint x, uint y); 500 501 //Update a texture from the contents of a render-window 502 void sfTexture_updateFromRenderWindow(sfTexture* texture, const sfRenderWindow* renderWindow, uint x, uint y); 503 504 //Enable or disable the smooth filter on a texture 505 void sfTexture_setSmooth(sfTexture* texture, bool smooth); 506 507 //Tell whether the smooth filter is enabled or not for a texture 508 bool sfTexture_isSmooth(const sfTexture* texture); 509 510 //Enable or disable repeating for a texture 511 void sfTexture_setRepeated(sfTexture* texture, bool repeated); 512 513 //Tell whether a texture is repeated or not 514 bool sfTexture_isRepeated(const sfTexture* texture); 515 516 //Bind a texture for rendering 517 void sfTexture_bind(const sfTexture* texture); 518 519 //Get the maximum texture size allowed 520 uint sfTexture_getMaximumSize(); 521 522 const(char)* sfErr_getOutput();