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 }