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 <ae@cy.md> 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 static import std..string; 25 26 static this() 27 { 28 DerelictSDL2.load(); 29 DerelictSDL2Image.load(); 30 31 IMG_Init(IMG_INIT_JPG | IMG_INIT_PNG); 32 } 33 34 /// Loads an `Image` using `SDL_Image`. 35 auto loadImage(string path, ref Image!RGBX target = *new Image!RGBX) 36 { 37 auto surface = IMG_Load(toStringz(path)); 38 enforce(surface, "Failed to load image " ~ path); 39 scope(exit) SDL_FreeSurface(surface); 40 target.size(surface.w, surface.h); 41 42 if (surface.format.palette) 43 { 44 switch (surface.format.BitsPerPixel) 45 { 46 case 1: depalettize!1(cast(ubyte*)surface.pixels, cast(RGBX*)target.pixels.ptr, surface.format.palette, surface.w, surface.h, surface.pitch); break; 47 case 2: depalettize!2(cast(ubyte*)surface.pixels, cast(RGBX*)target.pixels.ptr, surface.format.palette, surface.w, surface.h, surface.pitch); break; 48 case 4: depalettize!4(cast(ubyte*)surface.pixels, cast(RGBX*)target.pixels.ptr, surface.format.palette, surface.w, surface.h, surface.pitch); break; 49 case 8: depalettize!8(cast(ubyte*)surface.pixels, cast(RGBX*)target.pixels.ptr, surface.format.palette, surface.w, surface.h, surface.pitch); break; 50 default: 51 enforce(false, format("Don't know how to depalettize image with %d bits per pixel", surface.format.BitsPerPixel)); 52 } 53 } 54 else 55 rgbTransform(cast(ubyte*)surface.pixels, cast(RGBX*)target.pixels.ptr, surface.format, surface.w, surface.h, surface.pitch); 56 57 return target; 58 } 59 60 private: 61 62 void depalettize(int BITS)(ubyte* src, RGBX* dst, SDL_Palette *palette, uint w, uint h, int pitch) 63 { 64 static assert(BITS <= 8); 65 66 auto ncolors = palette.ncolors; 67 foreach (y; 0..h) 68 { 69 auto p = src; 70 foreach (x; 0..w) 71 { 72 ubyte c; 73 static if (BITS == 8) 74 c = *p++; 75 else 76 c = p[x / (8/BITS)] & (((1<<BITS)-1) << (x % (8/BITS))); 77 78 if (c >= ncolors) 79 throw new Exception("Color index exceeds number of colors in palette"); 80 *dst++ = cast(RGBX)(palette.colors[c]); 81 } 82 src += pitch; 83 } 84 } 85 86 void rgbTransform(ubyte* src, RGBX* dst, SDL_PixelFormat *format, uint w, uint h, int pitch) 87 { 88 auto bpp = format.BitsPerPixel; 89 enforce(bpp%8 == 0 && bpp >= 8 && bpp <= 32, std..string.format("Don't know how to process unpalettized image with %d bits per pixel", bpp)); 90 91 if (bpp == 32 92 && format.Rmask == 0x00_00_00_FF && format.Rshift== 0 93 && format.Gmask == 0x00_00_FF_00 && format.Rshift== 8 94 && format.Bmask == 0x00_FF_00_00 && format.Rshift==16) 95 { 96 // Everything is already in our desired format. 97 foreach (y; 0..h) 98 { 99 auto p = cast(RGBX*)src; 100 dst[0..w] = p[0..w]; 101 src += pitch; 102 dst += w; 103 } 104 } 105 else 106 { 107 // Use SDL_GetRGB for whatever weird formats. 108 auto Bpp = format.BytesPerPixel; 109 foreach (y; 0..h) 110 { 111 auto p = src; 112 foreach (x; 0..w) 113 { 114 RGBX c; 115 SDL_GetRGB(*cast(uint*)p, format, &c.r, &c.g, &c.b); 116 *dst++ = c; 117 p += Bpp; 118 } 119 src += pitch; 120 } 121 } 122 }