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