1 module gfm.sdl2.surface;
2 
3 import bindbc.sdl;
4 
5 import gfm.sdl2.sdl;
6 
7 /// SDL Surface wrapper.
8 /// A SDL2Surface might own the SDL_Surface* handle or not.
9 final class SDL2Surface
10 {
11     public
12     {
13         /// Whether a SDL Surface is owned by the wrapper or borrowed.
14         enum Owned
15         {
16             NO,  // Not owned.
17             YES  // Owned.
18         }
19 
20         /// Create from an existing SDL_Surface* handle.
21         this(SDL2 sdl2, SDL_Surface* surface, Owned owned)
22         {
23             assert(surface !is null);
24             _sdl2 = sdl2;
25             _surface = surface;
26             _handleOwned = owned;
27         }
28 
29         /// Create a new RGBA surface. Both pixels data and handle are owned.
30         /// See_also: $(LINK http://wiki.libsdl.org/SDL_CreateRGBSurface)
31         /// Throws: $(D SDL2Exception) on error.
32         this(SDL2 sdl2, int width, int height, int depth,
33              uint Rmask, uint Gmask, uint Bmask, uint Amask)
34         {
35             _sdl2 = sdl2;
36             _surface = SDL_CreateRGBSurface(0, width, height, depth, Rmask, Gmask, Bmask, Amask);
37             if (_surface is null)
38                 _sdl2.throwSDL2Exception("SDL_CreateRGBSurface");
39             _handleOwned = Owned.YES;
40         }
41 
42         /// Create surface from RGBA data. Pixels data is <b>not</b> and not owned.
43         /// See_also: clone, $(WEB wiki.libsdl.org/SDL_CreateRGBSurfaceFrom,SDL_CreateRGBSurfaceFrom)
44         /// Throws: $(D SDL2Exception) on error.
45         this(SDL2 sdl2, void* pixels, int width, int height, int depth, int pitch,
46              uint Rmask, uint Gmask, uint Bmask, uint Amask)
47         {
48             _sdl2 = sdl2;
49             _surface = SDL_CreateRGBSurfaceFrom(pixels, width, height, depth, pitch, Rmask, Gmask, Bmask, Amask);
50             if (_surface is null)
51                 _sdl2.throwSDL2Exception("SDL_CreateRGBSurfaceFrom");
52             _handleOwned = Owned.YES;
53         }
54 
55         /// Releases the SDL resource.
56         ~this()
57         {
58             if (_surface !is null)
59             {
60                 debug ensureNotInGC("SDL2Surface");
61                 if (_handleOwned == Owned.YES)
62                     SDL_FreeSurface(_surface);
63                 _surface = null;
64             }
65         }
66 
67         /// Converts the surface to another format.
68         /// See_also: $(LINK http://wiki.libsdl.org/SDL_ConvertSurface)
69         /// Returns: A new surface.
70         SDL2Surface convert(const(SDL_PixelFormat)* newFormat)
71         {
72             SDL_Surface* surface = SDL_ConvertSurface(_surface, newFormat, 0);
73             if (surface is null)
74                 _sdl2.throwSDL2Exception("SDL_ConvertSurface");
75             assert(surface != _surface); // should not be the same handle
76             return new SDL2Surface(_sdl2, surface, Owned.YES);
77         }
78 
79         /// Returns: A copy of the surface, useful for taking ownership of not-owned pixel data.
80         /// See_also: $WEB(wiki.libsdl.org/SDL_CreateRGBSurfaceFrom,SDL_CreateRGBSurfaceFrom)
81         SDL2Surface clone()
82         {
83             return convert(pixelFormat());
84         }
85 
86         /// Returns: Width of the surface in pixels.
87         @property int width() const
88         {
89             return _surface.w;
90         }
91 
92         /// Returns: Height of the surface in pixels.
93         @property int height() const
94         {
95             return _surface.h;
96         }
97 
98         /// Returns: Pointer to surface data.
99         /// You must lock the surface before accessng it.
100         ubyte* pixels()
101         {
102             return cast(ubyte*) _surface.pixels;
103         }
104 
105         /// Get the surface pitch (number of bytes between lines).
106         size_t pitch()
107         {
108             return _surface.pitch;
109         }
110 
111         /// Lock the surface, allow to use pixels().
112         /// Throws: $(D SDL2Exception) on error.
113         void lock()
114         {
115             if (SDL_LockSurface(_surface) != 0)
116                 _sdl2.throwSDL2Exception("SDL_LockSurface");
117         }
118 
119         /// Unlock the surface.
120         void unlock()
121         {
122             SDL_UnlockSurface(_surface);
123         }
124 
125         /// Returns: SDL handle.
126         SDL_Surface* handle()
127         {
128             return _surface;
129         }
130 
131         /// Returns: SDL_PixelFormat which describe the surface.
132         SDL_PixelFormat* pixelFormat()
133         {
134             return _surface.format;
135         }
136 
137         ///
138         struct RGBA
139         {
140             ubyte r, g, b, a;
141         }
142 
143         /// Get a surface pixel color.
144         /// Bugs: must be locked when using this method. Slow!
145         RGBA getRGBA(int x, int y)
146         {
147             // crash if out of image
148             if (x < 0 || x >= width())
149                 assert(0);
150 
151             if (y < 0 || y >= height())
152                 assert(0);
153 
154             SDL_PixelFormat* fmt = _surface.format;
155 
156             ubyte* pixels = cast(ubyte*)_surface.pixels;
157             int pitch = _surface.pitch;
158 
159             uint* pixel = cast(uint*)(pixels + y * pitch + x * fmt.BytesPerPixel);
160             ubyte r, g, b, a;
161             SDL_GetRGBA(*pixel, fmt, &r, &g, &b, &a);
162             return RGBA(r, g, b, a);
163         }
164 
165         /// Enable the key color as the transparent key.
166         /// See_also: $(LINK https://wiki.libsdl.org/SDL_SetColorKey)
167         /// Throws: $(D SDL2Exception) on error.
168         void setColorKey(bool enable, uint key)
169         {
170             if (0 != SDL_SetColorKey(this._surface, enable ? SDL_TRUE : SDL_FALSE, key))
171                 _sdl2.throwSDL2Exception("SDL_SetColorKey");
172         }
173 
174         /// Enable the (r, g, b, a) key color as the transparent key.
175         /// See_also: $(LINK https://wiki.libsdl.org/SDL_SetColorKey) $(https://wiki.libsdl.org/SDL_MapRGBA)
176         /// Throws: $(D SDL2Exception) on error.
177         void setColorKey(bool enable, ubyte r, ubyte g, ubyte b, ubyte a = 0)
178         {
179             uint key = SDL_MapRGBA(cast(const)this._surface.format, r, g, b, a);
180             this.setColorKey(enable, key);
181         }
182 
183         /// Perform a fast surface copy of given source surface to this destination surface.
184         /// See_also: $(LINK http://wiki.libsdl.org/SDL_BlitSurface)
185         /// Throws: $(D SDL2Exception) on error.
186         void blit(SDL2Surface source, SDL_Rect srcRect, SDL_Rect dstRect)
187         {
188             if (0 != SDL_BlitSurface(source._surface, &srcRect, _surface, &dstRect))
189                 _sdl2.throwSDL2Exception("SDL_BlitSurface");
190         }
191 
192         /// Perform a scaled surface copy of given source surface to this destination surface.
193         /// See_also: $(LINK http://wiki.libsdl.org/SDL_BlitScaled)
194         /// Throws: $(D SDL2Exception) on error.
195         void blitScaled(SDL2Surface source, SDL_Rect srcRect, SDL_Rect dstRect)
196         {
197             if (0 != SDL_BlitScaled(source._surface, &srcRect, _surface, &dstRect))
198                 _sdl2.throwSDL2Exception("SDL_BlitScaled");
199         }
200     }
201 
202     package
203     {
204         SDL2 _sdl2;
205         SDL_Surface* _surface;
206         Owned _handleOwned;
207     }
208 }