1 // Written in the D programming language. 2 3 /** 4 DLANGUI library. 5 6 This module contains image loading functions. 7 8 Currently uses FreeImage. 9 10 Usage of libpng is not feasible under linux due to conflicts of library and binding versions. 11 12 Synopsis: 13 14 ---- 15 import dlangui.graphics.images; 16 17 ---- 18 19 Copyright: Vadim Lopatin, 2014 20 License: $(WEB boost.org/LICENSE_1_0.txt, Boost License 1.0). 21 Authors: $(WEB coolreader.org, Vadim Lopatin) 22 */ 23 module dlangui.graphics.images; 24 25 immutable bool USE_LIBPNG = false; 26 immutable bool USE_FREEIMAGE = true; 27 28 import dlangui.core.logger; 29 import dlangui.core.types; 30 import dlangui.graphics.drawbuf; 31 import std.stream; 32 static if (USE_LIBPNG) { 33 import libpng.png; 34 } 35 36 /// load and decode image from file to ColorDrawBuf, returns null if loading or decoding is failed 37 ColorDrawBuf loadImage(string filename) { 38 Log.d("Loading image from file " ~ filename); 39 try { 40 std.stream.File f = new std.stream.File(filename); 41 scope(exit) { f.close(); } 42 return loadImage(f); 43 } catch (Exception e) { 44 Log.e("exception while loading image from file ", filename); 45 return null; 46 } 47 } 48 49 /// load and decode image from stream to ColorDrawBuf, returns null if loading or decoding is failed 50 ColorDrawBuf loadImage(InputStream stream) { 51 if (stream is null || !stream.isOpen) 52 return null; 53 static if (USE_FREEIMAGE) { 54 return loadFreeImage(stream); 55 } else static if (USE_LIBPNG) { 56 return loadPngImage(stream); 57 } 58 } 59 60 class ImageDecodingException : Exception { 61 this(string msg) { 62 super(msg); 63 } 64 } 65 66 shared static this() { 67 //import derelict.freeimage.freeimage; 68 //DerelictFI.load(); 69 } 70 71 static if (USE_FREEIMAGE) { 72 ColorDrawBuf loadFreeImage(InputStream stream) { 73 import derelict.freeimage.freeimage; 74 75 static bool FREE_IMAGE_LOADED; 76 if (!FREE_IMAGE_LOADED) { 77 DerelictFI.load(); 78 FREE_IMAGE_LOADED = true; 79 } 80 81 ubyte imagebuf[]; 82 ubyte readbuf[4096]; 83 for (;;) { 84 size_t bytesRead = stream.read(readbuf); 85 if (!bytesRead) 86 break; 87 imagebuf ~= readbuf[0..bytesRead]; 88 } 89 //pointer to the image, once loaded 90 FIBITMAP *dib = null; //image format 91 FREE_IMAGE_FORMAT fif = FIF_UNKNOWN; 92 // attach the binary data to a memory stream 93 FIMEMORY *hmem = FreeImage_OpenMemory(imagebuf.ptr, imagebuf.length); 94 fif = FreeImage_GetFileTypeFromMemory(hmem); 95 //check that the plugin has reading capabilities and load the file 96 if(!FreeImage_FIFSupportsReading(fif)) { 97 FreeImage_CloseMemory(hmem); 98 return null; 99 } 100 101 // load an image from the memory stream 102 dib = FreeImage_LoadFromMemory(fif, hmem, 0); 103 104 //if the image failed to load, return failure 105 if (!dib) { 106 Log.e("Failed to decode image"); 107 FreeImage_CloseMemory(hmem); 108 return null; 109 } 110 //retrieve the image data 111 ubyte * data = cast(ubyte*)FreeImage_GetBits(dib); 112 //get the image width and height, and size per pixel 113 int width = FreeImage_GetWidth(dib); 114 int height = FreeImage_GetHeight(dib); 115 int bpp = FreeImage_GetBPP(dib); 116 int pixelSize = (bpp + 7)/8; 117 FREE_IMAGE_COLOR_TYPE colorType = FreeImage_GetColorType(dib); 118 int transparentIndex = 0; 119 int transparencyCount = 0; 120 RGBQUAD * palette = null; 121 ubyte * transparencyTable = null; 122 if (colorType == FIC_PALETTE) { 123 palette = FreeImage_GetPalette(dib); 124 transparentIndex = FreeImage_GetTransparentIndex(dib); 125 transparencyCount = FreeImage_GetTransparencyCount(dib); 126 transparencyTable = FreeImage_GetTransparencyTable(dib); 127 } 128 int size = width*height*pixelSize; 129 130 ColorDrawBuf res = new ColorDrawBuf(width, height); 131 132 //swap R and B and invert image while copying 133 ubyte* src; 134 uint* dst; 135 uint r, g, b, a; 136 for( int i = 0, ii = height-1; i < height ; ++i, --ii ) { 137 dst = res.scanLine(i); 138 src = data + (ii * width) * pixelSize; 139 for( int j = 0; j < width; ++j, ++dst, src += pixelSize ) { 140 if (colorType == FIC_PALETTE) { 141 ubyte index = src[0]; 142 a = 0; 143 if (transparencyTable !is null) { 144 a = transparencyTable[index] ^ 0xFF; 145 } else if (transparentIndex >= 0 && index >= transparentIndex && index < transparentIndex + transparencyCount) { 146 a = 0xFF; 147 } 148 RGBQUAD pcolor = palette[index]; 149 r = pcolor.rgbRed; 150 g = pcolor.rgbGreen; 151 b = pcolor.rgbBlue; 152 dst[0] = (a << 24) | (r << 16) | (g << 8) | b; 153 } else { 154 a = 0; 155 switch (pixelSize) { 156 case 4: 157 a = src[3] ^ 255; 158 // fall through 159 goto case; 160 case 3: 161 r = src[2]; 162 g = src[1]; 163 b = src[0]; 164 break; 165 case 2: 166 // todo: do something better 167 r = g = src[1]; 168 b = src[0]; 169 break; 170 default: 171 case 1: 172 r = g = b = src[0]; 173 break; 174 } 175 dst[0] = (a << 24) | (r << 16) | (g << 8) | b; 176 } 177 } 178 } 179 FreeImage_CloseMemory(hmem); 180 return res; 181 } 182 } 183 184 static if (USE_LIBPNG) { 185 186 extern (C) void lvpng_error_func (png_structp png, png_const_charp msg) 187 { 188 string s = fromStringz(msg); 189 Log.d("Error while reading PNG image: ", s); 190 // todo: exceptions do not work inside C function 191 throw new ImageDecodingException("Error while decoding PNG image"); 192 } 193 194 extern (C) void lvpng_warning_func (png_structp png, png_const_charp msg) 195 { 196 string s = fromStringz(msg); 197 Log.d("Warn while reading PNG image: ", s); 198 // todo: exceptions do not work inside C function 199 throw new ImageDecodingException("Error while decoding PNG image"); 200 } 201 202 extern (C) void lvpng_read_func(png_structp png, png_bytep buf, png_size_t len) 203 { 204 InputStream stream = cast(InputStream)png_get_io_ptr(png); 205 ubyte[] localbuf = new ubyte[len]; 206 if (stream.read(localbuf) != len) 207 throw new ImageDecodingException("Error while reading PNG image"); 208 for (uint i = 0; i < len; i++) 209 buf[i] = localbuf[i]; 210 } 211 212 /// load and decode PNG image 213 ColorDrawBuf loadPngImage(InputStream stream) 214 { 215 png_structp png_ptr = null; 216 png_infop info_ptr = null; 217 png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, 218 cast(png_voidp)stream, &lvpng_error_func, &lvpng_warning_func); 219 if ( !png_ptr ) 220 return null; 221 222 try { 223 // 224 info_ptr = png_create_info_struct(png_ptr); 225 if (!info_ptr) 226 lvpng_error_func(png_ptr, "cannot create png info struct"); 227 png_set_read_fn(png_ptr, 228 cast(void*)stream, &lvpng_read_func); 229 png_read_info( png_ptr, info_ptr ); 230 231 232 png_uint_32 width, height; 233 int bit_depth, color_type, interlace_type; 234 png_get_IHDR(png_ptr, info_ptr, &width, &height, 235 &bit_depth, &color_type, &interlace_type, 236 null, null); 237 ColorDrawBuf drawbuf = new ColorDrawBuf(width, height); 238 239 if (color_type & PNG_COLOR_MASK_PALETTE) 240 png_set_palette_to_rgb(png_ptr); 241 242 if (color_type == PNG_COLOR_TYPE_GRAY && bit_depth < 8) 243 png_set_expand_gray_1_2_4_to_8(png_ptr); 244 245 if (png_get_valid(png_ptr, info_ptr, PNG_INFO_tRNS)) 246 png_set_tRNS_to_alpha(png_ptr); 247 248 if (bit_depth == 16) 249 png_set_strip_16(png_ptr); 250 251 png_set_invert_alpha(png_ptr); 252 253 if (bit_depth < 8) 254 png_set_packing(png_ptr); 255 256 png_set_filler(png_ptr, 0, PNG_FILLER_AFTER); 257 258 if (color_type == PNG_COLOR_TYPE_GRAY || 259 color_type == PNG_COLOR_TYPE_GRAY_ALPHA) 260 png_set_gray_to_rgb(png_ptr); 261 262 int number_passes = png_set_interlace_handling(png_ptr); 263 png_set_bgr(png_ptr); 264 265 for (int pass = 0; pass < number_passes; pass++) 266 { 267 for (int y = 0; y < height; y++) 268 { 269 uint * row = drawbuf.scanLine(y); 270 png_read_rows(png_ptr, cast(ubyte **)&row, null, 1); 271 } 272 } 273 274 png_read_end(png_ptr, info_ptr); 275 276 png_destroy_read_struct(&png_ptr, &info_ptr, null); 277 278 return drawbuf; 279 } catch (ImageDecodingException e) { 280 if (png_ptr) 281 { 282 png_destroy_read_struct(&png_ptr, &info_ptr, null); 283 } 284 return null; 285 } 286 } 287 288 //bool LVPngImageSource::CheckPattern( const lUInt8 * buf, int ) 289 //{ 290 //return( !png_sig_cmp((unsigned char *)buf, (png_size_t)0, 4) ); 291 //} 292 293 }