1 /**
2  * SDL_Image support.
3  *
4  * License:
5  *   This Source Code Form is subject to the terms of
6  *   the Mozilla Public License, v. 2.0. If a copy of
7  *   the MPL was not distributed with this file, You
8  *   can obtain one at http://mozilla.org/MPL/2.0/.
9  *
10  * Authors:
11  *   Vladimir Panteleev <vladimir@thecybershadow.net>
12  */
13 
14 module ae.utils.graphics.sdl2image;
15 
16 import ae.utils.graphics.color;
17 import ae.utils.graphics.image;
18 
19 import derelict.sdl2.sdl;
20 import derelict.sdl2.image;
21 
22 import std.exception;
23 import std.string : toStringz, format;
24 
25 static this()
26 {
27 	DerelictSDL2.load();
28 	DerelictSDL2Image.load();
29 
30 	IMG_Init(IMG_INIT_JPG | IMG_INIT_PNG);
31 }
32 
33 auto loadImage(string path, ref Image!RGBX target = *new Image!RGBX)
34 {
35 	auto surface = IMG_Load(toStringz(path));
36 	enforce(surface, "Failed to load image " ~ path);
37 	scope(exit) SDL_FreeSurface(surface);
38 	target.size(surface.w, surface.h);
39 
40 	if (surface.format.palette)
41 	{
42 		switch (surface.format.BitsPerPixel)
43 		{
44 			case 1: depalettize!1(cast(ubyte*)surface.pixels, target.pixels.ptr, surface.format.palette, surface.w, surface.h, surface.pitch); break;
45 			case 2: depalettize!2(cast(ubyte*)surface.pixels, target.pixels.ptr, surface.format.palette, surface.w, surface.h, surface.pitch); break;
46 			case 4: depalettize!4(cast(ubyte*)surface.pixels, target.pixels.ptr, surface.format.palette, surface.w, surface.h, surface.pitch); break;
47 			case 8: depalettize!8(cast(ubyte*)surface.pixels, target.pixels.ptr, surface.format.palette, surface.w, surface.h, surface.pitch); break;
48 			default:
49 				enforce(false, format("Don't know how to depalettize image with %d bits per pixel", surface.format.BitsPerPixel));
50 		}
51 	}
52 	else
53 		rgbTransform(cast(ubyte*)surface.pixels, target.pixels.ptr, surface.format, surface.w, surface.h, surface.pitch);
54 
55 	return target;
56 }
57 
58 private:
59 
60 void depalettize(int BITS)(ubyte* src, RGBX* dst, SDL_Palette *palette, uint w, uint h, int pitch)
61 {
62 	static assert(BITS <= 8);
63 
64 	auto ncolors = palette.ncolors;
65 	foreach (y; 0..h)
66 	{
67 		auto p = src;
68 		foreach (x; 0..w)
69 		{
70 			ubyte c;
71 			static if (BITS == 8)
72 				c = *p++;
73 			else
74 				c = p[x / (8/BITS)] & (((1<<BITS)-1) << (x % (8/BITS)));
75 
76 			if (c >= ncolors)
77 				throw new Exception("Color index exceeds number of colors in palette");
78 			*dst++ = cast(RGBX)(palette.colors[c]);
79 		}
80 		src += pitch;
81 	}
82 }
83 
84 void rgbTransform(ubyte* src, RGBX* dst, SDL_PixelFormat *format, uint w, uint h, int pitch)
85 {
86 	auto bpp = format.BitsPerPixel;
87 	enforce(bpp%8 == 0 && bpp >= 8 && bpp <= 32, format("Don't know how to process unpalettized image with %d bits per pixel", bpp));
88 
89 	if (bpp == 32
90 	 && format.Rmask == 0x00_00_00_FF && format.Rshift== 0
91 	 && format.Gmask == 0x00_00_FF_00 && format.Rshift== 8
92 	 && format.Bmask == 0x00_FF_00_00 && format.Rshift==16)
93 	{
94 		// Everything is already in our desired format.
95 		foreach (y; 0..h)
96 		{
97 			auto p = cast(RGBX*)src;
98 			dst[0..w] = p[0..w];
99 			src += pitch;
100 			dst += w;
101 		}
102 	}
103 	else
104 	{
105 		// Use SDL_GetRGB for whatever weird formats.
106 		auto Bpp = format.BytesPerPixel;
107 		foreach (y; 0..h)
108 		{
109 			auto p = src;
110 			foreach (x; 0..w)
111 			{
112 				RGBX c;
113 				SDL_GetRGB(*cast(uint*)p, format, &c.r, &c.g, &c.b);
114 				*dst++ = c;
115 				p += Bpp;
116 			}
117 			src += pitch;
118 		}
119 	}
120 }