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 (data.textureVersion != source.textureVersion)
223 			{
224 				auto pixelInfo = source.getPixels();
225 				SDL_UpdateTexture(data.t, null, pixelInfo.pixels, cast(uint)pixelInfo.pitch);
226 				data.textureVersion = source.textureVersion;
227 			}
228 		}
229 		return data;
230 	}
231 
232 	private void rebuildTexture(SDLTextureRenderData data, TextureSource source)
233 	{
234 		auto pixelInfo = source.getPixels();
235 		data.t = sdlEnforce(SDL_CreateTexture(renderer, PIXEL_FORMAT, SDL_TEXTUREACCESS_STREAMING, pixelInfo.w, pixelInfo.h), "SDL_CreateTexture failed");
236 		SDL_UpdateTexture(data.t, null, pixelInfo.pixels, cast(uint)pixelInfo.pitch);
237 		data.textureVersion = source.textureVersion;
238 	}
239 
240 	// **********************************************************************
241 
242 	override @property uint width()
243 	{
244 		return w;
245 	}
246 
247 	override @property uint height()
248 	{
249 		return h;
250 	}
251 }
252 
253 private final class SDLTextureRenderData : TextureRenderData
254 {
255 	SDL_Texture* t;
256 	SDLTextureRenderData next;
257 	static SDLTextureRenderData head;
258 	bool invalid;
259 	uint w, h;
260 
261 	void destroy()
262 	{
263 		invalid = true;
264 		SDL_DestroyTexture(t);
265 	}
266 }