/// this file imports all available image decoders, and provides convenient functions to load image regardless of it's format. module arsd.image; public import arsd.color; public import arsd.png; public import arsd.jpeg; public import arsd.bmp; static if (__traits(compiles, { import iv.vfs; })) enum ArsdImageHasIVVFS = true; else enum ArsdImageHasIVVFS = false; private bool strEquCI (const(char)[] s0, const(char)[] s1) pure nothrow @trusted @nogc { if (s0.length != s1.length) return false; foreach (immutable idx, char ch; s0) { if (ch >= 'A' && ch <= 'Z') ch += 32; // poor man's tolower() char c1 = s1.ptr[idx]; if (c1 >= 'A' && c1 <= 'Z') c1 += 32; // poor man's tolower() if (ch != c1) return false; } return true; } /// Image formats `arsd.image` can load (except `Unknown`, of course). enum ImageFileFormat { Unknown, /// Png, /// Bmp, /// Jpeg, /// } /// Try to guess image format from file extension. public ImageFileFormat guessImageFormatFromExtension (const(char)[] filename) { if (filename.length < 2) return ImageFileFormat.Unknown; size_t extpos = filename.length; version(Windows) { while (extpos > 0 && filename.ptr[extpos-1] != '.' && filename.ptr[extpos-1] != '/' && filename.ptr[extpos-1] != '\\' && filename.ptr[extpos-1] != ':') --extpos; } else { while (extpos > 0 && filename.ptr[extpos-1] != '.' && filename.ptr[extpos-1] != '/') --extpos; } if (extpos == 0 || filename.ptr[extpos-1] != '.') throw new Exception("cannot determine file format from extension"); auto ext = filename[extpos..$]; if (strEquCI(ext, "png")) return ImageFileFormat.Png; if (strEquCI(ext, "bmp")) return ImageFileFormat.Bmp; if (strEquCI(ext, "jpg") || strEquCI(ext, "jpeg")) return ImageFileFormat.Jpeg; return ImageFileFormat.Unknown; } /// Try to guess image format by first data bytes. public ImageFileFormat guessImageFormatFromMemory (const(void)[] membuf) { auto buf = cast(const(ubyte)[])membuf; if (buf.length == 0) return ImageFileFormat.Unknown; // detect file format // png if (buf.length > 7 && buf.ptr[0] == 0x89 && buf.ptr[1] == 0x50 && buf.ptr[2] == 0x4E && buf.ptr[3] == 0x47 && buf.ptr[4] == 0x0D && buf.ptr[5] == 0x0A && buf.ptr[6] == 0x1A) { return ImageFileFormat.Png; } // bmp if (buf.length > 6 && buf.ptr[0] == 'B' && buf.ptr[1] == 'M') { uint datasize = buf.ptr[2]|(buf.ptr[3]<<8)|(buf.ptr[4]<<16)|(buf.ptr[5]<<24); if (datasize > 6 && datasize <= buf.length) return ImageFileFormat.Bmp; } // jpg try { int width, height, components; if (detect_jpeg_image_from_memory(buf, width, height, components)) return ImageFileFormat.Jpeg; } catch (Exception e) {} // sorry return ImageFileFormat.Unknown; } /// Try to guess image format from file name and load that image. public MemoryImage loadImageFromFile(T:const(char)[]) (T filename) { static if (is(T == typeof(null))) { throw new Exception("cannot load image from unnamed file"); } else { final switch (guessImageFormatFromExtension(filename)) { case ImageFileFormat.Unknown: throw new Exception("cannot determine file format from extension"); case ImageFileFormat.Png: static if (is(T == string)) return readPng(filename); else return readPng(filename.idup); case ImageFileFormat.Bmp: static if (is(T == string)) return readBmp(filename); else return readBmp(filename.idup); case ImageFileFormat.Jpeg: return readJpeg(filename); } } } /// Try to guess image format from data and load that image. public MemoryImage loadImageFromMemory (const(void)[] membuf) { final switch (guessImageFormatFromMemory(membuf)) { case ImageFileFormat.Unknown: throw new Exception("cannot determine file format"); case ImageFileFormat.Png: return imageFromPng(readPng(cast(const(ubyte)[])membuf)); case ImageFileFormat.Bmp: return readBmp(cast(const(ubyte)[])membuf); case ImageFileFormat.Jpeg: return readJpegFromMemory(cast(const(ubyte)[])membuf); } } static if (ArsdImageHasIVVFS) { import iv.vfs; public MemoryImage loadImageFromFile (VFile fl) { auto fsz = fl.size-fl.tell; if (fsz < 4) throw new Exception("cannot determine file format"); if (fsz > int.max/8) throw new Exception("image data too big"); auto data = new ubyte[](cast(uint)fsz); scope(exit) delete data; // this should be safe, as image will copy data to it's internal storage fl.rawReadExact(data); return loadImageFromMemory(data); } }