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         /// 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         ~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         /// See_also: $(LINK http://wiki.libsdl.org/SDL_ConvertSurface)
73         /// Returns: A new surface.
74         SDL2Surface convert(const(SDL_PixelFormat)* newFormat)
75         {
76             SDL_Surface* surface = SDL_ConvertSurface(_surface, newFormat, 0);
77             if (surface is null)
78                 _sdl2.throwSDL2Exception("SDL_ConvertSurface");
79             assert(surface != _surface); // should not be the same handle
80             return new SDL2Surface(_sdl2, surface, Owned.YES);
81         }
82 
83         /// Returns: A copy of the surface, useful for taking ownership of not-owned pixel data.
84         /// See_also: $WEB(wiki.libsdl.org/SDL_CreateRGBSurfaceFrom,SDL_CreateRGBSurfaceFrom)
85         SDL2Surface clone()
86         {
87             return convert(pixelFormat());
88         }
89 
90         /// Returns: Width of the surface in pixels.
91         @property int width() const
92         {
93             return _surface.w;
94         }
95 
96         /// Returns: Height of the surface in pixels.
97         @property int height() const
98         {
99             return _surface.h;
100         }
101 
102         /// Returns: Pointer to surface data.
103         /// You must lock the surface before accessng it.
104         ubyte* pixels()
105         {
106             return cast(ubyte*) _surface.pixels;
107         }
108 
109         /// Get the surface pitch (number of bytes between lines).
110         size_t pitch()
111         {
112             return _surface.pitch;
113         }
114 
115         /// Lock the surface, allow to use pixels().
116         /// Throws: $(D SDL2Exception) on error.
117         void lock()
118         {
119             if (SDL_LockSurface(_surface) != 0)
120                 _sdl2.throwSDL2Exception("SDL_LockSurface");
121         }
122 
123         /// Unlock the surface.
124         void unlock()
125         {
126             SDL_UnlockSurface(_surface);
127         }
128 
129         /// Returns: SDL handle.
130         SDL_Surface* handle()
131         {
132             return _surface;
133         }
134 
135         /// Returns: SDL_PixelFormat which describe the surface.
136         SDL_PixelFormat* pixelFormat()
137         {
138             return _surface.format;
139         }
140 
141         ///
142         struct RGBA
143         {
144             ubyte r, g, b, a;
145         }
146 
147         /// Get a surface pixel color.
148         /// Bugs: must be locked when using this method. Slow!
149         RGBA getRGBA(int x, int y)
150         {
151             // crash if out of image
152             if (x < 0 || x >= width())
153                 assert(0);
154 
155             if (y < 0 || y >= height())
156                 assert(0);
157             
158             SDL_PixelFormat* fmt = _surface.format;
159 
160             ubyte* pixels = cast(ubyte*)_surface.pixels;
161             int pitch = _surface.pitch;
162 
163             uint* pixel = cast(uint*)(pixels + y * pitch + x * fmt.BytesPerPixel);
164             ubyte r, g, b, a;
165             SDL_GetRGBA(*pixel, fmt, &r, &g, &b, &a);
166             return RGBA(r, g, b, a);
167         }
168     }
169 
170     package
171     {
172         SDL2 _sdl2;
173         SDL_Surface* _surface;
174         Owned _handleOwned;
175     }
176 }