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