1 module d2d.rendering.bitmap;
2 
3 import d2d;
4 
5 /// Thin wrap around SDL_Surface including loading [png, webp, tiff, bmp] using SDL_Image.
6 class Bitmap : IVerifiable, IDisposable
7 {
8 	private SDL_Surface* _handle;
9 
10 	/// Handle to the `SDL_Surface*`.
11 	public @property SDL_Surface* surface()
12 	{
13 		return _handle;
14 	}
15 
16 	/// Checks if the handle is not null.
17 	public @property bool valid()
18 	{
19 		return _handle !is null;
20 	}
21 
22 	/// Width of this bitmap. Returns 0 if invalid.
23 	public @property int width()
24 	{
25 		if (!valid)
26 			return 0;
27 		return _handle.w;
28 	}
29 
30 	/// Height of this bitmap. Returns 0 if invalid.
31 	public @property int height()
32 	{
33 		if (!valid)
34 			return 0;
35 		return _handle.h;
36 	}
37 
38 	private this(SDL_Surface * surface)
39 	{
40 		_handle = surface;
41 	}
42 
43 	/// Creates a new width x height bitmap with a specified bit depth.
44 	public this(int width, int height, int depth = 24, int redChannel = 0, int greenChannel = 0, int blueChannel = 0, int alphaChannel = 0)
45 	{
46 		_handle = SDL_CreateRGBSurface(0, width, height, depth, redChannel, greenChannel, blueChannel, alphaChannel);
47 	}
48 
49 	public ~this()
50 	{
51 		dispose();
52 	}
53 
54 	/// Creates a new width x height bitmap with a specified bit depth containing pixel data.
55 	public this(void[] pixels, int width, int height, int depth = 24, int redChannel = 0, int greenChannel = 0, int blueChannel = 0, int alphaChannel = 0)
56 	{
57 		_handle = SDL_CreateRGBSurfaceFrom(pixels.ptr, width, height, depth, width * (depth >> 3), redChannel, greenChannel, blueChannel, alphaChannel);
58 	}
59 
60 	/// Creates a bitmap from a `SDL_Surface*`.
61 	public static Bitmap fromSurface(SDL_Surface* surface)
62 	{
63 		return new Bitmap(surface);
64 	}
65 
66 	/// Loads a png/webp/tiff/bmp from a file on the filesystem.
67 	public static Bitmap load(string file)
68 	{
69 		Bitmap bmp = new Bitmap(IMG_Load(file.toStringz()));
70 
71 		if (!bmp.valid)
72 			throw new Exception(cast(string) IMG_GetError().fromStringz());
73 
74 		return bmp;
75 	}
76 
77 	/// Deallocates the memory and invalidates `this`.
78 	public void dispose()
79 	{
80 		if (valid)
81 		{
82 			SDL_FreeSurface(_handle);
83 			_handle = null;
84 		}
85 	}
86 
87 	/// Saves the bitmap to a .bmp file.
88 	public void save(string file)
89 	{
90 		SDL_SaveBMP(_handle, file.toStringz());
91 	}
92 
93 	/// Copies the bitmap and creates a new bitmap in the given pixelformat.
94 	public Bitmap convert(const SDL_PixelFormat* format)
95 	{
96 		return new Bitmap(SDL_ConvertSurface(_handle, format, 0));
97 	}
98 
99 	/// Gets the rgb hex from the color based on the bitmap format.
100 	public uint mapRGB(Color color)
101 	{
102 		return SDL_MapRGB(_handle.format, color.R, color.G, color.B);
103 	}
104 
105 	/// Gets the rgb hex from the color based on the bitmap format.
106 	public uint mapRGBA(ubyte r, ubyte g, ubyte b, ubyte a)
107 	{
108 		return SDL_MapRGBA(_handle.format, r, g, b, a);
109 	}
110 
111 	/// Fills the entire bitmap with one color.
112 	public void fill(Color color)
113 	{
114 		SDL_FillRect(_handle, null, mapRGB(color));
115 	}
116 
117 	/// Fills a rectangle with one color.
118 	public void fill(int x, int y, int width, int height, Color color)
119 	{
120 		SDL_FillRect(_handle, new SDL_Rect(x, y, width, height), mapRGB(color));
121 	}
122 
123 	/// Gets the RGB color at position x, y. (0, 0) is top left.
124 	public Color getPixel(int x, int y)
125 	{
126 		ubyte r, g, b;
127 		SDL_GetRGB((cast(uint*) _handle.pixels)[x + y * width], _handle.format, &r, &g, &b);
128 		return Color(r, g, b);
129 	}
130 
131 	/// Gets the RGBA color at position x, y as R, G, B, A ubyte array. (0, 0) is top left.
132 	public ubyte[] getPixelRGBA(int x, int y)
133 	{
134 		ubyte r, g, b, a;
135 		SDL_GetRGBA((cast(uint*) _handle.pixels)[x + y * width], _handle.format, &r, &g, &b, &a);
136 		return [r, g, b, a];
137 	}
138 
139 	/// Sets the RGB color at position x, y. (0, 0) is top left.
140 	public void setPixel(int x, int y, Color color)
141 	{
142 		(cast(uint*) _handle.pixels)[x + y * width] = mapRGB(color);
143 	}
144 
145 	/// Sets the RGBA color at position x, y. (0, 0) is top left.
146 	public void setPixelRGBA(int x, int y, ubyte r, ubyte g, ubyte b, ubyte a)
147 	{
148 		(cast(uint*) _handle.pixels)[x + y * width] = mapRGBA(r, g, b, a);
149 	}
150 }