1 module gfm.sdl2.surface;
2 
3 import derelict.sdl2.sdl;
4 
5 import gfm.math.vector,
6        gfm.sdl2.sdl;
7 
8 /// SDL Surface wrapper.
9 /// A SDL2Surface might own the SDL_Surface* handle or not.
10 final class SDL2Surface
11 {
12     public
13     {
14         /// Whether a SDL Surface is owned by the wrapper or borrowed.
15         enum Owned
16         {
17             NO,  // Not owned.
18             YES  // Owned.
19         }
20 
21         /// Create from an existing SDL_Surface* handle.
22         this(SDL2 sdl2, SDL_Surface* surface, Owned owned)
23         {
24             assert(surface !is null);
25             _sdl2 = sdl2;
26             _surface = surface;
27             _handleOwned = owned;
28         }
29 
30         /// Create a new RGBA surface. Both pixels data and handle are owned.
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         ~this()
56         {
57             close();
58         }
59 
60         /// Releases the SDL resource.
61         void close()
62         {
63             if (_surface !is null)
64             {
65                 if (_handleOwned == Owned.YES)
66                     SDL_FreeSurface(_surface);
67                 _surface = null;
68             }
69         }
70 
71         /// Converts the surface to another format.
72         /// Returns: A new surface.
73         SDL2Surface convert(const(SDL_PixelFormat)* newFormat)
74         {
75             SDL_Surface* surface = SDL_ConvertSurface(_surface, newFormat, 0);
76             if (surface is null)
77                 _sdl2.throwSDL2Exception("SDL_ConvertSurface");
78             assert(surface != _surface); // should not be the same handle
79             return new SDL2Surface(_sdl2, surface, Owned.YES);
80         }
81 
82         /// Returns: A copy of the surface, useful for taking ownership of not-owned pixel data.
83         /// See_also: $WEB(wiki.libsdl.org/SDL_CreateRGBSurfaceFrom,SDL_CreateRGBSurfaceFrom)
84         SDL2Surface clone()
85         {
86             return convert(pixelFormat());
87         }
88 
89         /// Returns: Width of the surface in pixels.
90         @property int width() const
91         {
92             return _surface.w;
93         }
94 
95         /// Returns: Height of the surface in pixels.
96         @property int height() const
97         {
98             return _surface.h;
99         }
100 
101         /// Returns: Pointer to surface data.
102         /// You must lock the surface before accessng it.
103         ubyte* pixels()
104         {
105             return cast(ubyte*) _surface.pixels;
106         }
107 
108         /// Get the surface pitch (number of bytes between lines).
109         size_t pitch()
110         {
111             return _surface.pitch;
112         }
113 
114         /// Lock the surface, allow to use pixels().
115         /// Throws: $(D SDL2Exception) on error.
116         void lock()
117         {
118             if (SDL_LockSurface(_surface) != 0)
119                 _sdl2.throwSDL2Exception("SDL_LockSurface");
120         }
121 
122         /// Unlock the surface.
123         void unlock()
124         {
125             SDL_UnlockSurface(_surface);
126         }
127 
128         /// Returns: SDL handle.
129         SDL_Surface* handle()
130         {
131             return _surface;
132         }
133 
134         /// Returns: SDL_PixelFormat which describe the surface.
135         SDL_PixelFormat* pixelFormat()
136         {
137             return _surface.format;
138         }
139 
140         /// Get a surface pixel color.
141         /// Bugs: must be locked when using this method. Slow!
142         vec4ub getRGBA(int x, int y)
143         {
144             // crash if out of image
145             if (x < 0 || x >= width())
146                 assert(0);
147 
148             if (y < 0 || y >= height())
149                 assert(0);
150             
151             SDL_PixelFormat* fmt = _surface.format;
152 
153             ubyte* pixels = cast(ubyte*)_surface.pixels;
154             int pitch = _surface.pitch;
155 
156             uint* pixel = cast(uint*)(pixels + y * pitch + x * fmt.BytesPerPixel);
157             ubyte r, g, b, a;
158             SDL_GetRGBA(*pixel, fmt, &r, &g, &b, &a);
159             return vec4ub(r, g, b, a);
160         }
161     }
162 
163     package
164     {
165         SDL2 _sdl2;
166         SDL_Surface* _surface;
167         Owned _handleOwned;
168     }
169 }