1 /**
2  * ae.ui.video.sdl.renderer
3  *
4  * License:
5  *   This Source Code Form is subject to the terms of
6  *   the Mozilla Public License, v. 2.0. If a copy of
7  *   the MPL was not distributed with this file, You
8  *   can obtain one at http://mozilla.org/MPL/2.0/.
9  *
10  * Authors:
11  *   Vladimir Panteleev <ae@cy.md>
12  */
13 
14 module ae.ui.video.sdl2.renderer;
15 
16 import std.exception;
17 
18 import derelict.sdl2.sdl;
19 
20 import ae.ui.shell.sdl2.shell;
21 import ae.ui.video.renderer;
22 import ae.ui.video.software.common;
23 
24 /// The SDL pixel format we use.
25 enum PIXEL_FORMAT = SDL_DEFINE_PIXELFORMAT(SDL_PIXELTYPE_PACKED32, SDL_PACKEDORDER_XRGB, SDL_PACKEDLAYOUT_8888, 32, 4);
26 
27 /// Draw on a streaming SDL_Texture, and present it
28 final class SDL2SoftwareRenderer : Renderer
29 {
30 	SDL_Texture* t; /// The SDL texture object.
31 	SDL_Renderer* renderer; /// The SDL renderer object.
32 	/// Width and height.
33 	uint w, h;
34 
35 	this(SDL_Renderer* renderer, uint w, uint h)
36 	{
37 		this.renderer = renderer;
38 		this.w = w;
39 		this.h = h;
40 
41 		t = sdlEnforce(SDL_CreateTexture(renderer, PIXEL_FORMAT, SDL_TEXTUREACCESS_STREAMING, w, h), "SDL_CreateTexture failed");
42 	} ///
43 
44 	override Bitmap fastLock()
45 	{
46 		assert(false, "Can't fastLock SDL2SoftwareRenderer");
47 	} /// Not applicable - throws an `AssertError`
48 
49 	override Bitmap lock()
50 	{
51 		if (!locked)
52 		{
53 			void* pixels;
54 			int pitch;
55 			sdlEnforce(SDL_LockTexture(t, null, &pixels, &pitch)==0, "SDL_LockTexture failed");
56 			_bitmap = Bitmap(cast(Bitmap.StorageType*)pixels, w, h, pitch / cast(int)COLOR.sizeof);
57 			locked = true;
58 		}
59 		return _bitmap;
60 	} ///
61 
62 	override void unlock()
63 	{
64 		assert(locked);
65 		SDL_UnlockTexture(t);
66 		locked = false;
67 	} ///
68 
69 	override void present()
70 	{
71 		if (locked)
72 			unlock();
73 
74 		SDL_RenderCopy(renderer, t, null, null);
75 		SDL_RenderPresent(renderer);
76 	} ///
77 
78 	override void shutdown()
79 	{
80 		SDL_DestroyTexture(t);
81 	} ///
82 
83 	// **********************************************************************
84 
85 	override @property uint width()
86 	{
87 		return w;
88 	} ///
89 
90 	override @property uint height()
91 	{
92 		return h;
93 	} ///
94 
95 	mixin SoftwareRenderer;
96 
97 private:
98 	Bitmap _bitmap;
99 	bool locked;
100 
101 	@property Bitmap bitmap()
102 	{
103 		if (!locked)
104 			return lock();
105 		return _bitmap;
106 	}
107 }
108 
109 /// Use SDL 2 drawing APIs.
110 final class SDL2Renderer : Renderer
111 {
112 	SDL_Renderer* renderer; /// SDL renderer object.
113 	/// Width and height.
114 	uint w, h;
115 
116 	this(SDL_Renderer* renderer, uint w, uint h)
117 	{
118 		this.renderer = renderer;
119 		this.w = w;
120 		this.h = h;
121 	} ///
122 
123 	override Bitmap fastLock()
124 	{
125 		assert(false, "Can't fastLock SDL2Renderer");
126 	} /// Not applicable - throws an `AssertError`
127 
128 	override Bitmap lock()
129 	{
130 		assert(false, "Not possible");
131 	} /// ditto
132 
133 	override void unlock()
134 	{
135 		assert(false, "Not possible");
136 	} /// ditto
137 
138 	override void present()
139 	{
140 		SDL_RenderPresent(renderer);
141 	} ///
142 
143 	override void shutdown() {} ///
144 
145 	// **********************************************************************
146 
147 	override void putPixel(int x, int y, COLOR color)
148 	{
149 		SDL_SetRenderDrawColor(renderer, color.r, color.g, color.b, color.x);
150 		SDL_RenderDrawPoint(renderer, x, y);
151 	} ///
152 
153 	override void putPixels(Pixel[] pixels)
154 	{
155 		foreach (ref pixel; pixels)
156 			putPixel(pixel.x, pixel.y, pixel.color);
157 	} ///
158 
159 	override void line(float x0, float y0, float x1, float y1, COLOR color)
160 	{
161 		SDL_SetRenderDrawColor(renderer, color.r, color.g, color.b, color.x);
162 		SDL_RenderDrawLine(renderer, cast(int)x0, cast(int)y0, cast(int)x1, cast(int)y1);
163 	} ///
164 
165 	override void vline(int x, int y0, int y1, COLOR color)
166 	{
167 		SDL_SetRenderDrawColor(renderer, color.r, color.g, color.b, color.x);
168 		SDL_RenderDrawLine(renderer, x, y0, x, y1);
169 	} ///
170 
171 	override void hline(int x0, int x1, int y, COLOR color)
172 	{
173 		SDL_SetRenderDrawColor(renderer, color.r, color.g, color.b, color.x);
174 		SDL_RenderDrawLine(renderer, x0, y, x1, y);
175 	} ///
176 
177 	override void fillRect(int x0, int y0, int x1, int y1, COLOR color)
178 	{
179 		SDL_SetRenderDrawColor(renderer, color.r, color.g, color.b, color.x);
180 		auto rect = SDL_Rect(x0, y0, x1-x0, y1-y0);
181 		SDL_RenderFillRect(renderer, &rect);
182 	} ///
183 
184 	override void fillRect(float x0, float y0, float x1, float y1, COLOR color)
185 	{
186 		fillRect(cast(int)x0, cast(int)y0, cast(int)x1, cast(int)y1, color);
187 	} ///
188 
189 	override void clear()
190 	{
191 		SDL_SetRenderDrawColor(renderer, 0, 0, 0, 0);
192 		SDL_RenderClear(renderer);
193 	} ///
194 
195 	override void draw(int x, int y, TextureSource source, int u0, int v0, int u1, int v1)
196 	{
197 		auto data = updateTexture(source);
198 		auto srcRect = SDL_Rect(u0, v0, u1-u0, v1-v0);
199 		auto dstRect = SDL_Rect(x, y, u1-u0, v1-v0);
200 		sdlEnforce(SDL_RenderCopy(renderer, data.t, &srcRect, &dstRect)==0, "SDL_RenderCopy");
201 	} ///
202 
203 	override void draw(float x0, float y0, float x1, float y1, TextureSource source, int u0, int v0, int u1, int v1)
204 	{
205 		auto data = updateTexture(source);
206 		auto srcRect = SDL_Rect(u0, v0, u1-u0, v1-v0);
207 		auto dstRect = SDL_Rect(cast(int)x0, cast(int)y0, cast(int)(x1-x0), cast(int)(y1-y0));
208 		sdlEnforce(SDL_RenderCopy(renderer, data.t, &srcRect, &dstRect)==0, "SDL_RenderCopy");
209 	} ///
210 
211 	// **********************************************************************
212 
213 	private SDLTextureRenderData updateTexture(TextureSource source)
214 	{
215 		auto data = cast(SDLTextureRenderData) cast(void*) source.renderData[Renderers.SDL2];
216 		if (data is null || data.invalid)
217 		{
218 			source.renderData[Renderers.SDL2] = data = new SDLTextureRenderData;
219 			data.next = SDLTextureRenderData.head;
220 			SDLTextureRenderData.head = data;
221 			rebuildTexture(data, source);
222 		}
223 		else
224 		{
225 			if (SDL_QueryTexture(data.t, null, null, null, null) != 0)
226 			{
227 				data.destroy();
228 				rebuildTexture(data, source);
229 			}
230 			else
231 			if (data.textureVersion != source.textureVersion)
232 			{
233 				auto pixelInfo = source.getPixels();
234 				sdlEnforce(SDL_UpdateTexture(data.t, null, pixelInfo.pixels, cast(uint)pixelInfo.pitch)==0, "SDL_UpdateTexture");
235 				data.textureVersion = source.textureVersion;
236 			}
237 		}
238 		return data;
239 	}
240 
241 	private void rebuildTexture(SDLTextureRenderData data, TextureSource source)
242 	{
243 		auto pixelInfo = source.getPixels();
244 		data.t = sdlEnforce(SDL_CreateTexture(renderer, PIXEL_FORMAT, SDL_TEXTUREACCESS_STREAMING, cast(int)pixelInfo.w, cast(int)pixelInfo.h), "SDL_CreateTexture");
245 		sdlEnforce(SDL_UpdateTexture(data.t, null, pixelInfo.pixels, cast(uint)pixelInfo.pitch)==0, "SDL_UpdateTexture");
246 		data.textureVersion = source.textureVersion;
247 		data.invalid = false;
248 	}
249 
250 	// **********************************************************************
251 
252 	override @property uint width()
253 	{
254 		return w;
255 	} ///
256 
257 	override @property uint height()
258 	{
259 		return h;
260 	} ///
261 }
262 
263 private final class SDLTextureRenderData : TextureRenderData
264 {
265 	SDL_Texture* t;
266 	SDLTextureRenderData next;
267 	static SDLTextureRenderData head;
268 	bool invalid;
269 	uint w, h;
270 
271 	void destroy()
272 	{
273 		invalid = true;
274 		SDL_DestroyTexture(t);
275 	}
276 }