1 /** 2 * This module provides classes used to display graphics. 3 * It builds uppon $(LINK2 https://www.libsdl.org/download-2.0.php, SDL2) 4 * and if you'd rather use sdl it's functions can be called directly by 5 * imporrting like this: `import caLit_util.graphics.derelict.sdl2.sdl` 6 */ 7 8 module caLib_util.graphics; 9 10 import std.stdio : writeln; 11 import std.file : thisExePath, getcwd, exists, isFile; 12 import std.path : buildNormalizedPath; 13 import std.exception : enforce; 14 import std.conv : to; 15 import caLib_util.build : arch, os; 16 17 public import derelict.sdl2.sdl; 18 19 20 21 shared static this() 22 { 23 // Derelict dosen't check some of the directories for SDL 24 // do it explicitly instead 25 static if(os == "Linux") 26 { 27 string path; 28 29 // look for sdl in the same directory as the executable 30 path = buildNormalizedPath(thisExePath(), "../libSDL2.so"); 31 if(exists(path) && isFile(path)) 32 { 33 DerelictSDL2.load(path); 34 return; 35 } 36 37 // look for sdl in the working directory 38 path = buildNormalizedPath(getcwd(), "libSDL2.so"); 39 if(exists(path) && isFile(path)) 40 { 41 DerelictSDL2.load(path); 42 } 43 44 // meybe look in the PATH also? I trust Derelict to do that but i dunno 45 46 // now sdl can look elsware 47 DerelictSDL2.load(); 48 } 49 else 50 { 51 DerelictSDL2.load(); 52 } 53 } 54 55 56 57 /** 58 * A class representing a window 59 * 60 * It wrapps a $(B SDL_Window) and $(B SDL_Renderer) wich can both be freely retrived 61 * and manipulated 62 */ 63 class Window 64 { 65 66 private: 67 68 uint screenWidth; 69 uint screenHeight; 70 71 SDL_Window* window; 72 SDL_Renderer* renderer; 73 74 public: 75 76 /// 77 this(int screenWidth, int screenHeight) 78 { 79 this(screenWidth, screenHeight, "GCaL"); 80 } 81 82 /// 83 this(uint screenWidth, uint screenHeight, string title) 84 in { assert(title !is null); } 85 body 86 { 87 88 this.screenWidth = screenWidth; 89 this.screenHeight = screenHeight; 90 91 SDL_Window* window; 92 93 int err = SDL_Init(SDL_INIT_EVERYTHING); 94 95 assert(err >= 0, "SDL could not initialize! SDL_Error: " 96 ~ to!string(SDL_GetError()) ~ "\n"); 97 98 window = SDL_CreateWindow(cast(const char*)title, 99 SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, 100 screenWidth, screenHeight, SDL_WINDOW_SHOWN); 101 102 assert(window !is(null), "Window could not be created! SDL_Error: " 103 ~ to!string(SDL_GetError()) ~ "\n"); 104 105 renderer = SDL_CreateRenderer(window, -1, SDL_RENDERER_ACCELERATED); 106 } 107 108 ~this() 109 { 110 SDL_DestroyRenderer(renderer); 111 SDL_DestroyWindow(window); 112 } 113 114 /// Returns the width of the screen in pixels 115 int getWidth() 116 { 117 return screenWidth; 118 } 119 120 /// Returns the height of the screen in pixels 121 int getHeight() 122 { 123 return screenHeight; 124 } 125 126 /// Returns a reference to the $(B SDL_Window) wrapped in this class 127 SDL_Window* getWindow() 128 { 129 return window; 130 } 131 132 /// Returns a reference to the $(B SDL_Renderer) wrapped in this class 133 SDL_Renderer* getRenderer() 134 { 135 return renderer; 136 } 137 } 138 139 140 141 /** 142 * A class representing a texture. A texture contains the pixeldata that can 143 * be rendered to its associated $(B Window). 144 * 145 * It wrapps a $(B SDL_Texture) wich can be freely retrived and manipulated 146 */ 147 class Texture 148 { 149 150 private: 151 152 uint textureWidth; 153 uint textureHeight; 154 155 SDL_Texture* texture; 156 157 int pitch; 158 159 immutable bpp = 4; 160 immutable depth = 8*bpp; 161 immutable pixelFormat = SDL_PIXELFORMAT_ARGB8888; 162 163 public: 164 165 /// 166 this(Window window, int textureWidth, int textureHeight) 167 in 168 { assert(window !is null); } 169 body 170 { 171 DerelictSDL2.load(); 172 173 this.textureWidth = textureWidth; 174 this.textureHeight = textureHeight; 175 pitch = textureWidth * bpp; 176 depth = 8*bpp; 177 178 texture = SDL_CreateTexture(window.getRenderer(), 179 pixelFormat, 180 SDL_TEXTUREACCESS_STREAMING, 181 textureWidth, textureHeight); 182 } 183 184 ~this() 185 { 186 SDL_DestroyTexture(texture); 187 } 188 189 /** 190 * Lock the texture to manipulate its pixeldata 191 * 192 * Locks the thexture meaning that the texture can't be used for anything 193 * untill its $(CU unlock) method is called 194 * 195 * A pointer is returned. It points to the pixeldata wich can be manipulated. 196 * The length of the pixeldata is the textures width*height. The value for 197 * a pixel at location x, y can be obtained by getting the element at index 198 * y * width + x 199 * 200 * Examples: 201 * ---- 202 * auto window = new Window(800, 400, "myTestWindow"); //create a window 203 * auto texture = new Texture(window, 300, 100); //create a texture 204 * uint* pixels = texture.lock() //lock the texture and get the pixeldata pointer 205 * pixels[10 * texture.getWidth() + 20] = 0x00FF0000; // make the pixel at position 20, 10 red 206 * texture.lock() //now were done manipulating the pixels, unlock the texture 207 * 208 * // render the texture to the window 209 * SDL_RenderCopy(window.getRenderer(), texture.getTexture(), SDL_Rect(0,0,300,100), SDL_Rect(0,0,800,400)); 210 * SDL_RenderPresent(window.getRenderer()); 211 * ---- 212 * 213 * Returns: 214 * A pointer to the pixeldata, 215 */ 216 uint* lock() 217 { 218 void* pixelsptr; 219 int[] pitch = [pitch]; 220 SDL_LockTexture(texture, null, &pixelsptr, pitch.ptr); 221 uint* pixels = cast(uint*)(pixelsptr); 222 return pixels; 223 } 224 225 /// Unlocks the texture 226 void unlock() 227 { 228 SDL_UnlockTexture(texture); 229 } 230 231 /// Returns the texture's width in pixels 232 uint getWidth() 233 { 234 return textureWidth; 235 } 236 237 /// Return the texture's height in pixels 238 uint getHeight() 239 { 240 return textureHeight; 241 } 242 243 /** 244 * Returns the pixel format 245 * 246 * The pixel format is a SDL_PIXELFORMAT and the default is 247 * SDL_PIXELFORMAT_ARGB8888 248 */ 249 int getPixelFormat() 250 { 251 return pixelFormat; 252 } 253 254 /// Returns the pixels depth 255 int getDepth() 256 { 257 return depth; 258 } 259 260 /// Returns the pitch 261 int getPitch() 262 { 263 return pitch; 264 } 265 266 /// Retuens the internal $(B SDL_Texture) 267 SDL_Texture* getTexture() 268 { 269 return texture; 270 } 271 } 272 273 274 275 /// Creates a $(B SDL_Surface) from a $(B Texture). 276 SDL_Surface* createRGBSurfaceFromTexture(Texture texture) 277 { 278 uint* pixels = texture.lock(); 279 scope(exit) texture.unlock(); 280 return SDL_CreateRGBSurfaceFrom(pixels, texture.getWidth(), 281 texture.getHeight(), texture.getDepth(), texture.getPitch(), 282 0x00FF0000, 0x0000FF00, 0x000000FF, 0xFF000000); 283 } 284 285 286 287 /// Saves a $(B Texture) as a bitmap picture 288 void saveTextureAsBmp(Texture texture, string path) 289 { 290 SDL_Surface* sshot = createRGBSurfaceFromTexture(texture); 291 scope(exit) SDL_FreeSurface(sshot); 292 293 int err = SDL_SaveBMP(sshot, cast(const char*)(path)); 294 295 enforce(err != -1, "Could not save image \"" ~ path ~ "\" SDL_Error: " 296 ~ to!string(SDL_GetError())); 297 }