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.Renderer;
25 
26 private {
27 	import derelict.sdl2.sdl;
28 	
29 	import Dgame.Math.Rect;
30 	import Dgame.Graphics.Surface;
31 	import Dgame.Graphics.Color;
32 	import Dgame.Graphics.RendererTexture;
33 	import Dgame.Window.Window;
34 	import Dgame.Internal.Unique;
35 }
36 
37 /**
38  * Renderer support (hardware) accelerated rendering.
39  *
40  * Author: rschuett
41  */
42 final class Renderer {
43 	/**
44 	 * Supported BlendModes
45 	 */
46 	enum BlendMode {
47 		None  = SDL_BLENDMODE_NONE,	    /** no blending */
48 		Blend = SDL_BLENDMODE_BLEND,	/** dst = (src * A) + (dst * (1-A)) */
49 		Add   = SDL_BLENDMODE_ADD,		/** dst = (src * A) + dst */
50 		Mod   = SDL_BLENDMODE_MOD,		/** dst = src * dst */
51 	}
52 	
53 	/**
54 	 * Supported Flags
55 	 */
56 	enum Flags {
57 		Software = SDL_RENDERER_SOFTWARE,			/** the renderer is a software fallback */
58 		HwAccel  = SDL_RENDERER_ACCELERATED,		/** the renderer uses hardware acceleration */
59 		VSync    = SDL_RENDERER_PRESENTVSYNC,		/** present is synchronized with the refresh rate */
60 		TargetTexture = SDL_RENDERER_TARGETTEXTURE	/** the renderer supports rendering to texture */
61 	}
62 	
63 	/**
64 	 * Flags
65 	 */
66 	const Flags flags;
67 	
68 private:
69 	unique_ptr!(SDL_Renderer) _target;
70 	
71 	ushort _width;
72 	ushort _height;
73 	
74 private:
75 	/**
76 	 * CTor
77 	 */
78 	this(SDL_Renderer* renderer, ushort w, ushort h) in {
79 		assert(renderer !is null, "Renderer is null.");
80 	} body {
81 		this._target = make_unique(renderer, (SDL_Renderer* re) => SDL_DestroyRenderer(re));
82 		
83 		this._width  = w;
84 		this._height = h;
85 		
86 		SDL_RendererInfo renderInfo;
87 		if (SDL_GetRendererInfo(renderer, &renderInfo) == 0)
88 			this.flags = cast(Flags) renderInfo.flags;
89 		else
90 			throw new Exception("Could not detect Renderer Flags.");
91 	}
92 	
93 	/**
94 	 * Set a texture as the current rendering target.
95 	 */
96 	bool setTarget(SDL_Texture* tex, ushort w, ushort h) {
97 		this._width  = w;
98 		this._height = h;
99 		
100 		return SDL_SetRenderTarget(this._target, tex) == 0;
101 	}
102 	
103 public:
104 	/**
105 	 * Use this function to get the renderer associated with the window.
106 	 */
107 	static Renderer getFrom(Window win) {
108 		SDL_Window* window = SDL_GetWindowFromID(win.id);
109 		
110 		return new Renderer(SDL_GetRenderer(window), win.width, win.height);
111 	}
112 	
113 	/**
114 	 * Use this function to get the number of 2D rendering drivers available for the current display.
115 	 */
116 	static int countDrivers() {
117 		return SDL_GetNumRenderDrivers();
118 	}
119 	
120 	/**
121 	 * Use this function to create a 2D rendering context for a window.
122 	 */
123 	this(Window win, Flags flags) {
124 		SDL_Window* window = SDL_GetWindowFromID(win.id);
125 		
126 		this(SDL_CreateRenderer(window, -1, flags), win.width, win.height);
127 	}
128 	
129 	/**
130 	 * Use this function to create a 2D software rendering context for a Surface.
131 	 */
132 	this(ref Surface srfc) {
133 		this(SDL_CreateSoftwareRenderer(srfc.ptr), srfc.width, srfc.height);
134 	}
135 	
136 	@disable
137 	void opAssign(Renderer rhs);
138 	
139 	/**
140 	 * Returns the width
141 	 */
142 	@property
143 	ushort width() const pure nothrow {
144 		return this._width;
145 	}
146 	
147 	/**
148 	 * Returns the height
149 	 */
150 	@property
151 	ushort height() const pure nothrow {
152 		return this.height;
153 	}
154 	
155 	/**
156 	 * Use this function to update the screen with rendering performed.
157 	 */
158 	void present() {
159 		SDL_RenderPresent(this._target);
160 	}
161 	
162 	/**
163 	 * Use this function to set the blend mode for a texture, used by 'copy'.
164 	 */
165 	void setBlendMode(BlendMode bmode) {
166 		SDL_SetRenderDrawBlendMode(this._target, bmode);
167 	}
168 	
169 	/**
170 	 * Use this function to get the blend mode used for texture copy operations.
171 	 */
172 	BlendMode getBlendMode() {
173 		SDL_BlendMode blendMode;
174 		SDL_GetRenderDrawBlendMode(this._target, &blendMode);
175 		
176 		return cast(BlendMode) blendMode;
177 	}
178 	
179 	/**
180 	 * Use this function to set the color used for drawing operations (clear).
181 	 */
182 	void setDrawColor(ref const Color col) {
183 		this.setDrawColor(col.red, col.green, col.blue, col.alpha);
184 	}
185 	
186 	/**
187 	 * Rvalue version
188 	 */
189 	void setDrawColor(const Color col) {
190 		this.setDrawColor(col);
191 	}
192 	
193 	/**
194 	 * Use this function to set the color used for drawing operations (clear).
195 	 */
196 	void setDrawColor(ubyte r, ubyte g, ubyte b, ubyte a) {
197 		SDL_SetRenderDrawColor(this._target, r, g, b, a);
198 	}
199 	
200 	/**
201 	 * Use this function to create a texture for a rendering context.
202 	 */
203 	RendererTexture createSoftTexture(ushort width, ushort height, RendererTexture.Access access) {
204 		SDL_Texture* tex = SDL_CreateTexture(this._target, SDL_PIXELFORMAT_UNKNOWN, access, width, height);
205 		
206 		return RendererTexture(tex, access);
207 	}
208 	
209 	/**
210 	 * Use this function to create a texture from an existing surface.
211 	 */
212 	RendererTexture createRendererTexture(ref Surface srfc, bool release = false,
213 	                                      RendererTexture.Access access = RendererTexture.Access.Static)
214 	{
215 		scope(exit) {
216 			if (release)
217 				srfc.free();
218 		}
219 		
220 		if (access & RendererTexture.Access.Static) {
221 			SDL_Texture* tex = SDL_CreateTextureFromSurface(this._target, srfc.ptr);
222 			
223 			return RendererTexture(tex, access);
224 		}
225 		
226 		RendererTexture hw = this.createSoftTexture(srfc.width, srfc.height, access);
227 		hw.update(srfc.pixels, null);
228 		
229 		return hw;
230 	}
231 	
232 	/**
233 	 * Rvalue version
234 	 */
235 	RendererTexture createRendererTexture(Surface srfc, bool release = false,
236 	                                      RendererTexture.Access access = RendererTexture.Access.Static)
237 	{
238 		return this.createRendererTexture(srfc, release, access);
239 	}
240 	
241 	/**
242 	 * Set a Surface as the current rendering target.
243 	 */
244 	bool setTarget(ref Surface srfc) {
245 		SDL_Texture* tex = SDL_CreateTextureFromSurface(this._target, srfc.ptr);
246 		
247 		return this.setTarget(tex, srfc.width, srfc.height);
248 	}
249 	
250 	/**
251 	 * Set a SoftTexture as the current rendering target.
252 	 */
253 	bool setTarget(ref RendererTexture hw) {
254 		return this.setTarget(hw.ptr, hw.width, hw.height);
255 	}
256 	
257 	/**
258 	 * Use this function to copy a portion of the texture to the current rendering target.
259 	 */
260 	bool copy(ref RendererTexture hw, const ShortRect* src = null, const ShortRect *dst = null) {
261 		SDL_Rect a = void;
262 		SDL_Rect b = void;
263 
264 		const SDL_Rect* _src = transfer(src, &a);
265 		const SDL_Rect* _dst = transfer(dst, &b);
266 		
267 		return SDL_RenderCopy(this._target, hw.ptr, _src, _dst) == 0;
268 	}
269 	
270 	/**
271 	 * Use this function to clear the current rendering target with the drawing color.
272 	 */
273 	void clear() {
274 		SDL_RenderClear(this._target);
275 	}
276 	
277 	/**
278 	 * Use this function to set the drawing area for rendering on the current target.
279 	 */
280 	void setViewport(ref const ShortRect view) {
281 		SDL_Rect a = void;
282 		view.transferTo(&a);
283 
284 		SDL_RenderSetViewport(this._target, &a);
285 	}
286 	
287 	/**
288 	 * Rvalue version
289 	 */
290 	void setViewport(const ShortRect view) {
291 		this.setViewport(view);
292 	}
293 	
294 	/**
295 	 * Use this function to get the drawing area for the current target.
296 	 */
297 	ShortRect getViewport() {
298 		SDL_Rect a = void;
299 		SDL_RenderGetViewport(this._target, &a);
300 		
301 		return ShortRect(a);
302 	}
303 	
304 	/**
305 	 * Use this function to read pixels from the current rendering target.
306 	 * Note: This is a very slow operation, and should not be used frequently.
307 	 * Note: This method <b>allocates</b> GC memory.
308 	 *
309 	 * rect represents the area to read, or null for the entire render target
310 	 */
311 	void* readPixels(const ShortRect* rect) {
312 		void[] pixels = new void[rect.width * rect.height * 4];
313 
314 		SDL_Rect a = void;
315 		const SDL_Rect* area = transfer(rect, &a);
316 
317 		SDL_RenderReadPixels(this._target, area, 0, &pixels[0], this._width * 4);
318 		
319 		return &pixels[0];
320 	}
321 }