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