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 }