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 }