diff --git a/src/dlangui/graphics/images.d b/src/dlangui/graphics/images.d index 67fcecdd..2e3216eb 100644 --- a/src/dlangui/graphics/images.d +++ b/src/dlangui/graphics/images.d @@ -31,6 +31,8 @@ version (USE_DEIMAGE) { version (USE_DLIBIMAGE) { import dlib.image.io.io; import dlib.image.image; + import dlib.image.io.png; + import dlib.image.io.jpeg; version = ENABLE_DLIBIMAGE_JPEG; } @@ -39,10 +41,24 @@ import dlangui.core.types; import dlangui.graphics.colors; import dlangui.graphics.drawbuf; import std.stream; +import std.path; import std.conv : to; + /// load and decode image from file to ColorDrawBuf, returns null if loading or decoding is failed ColorDrawBuf loadImage(string filename) { + try { + immutable ubyte[] data = cast(immutable ubyte[])std.file.read(filename); + return loadImage(data, filename); + } catch (Exception e) { + Log.e("exception while loading image from file ", filename); + Log.e(to!string(e)); + return null; + } +} + +/// load and decode image from input stream to ColorDrawBuf, returns null if loading or decoding is failed +ColorDrawBuf loadImage(immutable ubyte[] data, string filename) { Log.d("Loading image from file " ~ filename); version (USE_DEIMAGE) { try { @@ -69,6 +85,7 @@ ColorDrawBuf loadImage(string filename) { } } else version (USE_DLIBIMAGE) { import std.algorithm; + static import dlib.core.stream; try { version (ENABLE_DLIBIMAGE_JPEG) { } else { @@ -76,19 +93,23 @@ ColorDrawBuf loadImage(string filename) { if (filename.endsWith(".jpeg") || filename.endsWith(".jpg") || filename.endsWith(".JPG") || filename.endsWith(".JPEG")) return null; } - SuperImage image = dlib.image.io.io.loadImage(filename); + SuperImage image = null; + dlib.core.stream.ArrayStream dlibstream = new dlib.core.stream.ArrayStream(cast(ubyte[])data, data.length); + switch(filename.extension) + { + case ".jpg", ".JPG", ".jpeg": + image = dlib.image.io.jpeg.loadJPEG(dlibstream); + break; + case ".png", ".PNG": + image = dlib.image.io.png.loadPNG(dlibstream); + break; + default: + break; + } + //SuperImage image = dlib.image.io.io.loadImage(filename); if (!image) return null; - int w = image.width; - int h = image.height; - ColorDrawBuf buf = new ColorDrawBuf(w, h); - for (int y = 0; y < h; y++) { - uint * dstLine = buf.scanLine(y); - for (int x = 0; x < w; x++) { - auto pixel = image[x, h - 1 - y].convert(8); - dstLine[x] = makeRGBA(pixel.r, pixel.g, pixel.b, 255 - pixel.a); - } - } + ColorDrawBuf buf = importImage(image); destroy(image); return buf; } catch (Exception e) { @@ -110,6 +131,22 @@ ColorDrawBuf loadImage(string filename) { } +version (USE_DLIBIMAGE) { + ColorDrawBuf importImage(SuperImage image) { + int w = image.width; + int h = image.height; + ColorDrawBuf buf = new ColorDrawBuf(w, h); + for (int y = 0; y < h; y++) { + uint * dstLine = buf.scanLine(y); + for (int x = 0; x < w; x++) { + auto pixel = image[x, h - 1 - y].convert(8); + dstLine[x] = makeRGBA(pixel.r, pixel.g, pixel.b, 255 - pixel.a); + } + } + return buf; + } +} + class ImageDecodingException : Exception { this(string msg) { super(msg); diff --git a/src/dlangui/graphics/resources.d b/src/dlangui/graphics/resources.d index 31d10083..311f8657 100644 --- a/src/dlangui/graphics/resources.d +++ b/src/dlangui/graphics/resources.d @@ -37,9 +37,16 @@ import std.conv; import std.string; import std.path; +/// filename prefix for embedded resources +immutable string EMBEDDED_RESOURCE_PREFIX = "@embedded@/"; + struct EmbeddedResource { - string name; - ubyte[] data; + immutable string name; + immutable ubyte[] data; + this(immutable string name, immutable ubyte[] data) { + this.name = name; + this.data = data; + } } struct EmbeddedResourceList { @@ -47,14 +54,41 @@ struct EmbeddedResourceList { void addResources(EmbeddedResource[] resources) { list ~= resources; } + /// find by exact file name + EmbeddedResource * find(string name) { + for(int i = 0; i < list.length; i++) + if (name.equal(list[i].name)) + return &list[i]; + return null; + } + /// find by name w/o extension + EmbeddedResource * findAutoExtension(string name) { + string xmlname = name ~ ".xml"; + string pngname = name ~ ".png"; + string png9name = name ~ ".9.png"; + string jpgname = name ~ ".jpg"; + string jpegname = name ~ ".jpeg"; + for(int i = 0; i < list.length; i++) { + string s = list[i].name; + if (s.equal(xmlname) || s.equal(pngname) || s.equal(png9name) + || s.equal(jpgname) || s.equal(jpegname)) + return &list[i]; + } + return null; + } } __gshared EmbeddedResourceList embeddedResourceList; EmbeddedResource[] embedResource(string resourceName)() { - static if ((baseName(resourceName)).length > 0) - return [EmbeddedResource(resourceName, cast(ubyte[])import(baseName(resourceName)))]; - else + immutable string name = baseName(resourceName); + static if (name.length > 0) { + immutable ubyte[] data = cast(immutable ubyte[])import(name); + static if (data.length > 0) + return [EmbeddedResource(name, data)]; + else + return []; + } else return []; } @@ -62,8 +96,10 @@ EmbeddedResource[] embedResource(string resourceName)() { EmbeddedResource[] embedResources(string[] resourceNames)() { static if (resourceNames.length == 0) return []; + static if (resourceNames.length == 1) + return embedResource!(resourceNames[0])(); else - return embedResource!(resourceNames[0])() ~ embedResources!(resourceNames[1..$])(); + return embedResources!(resourceNames[0 .. $/2])() ~ embedResources!(resourceNames[$/2 .. $])(); } /// embed all resources from list @@ -78,6 +114,24 @@ __gshared static this() { } } +/// load resource bytes from embedded resource or file +immutable(ubyte[]) loadResourceBytes(string filename) { + if (filename.startsWith(EMBEDDED_RESOURCE_PREFIX)) { + EmbeddedResource * embedded = embeddedResourceList.find(filename[EMBEDDED_RESOURCE_PREFIX.length .. $]); + if (embedded) + return embedded.data; + return null; + } else { + try { + immutable ubyte[] data = cast(immutable ubyte[])std.file.read(filename); + return data; + } catch (Exception e) { + Log.e("exception while loading file ", filename); + return null; + } + } +} + class Drawable : RefCountedObject { //private static int _instanceCount; this() { @@ -538,7 +592,11 @@ class StateDrawable : Drawable { import std.string; try { - string s = cast(string)std.file.read(filename); + string s = cast(string)loadResourceBytes(filename); + if (!s) { + Log.e("Cannot read drawable resource from file ", filename); + return false; + } // Check for well-formedness //check(s); @@ -550,9 +608,6 @@ class StateDrawable : Drawable { } catch (CheckException e) { Log.e("Invalid XML file ", filename); return false; - } catch (Throwable e) { - Log.e("Cannot read drawable resource from file ", filename); - return false; } } @@ -602,10 +657,13 @@ class ImageCache { _used = true; return _drawbuf; } - _drawbuf = loadImage(_filename); - if (_filename.endsWith(".9.png")) - _drawbuf.detectNinePatch(); - _used = true; + immutable ubyte[] data = loadResourceBytes(_filename); + if (data) { + _drawbuf = loadImage(data, _filename); + if (_filename.endsWith(".9.png")) + _drawbuf.detectNinePatch(); + _used = true; + } if (_drawbuf.isNull) _error = true; return _drawbuf; @@ -917,6 +975,12 @@ class DrawableCache { return id; // it's not a file name, just a color #AARRGGBB if (id in _idToFileMap) return _idToFileMap[id]; + EmbeddedResource * embedded = embeddedResourceList.findAutoExtension(id); + if (embedded) { + string fn = EMBEDDED_RESOURCE_PREFIX ~ embedded.name; + _idToFileMap[id] = fn; + return fn; + } foreach(string path; _resourcePaths) { string fn; fn = checkFileName(path, id, ".xml"); diff --git a/src/dlangui/widgets/styles.d b/src/dlangui/widgets/styles.d index 6c82662b..07395aa5 100644 --- a/src/dlangui/widgets/styles.d +++ b/src/dlangui/widgets/styles.d @@ -1278,11 +1278,15 @@ bool loadTheme(Theme theme, string resourceId, int level = 0) { filename = drawableCache.findResource(resourceId); if (!filename || !filename.endsWith(".xml")) return false; - string s = cast(string)std.file.read(filename); + string s = cast(string)loadResourceBytes(filename); + if (!s) { + Log.e("Cannot read XML resource ", resourceId, " from file ", filename); + return false; + } // Check for well-formedness //check(s); - + // Make a DOM tree auto doc = new Document(s); @@ -1290,9 +1294,6 @@ bool loadTheme(Theme theme, string resourceId, int level = 0) { } catch (CheckException e) { Log.e("Invalid XML resource ", resourceId); return false; - } catch (Throwable e) { - Log.e("Cannot read XML resource ", resourceId, " from file ", filename, " exception: ", e); - return false; } }