1 /*
2  *******************************************************************************************
3  * Dgame (a D game framework) - Copyright (c) Randy Schütt
4  * 
5  * This software is provided 'as-is', without any express or implied warranty.
6  * In no event will the authors be held liable for any damages arising from
7  * the use of this software.
8  * 
9  * Permission is granted to anyone to use this software for any purpose,
10  * including commercial applications, and to alter it and redistribute it
11  * freely, subject to the following restrictions:
12  * 
13  * 1. The origin of this software must not be misrepresented; you must not claim
14  *    that you wrote the original software. If you use this software in a product,
15  *    an acknowledgment in the product documentation would be appreciated but is
16  *    not required.
17  * 
18  * 2. Altered source versions must be plainly marked as such, and must not be
19  *    misrepresented as being the original software.
20  * 
21  * 3. This notice may not be removed or altered from any source distribution.
22  *******************************************************************************************
23  */
24 module Dgame.Graphics.Surface;
25 
26 private {
27 	debug import std.stdio : writefln, writeln;
28 	import std..string : format, toStringz;
29 	import std.file : exists;
30 	import std.conv : to;
31 	import std.algorithm : reverse;
32 	import std.exception : enforce;
33 	import core.stdc..string : memcpy;
34 	
35 	import derelict.sdl2.sdl;
36 	import derelict.sdl2.image;
37 	
38 	import Dgame.Internal.Shared;
39 	
40 	import Dgame.Math.Rect;
41 	import Dgame.Math.Vector2;
42 	import Dgame.Graphics.Color;
43 }
44 
45 /**
46  * Surface is a wrapper for a SDL_Surface.
47  *
48  * Author: rschuett
49  */
50 struct Surface {
51 	/**
52 	 * Supported BlendModes
53 	 */
54 	enum BlendMode : ubyte {
55 		None   = SDL_BLENDMODE_NONE,	/** no blending */
56 		Blend  = SDL_BLENDMODE_BLEND,	/** dst = (src * A) + (dst * (1-A)) */
57 		Add    = SDL_BLENDMODE_ADD,		/** dst = (src * A) + dst */
58 		Mod    = SDL_BLENDMODE_MOD		/** dst = src * dst */
59 	}
60 	
61 	/**
62 	 * Supported Color Masks
63 	 */
64 	enum Mask : ubyte {
65 		Red   = 1,	/** Red Mask */
66 		Green = 2,	/** Green Mask */
67 		Blue  = 4,	/** Blue Mask */
68 		Alpha = 8	/** Alpha Mask */
69 	}
70 	
71 	enum RMask = 0; /** Default Red Mask. */
72 	enum GMask = 0; /** Default Green Mask. */
73 	enum BMask = 0; /** Default Blue Mask. */
74 	
75 	version(LittleEndian) {
76 		enum AMask = 0xff000000;
77 	} else {
78 		enum AMask = 0x000000ff;
79 	}
80 	
81 	/**
82 	 * Flip mode
83 	 */
84 	enum Flip : ubyte {
85 		Vertical   = 1, /** Vertical Flip */
86 		Horizontal = 2  /** Horizontal Flip */
87 	}
88 	
89 private:
90 	shared_ptr!(SDL_Surface) _target;
91 	string _filename;
92 	
93 private:
94 	/**
95 	 * Create a new SDL_Surface* of the given width, height and depth.
96 	 */
97 	static SDL_Surface* create(ushort width, ushort height, ubyte depth = 32) in {
98 		assert(depth >= 8 && depth <= 32, "Invalid depth.");
99 	} body {
100 		return SDL_CreateRGBSurface(0, width, height, depth, RMask, GMask, BMask, AMask);
101 	}
102 	
103 	/**
104 	 * Create a new SDL_Surface* of the given memory, width, height and depth.
105 	 */
106 	static SDL_Surface* create(void* memory, ushort width, ushort height, ubyte depth = 32) in {
107 		assert(memory !is null, "Memory is empty.");
108 		assert(depth >= 8 && depth <= 32, "Invalid depth.");
109 	} body {
110 		return SDL_CreateRGBSurfaceFrom(memory, width, height, depth, (depth / 8) * width,
111 		                                RMask, GMask, BMask, AMask);
112 	}
113 	
114 	/**
115 	 * CTor
116 	 */
117 	this(SDL_Surface* srfc) in {
118 		assert(srfc !is null, "Invalid SDL_Surface.");
119 		assert(srfc.pixels !is null, "Invalid pixel data.");
120 	} body {
121 		debug writeln("CTor Surface with SDL_Surface: ", srfc);
122 		this._target = make_shared(srfc, (SDL_Surface* ptr) => SDL_FreeSurface(ptr));
123 	}
124 	
125 public:
126 	/**
127 	 * CTor
128 	 */
129 	this(string filename) {
130 		debug writeln("CTor Surface : ", filename);
131 		this.loadFromFile(filename);
132 	}
133 	
134 	debug(Dgame)
135 	this(this) {
136 		writeln("Postblit Surface: ", this._target.usage, ':', this.filename);
137 	}
138 	
139 	debug(Dgame)
140 	~this() {
141 		writeln("DTor Surface", ':', this.filename, "::",this._target.usage);
142 	}
143 	
144 	/**
145 	 * Destroy the current Surface <b>and all</b>, which are linked to this Surface</b>.
146 	 */
147 	void free() {
148 		debug writeln("Free Surface:", this.filename);
149 		this._target.terminate();
150 	}
151 	
152 	/**
153 	 * Returns the current use count
154 	 */
155 	int useCount() const pure nothrow {
156 		return this._target.usage;
157 	}
158 	
159 	/**
160 	 * Make a new Surface of the given width, height and depth.
161 	 */
162 	static Surface make(ushort width, ushort height, ubyte depth = 32) {
163 		SDL_Surface* srfc = Surface.create(width, height, depth);
164 		if (srfc is null) {
165 			const string err = to!string(SDL_GetError());
166 			throw new Exception("Surface konnte nicht erstellt werden: " ~ err);
167 		}
168 		
169 		return Surface(srfc);
170 	}
171 	
172 	/**
173 	 * Make an new Surface of the given memory, width, height and depth.
174 	 */
175 	static Surface make(void* memory, ushort width, ushort height, ubyte depth = 32) {
176 		SDL_Surface* srfc = Surface.create(memory, width, height, depth);
177 		if (srfc is null) {
178 			const string err = to!string(SDL_GetError());
179 			throw new Exception("Surface konnte nicht erstellt werden: " ~ err);
180 		}
181 		
182 		return Surface(srfc);
183 	}
184 	
185 	/**
186 	 * Returns if the Surface is valid. Which means that the Surface has valid data.
187 	 */
188 	bool isValid() const pure nothrow {
189 		return this._target.isValid() && this._target.pixels !is null;
190 	}
191 	
192 	/**
193 	 * Load from filename. If any data is already stored, the data will be freed.
194 	 */
195 	void loadFromFile(string filename) {
196 		debug writefln("Load Image: %s", filename);
197 		enforce(filename.length >= 4 && exists(filename),
198 		        "The file " ~ filename ~ " does not exist.");
199 
200 		SDL_Surface* srfc = IMG_Load(toStringz(filename));
201 		debug writefln("Image %s loaded :: %X", filename, srfc);
202 		if (srfc is null) {
203 			const string err = to!string(SDL_GetError());
204 			throw new Exception(.format("Could not load image %s. Error: %s.", filename, err));
205 		}
206 		
207 		this._target = make_shared(srfc, (SDL_Surface* ptr) => SDL_FreeSurface(ptr));
208 		this._filename = filename;
209 	}
210 	
211 	/**
212 	 * Load from memory.
213 	 */
214 	void loadFromMemory(void* memory, ushort width, ushort height, ubyte depth = 32) in {
215 		assert(memory !is null, "Memory is empty.");
216 		assert(depth >= 8 && depth <= 32, "Invalid depth.");
217 	} body {
218 		SDL_Surface* srfc = SDL_CreateRGBSurfaceFrom(memory, width, height, depth,
219 		                                             (depth / 8) * width,
220 		                                             RMask, GMask, BMask, AMask);
221 		
222 		if (srfc is null) {
223 			const string err = to!string(SDL_GetError());
224 			throw new Exception("Could not load image. Error: " ~ err);
225 		}
226 		
227 		this._target = make_shared(srfc, (SDL_Surface* ptr) => SDL_FreeSurface(ptr));
228 	}
229 	
230 	/**
231 	 * Save the current pixel data to the file.
232 	 */
233 	void saveToFile(string filename) {
234 		enforce(filename.length >= 4, "File name is too short.");
235 		
236 		if (SDL_SaveBMP(this.ptr, toStringz(filename)) != 0) {
237 			const string err = to!string(SDL_GetError());
238 			throw new Exception(.format("Could not save image %s. Error: %s.", filename, err));
239 		}
240 	}
241 	
242 	/**
243 	 * Fills a specific area of the surface with the given color.
244 	 * The second parameter is a pointer to the area.
245 	 * If it's null, the whole Surface is filled.
246 	 */
247 	void fill(ref const Color col, const ShortRect* rect = null) {
248 		SDL_Rect a = void;
249 		const SDL_Rect* ptr = transfer(rect, &a);
250 
251 		const uint key = SDL_MapRGBA(this._target.format, col.red, col.green, col.blue, col.alpha);
252 		
253 		SDL_FillRect(this._target, ptr, key);
254 	}
255 	
256 	/**
257 	 * Rvalue version
258 	 */
259 	void fill(const Color col, const ShortRect* rect = null) {
260 		this.fill(col, rect);
261 	}
262 	
263 	/**
264 	 * Fills multiple areas of the Surface with the given color.
265 	 */
266 	void fillAreas(ref const Color col, const ShortRect[] rects) {
267 		SDL_Rect a = void;
268 		const SDL_Rect* ptr_start = (rects.length > 0) ? transfer(&rects[0], &a) : null;
269 		const uint key = SDL_MapRGBA(this._target.format, col.red, col.green, col.blue, col.alpha);
270 		
271 		SDL_FillRects(this._target, ptr_start, cast(uint) rects.length, key);
272 	}
273 	
274 	/**
275 	 * Rvalue version
276 	 */
277 	void fillAreas(const Color col, const ShortRect[] rects) {
278 		this.fillAreas(col, rects);
279 	}
280 	
281 	/**
282 	 * Use this function to set the RLE acceleration hint for a surface.
283 	 * RLE (Run-Length-Encoding) is a way of compressing data.
284 	 * If RLE is enabled, color key and alpha blending blits are much faster, 
285 	 * but the surface must be locked before directly accessing the pixels.
286 	 *
287 	 * Returns: whether the call succeeded or not
288 	 */
289 	bool optimizeRLE(bool enable) {
290 		return SDL_SetSurfaceRLE(this._target, enable) == 0;
291 	}
292 	
293 	/**
294 	 * Use this function to set up a surface for directly accessing the pixels.
295 	 *
296 	 * Returns: whether the call succeeded or not
297 	 */
298 	bool lock() {
299 		if (SDL_LockSurface(this._target) == 0)
300 			return true;
301 		return false;
302 	}
303 	
304 	/**
305 	 * Use this function to release a surface after directly accessing the pixels.
306 	 */
307 	void unlock() {
308 		SDL_UnlockSurface(this._target);
309 	}
310 	
311 	/**
312 	 * Returns whether this Surface is locked or not.
313 	 */
314 	bool isLocked() const pure nothrow {
315 		return this._target.locked != 0;
316 	}
317 	
318 	/**
319 	 * Use this function to determine whether a surface must be locked for access.
320 	 */
321 	bool mustLock() {
322 		return SDL_MUSTLOCK(this._target) == SDL_TRUE;
323 	}
324 	
325 	/**
326 	 * Use this function to adapt the format of another Surface to this surface.
327 	 * Works like <code>SDL_DisplayFormat</code>.
328 	 */
329 	void adaptTo(ref Surface srfc) in {
330 		assert(srfc.isValid(), "Could not adapt to invalid surface.");
331 		assert(this.isValid(), "Could not adapt a invalid surface.");
332 	} body {
333 		this.adaptTo(srfc.ptr.format);
334 	}
335 	
336 	/**
337 	 * Use this function to adapt the format of another Surface to this surface.
338 	 * Works like <code>SLD_DisplayFormat</code>.
339 	 */
340 	void adaptTo(SDL_PixelFormat* fmt) in {
341 		assert(fmt !is null, "Null format is invalid.");
342 	} body {
343 		SDL_Surface* adapted = SDL_ConvertSurface(this._target, fmt, 0);
344 		enforce(adapted !is null, "Could not adapt surface.");
345 		
346 		this._target = make_shared(adapted, (SDL_Surface* ptr) => SDL_FreeSurface(ptr));
347 	}
348 	
349 	/**
350 	 * Set the colorkey.
351 	 */
352 	void setColorkey(ref const Color col) {
353 		this.setColorkey(col.red, col.green, col.blue, col.alpha);
354 	}
355 	
356 	/**
357 	 * Rvalue version
358 	 */
359 	void setColorkey(const Color col) {
360 		this.setColorkey(col);
361 	}
362 	
363 	/**
364 	 * Set the colorkey.
365 	 */
366 	void setColorkey(ubyte red, ubyte green, ubyte blue) {
367 		const uint key = SDL_MapRGB(this._target.format, red, green, blue);
368 		SDL_SetColorKey(this._target, SDL_TRUE, key);
369 	}
370 	
371 	/**
372 	 * Set the colorkey.
373 	 */
374 	void setColorkey(ubyte red, ubyte green, ubyte blue, ubyte alpha) {
375 		const uint key = SDL_MapRGBA(this._target.format, red, green, blue, alpha);
376 		SDL_SetColorKey(this._target, SDL_TRUE, key);
377 	}
378 	
379 	/**
380 	 * Returns the current colorkey.
381 	 */
382 	Color getColorkey() {
383 		uint key = 0;
384 		SDL_GetColorKey(this._target, &key);
385 		
386 		ubyte r, g, b, a;
387 		SDL_GetRGBA(key, this._target.format, &r, &g, &b, &a);
388 		
389 		return Color(r, g, b, a);
390 	}
391 	
392 	/**
393 	 * Set the Alpha mod.
394 	 */
395 	void setAlphaMod(ubyte alpha) {
396 		SDL_SetSurfaceAlphaMod(this._target, alpha);
397 	}
398 	
399 	/**
400 	 * Returns the current Alpha mod.
401 	 */
402 	ubyte getAlphaMod() {
403 		ubyte alpha;
404 		SDL_GetSurfaceAlphaMod(this._target, &alpha);
405 		
406 		return alpha;
407 	}
408 	
409 	/**
410 	 * Set the Blendmode.
411 	 */
412 	void setBlendMode(BlendMode mode) {
413 		SDL_SetSurfaceBlendMode(this._target, mode);
414 	}
415 	
416 	/**
417 	 * Returns the current Blendmode.
418 	 */
419 	BlendMode getBlendMode() {
420 		SDL_BlendMode mode;
421 		SDL_GetSurfaceBlendMode(this._target, &mode);
422 		
423 		return cast(BlendMode) mode;
424 	}
425 	
426 	/**
427 	 * Returns the clip rect of this surface.
428 	 * The clip rect is the area of the surface which is drawn.
429 	 */
430 	ShortRect getClipRect() {
431 		SDL_Rect clip = void;
432 		SDL_GetClipRect(this._target, &clip);
433 
434 		return ShortRect(clip);
435 	}
436 	
437 	/**
438 	 * Set the clip rect.
439 	 */
440 	void setClipRect(ref const ShortRect clip) {
441 		SDL_Rect a = void;
442 		clip.transferTo(&a);
443 
444 		SDL_SetClipRect(this._target, &a);
445 	}
446 	
447 	/**
448 	 * Rvalue version
449 	 */
450 	void setClipRect(const ShortRect clip) {
451 		this.setClipRect(clip);
452 	}
453 	
454 	@property {
455 		/**
456 		 * Returns the current filename, if any
457 		 */
458 		string filename() const pure nothrow {
459 			return this._filename;
460 		}
461 		
462 		/**
463 		 * Returns the width.
464 		 */
465 		ushort width() const pure nothrow {
466 			return this._target.isValid() ? cast(ushort) this._target.w : 0;
467 		}
468 		
469 		/**
470 		 * Returns the height.
471 		 */
472 		ushort height() const pure nothrow {
473 			return this._target.isValid() ? cast(ushort) this._target.h : 0;
474 		}
475 		
476 		/**
477 		 * Returns the pixel data of this surface.
478 		 */
479 		inout(void*) pixels() inout {
480 			return this._target.isValid() ? this._target.pixels : null;
481 		}
482 		
483 		/**
484 		 * Count the bits of this surface.
485 		 * Could be 32, 24, 16, 8, 0.
486 		 */
487 		ubyte bits() const pure nothrow {
488 			return this._target.isValid() ? this._target.format.BitsPerPixel : 0;
489 		}
490 		
491 		/**
492 		 * Count the bytes of this surface.
493 		 * Could be 4, 3, 2, 1, 0. (countBits / 8)
494 		 */
495 		ubyte bytes() const pure nothrow {
496 			return this._target.isValid() ? this._target.format.BytesPerPixel : 0;
497 		}
498 		
499 		/**
500 		 * Returns the Surface pitch or 0.
501 		 */
502 		int pitch() const pure nothrow {
503 			return this._target.isValid() ? this._target.pitch : 0;
504 		}
505 		
506 		/**
507 		 * Returns the PixelFormat
508 		 */
509 		const(SDL_PixelFormat*) pixelFormat() const pure nothrow {
510 			return this._target.format;
511 		}
512 	}
513 	
514 	/**
515 	 * Returns if the given color match the color of the given mask of the surface.
516 	 *
517 	 * See: Surface.Mask enum.
518 	 */
519 	bool isMask(Mask mask, ref const Color col) const {
520 		const uint map = SDL_MapRGBA(this._target.format, col.red, col.green, col.blue, col.alpha);
521 		
522 		return this.isMask(mask, map);
523 	}
524 	
525 	/**
526 	 * Rvalue version
527 	 */
528 	bool isMask(Mask mask, const Color col) const {
529 		return this.isMask(mask, col);
530 	}
531 	
532 	/**
533 	 * Returns if the given converted color match the color of the given mask of the surface.
534 	 *
535 	 * See: Surface.Mask enum.
536 	 */
537 	bool isMask(Mask mask, uint col) const pure nothrow {
538 		bool[4] result = void;
539 		ubyte index = 0;
540 		
541 		if (mask & Mask.Red)
542 			result[index++] = this._target.format.Rmask == col;
543 		if (mask & Mask.Green)
544 			result[index++] = this._target.format.Gmask == col;
545 		if (mask & Mask.Blue)
546 			result[index++] = this._target.format.Bmask == col;
547 		if (mask & Mask.Alpha)
548 			result[index++] = this._target.format.Amask == col;
549 		
550 		for (ubyte i = 0; i < index; ++i) {
551 			if (!result[i])
552 				return false;
553 		}
554 		
555 		return true;
556 	}
557 	
558 	/**
559 	 * Returns the pixel at the given coordinates.
560 	 */
561 	uint getPixelAt(ref const Vector2s pos) const {
562 		return this.getPixelAt(pos.x, pos.y);
563 	}
564 	
565 	/**
566 	 * Returns the pixel at the given coordinates.
567 	 */
568 	uint getPixelAt(ushort x, ushort y) const {
569 		uint* pixels = cast(uint*) this.pixels;
570 		enforce(pixels !is null, "No pixel at this point.");
571 		
572 		return pixels[(y * this._target.w) + x];
573 	}
574 	
575 	/**
576 	 * Put a new pixel at the given coordinates.
577 	 */
578 	void putPixelAt(ref const Vector2s pos, uint pixel) {
579 		this.putPixelAt(pos.x, pos.y, pixel);
580 	}
581 	
582 	/**
583 	 * Put a new pixel at the given coordinates.
584 	 */
585 	void putPixelAt(ushort x, ushort y, uint pixel) {
586 		uint* pixels = cast(uint*) this.pixels;
587 		enforce(pixels !is null, "No pixel at this point.");
588 		
589 		pixels[(y * this._target.w) + x] = pixel;
590 	}
591 	
592 	/**
593 	 * Returns the color on the given position.
594 	 */
595 	Color getColorAt(ref const Vector2s pos) const {
596 		return this.getColorAt(pos.x, pos.y);
597 	}
598 	
599 	/**
600 	 * Returns the color on the given position.
601 	 */
602 	Color getColorAt(ushort x, ushort y) const {
603 		const uint len = this.width * this.height;
604 		
605 		if ((x * y) <= len) {
606 			const uint pixel = this.getPixelAt(x, y);
607 			
608 			ubyte r, g, b, a;
609 			SDL_GetRGBA(pixel, this._target.format, &r, &g, &b, &a);
610 			
611 			return Color(r, g, b, a);
612 		}
613 		
614 		enforce(len != 0, "Invalid Surface for getColorAt.");
615 		
616 		throw new Exception("No color at this position.");
617 	}
618 	
619 	/**
620 	 * Returns a pointer to the SDL_Surface
621 	 */
622 	@property
623 	inout(SDL_Surface)* ptr() inout pure nothrow {
624 		return this._target.ptr;
625 	}
626 	
627 	/**
628 	 * Use this function to perform a fast, low quality,
629 	 * stretch blit between two surfaces of the same pixel format.
630 	 * src is the a pointer to a Rect structure which represents the rectangle to be copied, 
631 	 * or null to copy the entire surface.
632 	 * dst is a pointer to a Rect structure which represents the rectangle that is copied into.
633 	 * null means, that the whole srfc is copied to (0|0).
634 	 */
635 	bool blitScaled(ref Surface srfc, const ShortRect* src = null, ShortRect* dst = null) {
636 		return this.blitScaled(srfc.ptr, src, dst);
637 	}
638 	
639 	/**
640 	 * Same as above, but with a SDL_Surface* instead of a Surface.
641 	 */
642 	bool blitScaled(SDL_Surface* srfc, const ShortRect* src = null, ShortRect* dst = null) in {
643 		assert(srfc !is null, "Null surface cannot be blit.");
644 	} body {
645 		SDL_Rect a = void;
646 		SDL_Rect b = void;
647 
648 		const SDL_Rect* src_ptr = transfer(src, &a);
649 		SDL_Rect* dst_ptr = transfer(dst, &b);
650 		
651 		return SDL_BlitScaled(srfc, src_ptr, this._target, dst_ptr) == 0;
652 	}
653 	
654 	/**
655 	 * Use this function to perform low-level surface blitting only.
656 	 */
657 	bool lowerBlit(ref Surface srfc, ShortRect* src = null, ShortRect* dst = null) {
658 		return this.lowerBlit(srfc.ptr, src, dst);
659 	}
660 	
661 	/**
662 	 * Same as above, but with a SDL_Surface* instead of a Surface.
663 	 */
664 	bool lowerBlit(SDL_Surface* srfc, ShortRect* src = null, ShortRect* dst = null) in {
665 		assert(srfc !is null, "Null surface cannot be blit.");
666 	} body {
667 		SDL_Rect a = void;
668 		SDL_Rect b = void;
669 
670 		SDL_Rect* src_ptr = transfer(src, &a);
671 		SDL_Rect* dst_ptr = transfer(dst, &b);
672 		
673 		return SDL_LowerBlit(srfc, src_ptr, this._target, dst_ptr) == 0;
674 	}
675 	
676 	/**
677 	 * Use this function to perform a fast blit from the source surface to the this surface.
678 	 * src is the a pointer to a Rect structure which represents the rectangle to be copied, 
679 	 * or null to copy the entire surface.
680 	 * dst is a pointer to a Rect structure which represents the rectangle that is copied into.
681 	 * null means, that the whole srfc is copied to (0|0).
682 	 */
683 	bool blit(ref Surface srfc, const ShortRect* src = null, ShortRect* dst = null) {
684 		return this.blit(srfc.ptr, src, dst);
685 	}
686 	
687 	/**
688 	 * Same as above, but with a SDL_Surface* instead of a Surface.
689 	 */
690 	bool blit(SDL_Surface* srfc, const ShortRect* src = null, ShortRect* dst = null) in {
691 		assert(srfc !is null, "Null surface cannot be blit.");
692 	} body {
693 		SDL_Rect a = void;
694 		SDL_Rect b = void;
695 
696 		const SDL_Rect* src_ptr = transfer(src, &a);
697 		SDL_Rect* dst_ptr = transfer(dst, &b);
698 		
699 		return SDL_BlitSurface(srfc, src_ptr, this._target, dst_ptr) == 0;
700 	}
701 	
702 	/**
703 	 * Returns a subsurface from this surface. rect represents the viewport.
704 	 * The subsurface is a separate Surface object.
705 	 */
706 	Surface subSurface(ref const ShortRect rect) {
707 		SDL_Surface* sub = this.create(rect.width, rect.height);
708 		enforce(sub !is null, "Failed to construct a sub surface.");
709 
710 		SDL_Rect clip = void;
711 		rect.transferTo(&clip);
712 
713 		if (SDL_BlitSurface(this._target, &clip, sub, null) != 0)
714 			throw new Exception("An error occured by blitting the subsurface.");
715 		
716 		return Surface(sub);
717 	}
718 	
719 	/**
720 	 * Rvalue version
721 	 */
722 	Surface subSurface(const ShortRect rect) {
723 		return this.subSurface(rect);
724 	}
725 	
726 	/**
727 	 * Returns an new flipped Surface
728 	 * The current Surface is not modified.
729 	 *
730 	 * Note: This function may be slow
731 	 */
732 	Surface flip(Flip flip) {
733 		ubyte* pixels = cast(ubyte*) this.pixels;
734 		
735 		const ubyte bytes = this.bytes;
736 		const uint memSize = this.width * this.height * bytes;
737 		
738 		Surface flipped = Surface.make(this.width, this.height);
739 		ubyte* newPixels = cast(ubyte*) flipped.pixels;
740 		
741 		final switch (flip) {
742 			case Flip.Vertical:
743 				const uint rowSize = this.width * bytes;
744 				
745 				ubyte* source = &pixels[this.width * (this.height - 1) * bytes];
746 				ubyte* dest = &newPixels[0];
747 				
748 				for (ushort y = 0; y < this.height; ++y) {
749 					.memcpy(dest, source, rowSize);
750 					//std.algorithm.reverse(dest[0 .. rowSize]);
751 					source -= rowSize;
752 					dest += rowSize;
753 				}
754 				break;
755 			case Flip.Horizontal:
756 				for (ushort y = 0; y < this.height; ++y) {
757 					ubyte* source = &pixels[y * this.width * bytes];
758 					ubyte* dest = &newPixels[(y + 1) * this.width * bytes - bytes];
759 					
760 					for (ushort x = 0; x < this.width; ++x) {
761 						dest[0] = source[0];
762 						dest[1] = source[1];
763 						dest[2] = source[2];
764 						if (bytes == 4)
765 							dest[3] = source[3];
766 						
767 						source += bytes;
768 						dest -= bytes;
769 					}
770 				}
771 				break;
772 			case Flip.Vertical | Flip.Horizontal:
773 				newPixels[0 .. memSize] = pixels[0 .. memSize];
774 				.reverse(newPixels[0 .. memSize]);
775 				break;
776 		}
777 		
778 		return flipped;
779 	}
780 } unittest {
781 	writeln("<Surface unittest>");
782 	
783 	Surface s1 = Surface.make(64, 64, 32);
784 	
785 	assert(s1.useCount() == 1, to!string(s1.useCount()));
786 	{
787 		Surface s2 = s1;
788 		
789 		assert(s1.useCount() == 2, to!string(s1.useCount()));
790 		assert(s2.useCount() == 2, to!string(s2.useCount()));
791 		
792 		s2 = s1;
793 		
794 		assert(s1.useCount() == 2, to!string(s1.useCount()));
795 		assert(s2.useCount() == 2, to!string(s2.useCount()));
796 		
797 		{
798 			Surface s3 = s2;
799 			
800 			assert(s1.useCount() == 3, to!string(s1.useCount()));
801 			assert(s2.useCount() == 3, to!string(s2.useCount()));
802 			assert(s3.useCount() == 3, to!string(s3.useCount()));
803 		}
804 		
805 		assert(s1.useCount() == 2, to!string(s1.useCount()));
806 		assert(s2.useCount() == 2, to!string(s2.useCount()));
807 	}
808 	assert(s1.useCount() == 1, to!string(s1.useCount()));
809 	
810 	writeln("</Surface unittest>");
811 }