1 module gfm.sdl2.surface;
2 
3 import derelict.sdl2.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         /// Throws: $(D SDL2Exception) on error.
31         this(SDL2 sdl2, int width, int height, int depth,
32              uint Rmask, uint Gmask, uint Bmask, uint Amask)
33         {
34             _sdl2 = sdl2;
35             _surface = SDL_CreateRGBSurface(0, width, height, depth, Rmask, Gmask, Bmask, Amask);
36             if (_surface is null)
37                 _sdl2.throwSDL2Exception("SDL_CreateRGBSurface");
38             _handleOwned = Owned.YES;
39         }
40 
41         /// Create surface from RGBA data. Pixels data is <b>not</b> and not owned.
42         /// See_also: clone, $(WEB wiki.libsdl.org/SDL_CreateRGBSurfaceFrom,SDL_CreateRGBSurfaceFrom)
43         /// Throws: $(D SDL2Exception) on error.
44         this(SDL2 sdl2, void* pixels, int width, int height, int depth, int pitch, 
45              uint Rmask, uint Gmask, uint Bmask, uint Amask)
46         {
47             _sdl2 = sdl2;
48             _surface = SDL_CreateRGBSurfaceFrom(pixels, width, height, depth, pitch, Rmask, Gmask, Bmask, Amask);
49             if (_surface is null)
50                 _sdl2.throwSDL2Exception("SDL_CreateRGBSurfaceFrom");
51             _handleOwned = Owned.YES;
52         }
53 
54         ~this()
55         {
56             close();
57         }
58 
59         /// Releases the SDL resource.
60         void close()
61         {
62             if (_surface !is null)
63             {
64                 if (_handleOwned == Owned.YES)
65                     SDL_FreeSurface(_surface);
66                 _surface = null;
67             }
68         }
69 
70         /// Converts the surface to another format.
71         /// Returns: A new surface.
72         SDL2Surface convert(const(SDL_PixelFormat)* newFormat)
73         {
74             SDL_Surface* surface = SDL_ConvertSurface(_surface, newFormat, 0);
75             if (surface is null)
76                 _sdl2.throwSDL2Exception("SDL_ConvertSurface");
77             assert(surface != _surface); // should not be the same handle
78             return new SDL2Surface(_sdl2, surface, Owned.YES);
79         }
80 
81         /// Returns: A copy of the surface, useful for taking ownership of not-owned pixel data.
82         /// See_also: $WEB(wiki.libsdl.org/SDL_CreateRGBSurfaceFrom,SDL_CreateRGBSurfaceFrom)
83         SDL2Surface clone()
84         {
85             return convert(pixelFormat());
86         }
87 
88         /// Returns: Width of the surface in pixels.
89         @property int width() const
90         {
91             return _surface.w;
92         }
93 
94         /// Returns: Height of the surface in pixels.
95         @property int height() const
96         {
97             return _surface.h;
98         }
99 
100         /// Returns: Pointer to surface data.
101         /// You must lock the surface before accessng it.
102         ubyte* pixels()
103         {
104             return cast(ubyte*) _surface.pixels;
105         }
106 
107         /// Get the surface pitch (number of bytes between lines).
108         size_t pitch()
109         {
110             return _surface.pitch;
111         }
112 
113         /// Lock the surface, allow to use pixels().
114         /// Throws: $(D SDL2Exception) on error.
115         void lock()
116         {
117             if (SDL_LockSurface(_surface) != 0)
118                 _sdl2.throwSDL2Exception("SDL_LockSurface");
119         }
120 
121         /// Unlock the surface.
122         void unlock()
123         {
124             SDL_UnlockSurface(_surface);
125         }
126 
127         /// Returns: SDL handle.
128         SDL_Surface* handle()
129         {
130             return _surface;
131         }
132 
133         /// Returns: SDL_PixelFormat which describe the surface.
134         SDL_PixelFormat* pixelFormat()
135         {
136             return _surface.format;
137         }
138 
139         struct RGBA
140         {
141             ubyte r, g, b, a;
142         }
143 
144         /// Get a surface pixel color.
145         /// Bugs: must be locked when using this method. Slow!
146         RGBA getRGBA(int x, int y)
147         {
148             // crash if out of image
149             if (x < 0 || x >= width())
150                 assert(0);
151 
152             if (y < 0 || y >= height())
153                 assert(0);
154             
155             SDL_PixelFormat* fmt = _surface.format;
156 
157             ubyte* pixels = cast(ubyte*)_surface.pixels;
158             int pitch = _surface.pitch;
159 
160             uint* pixel = cast(uint*)(pixels + y * pitch + x * fmt.BytesPerPixel);
161             ubyte r, g, b, a;
162             SDL_GetRGBA(*pixel, fmt, &r, &g, &b, &a);
163             return RGBA(r, g, b, a);
164         }
165     }
166 
167     package
168     {
169         SDL2 _sdl2;
170         SDL_Surface* _surface;
171         Owned _handleOwned;
172     }
173 }