diff --git a/src/dlangui/graphics/drawbuf.d b/src/dlangui/graphics/drawbuf.d index 93dcf362..cd70b355 100644 --- a/src/dlangui/graphics/drawbuf.d +++ b/src/dlangui/graphics/drawbuf.d @@ -18,9 +18,13 @@ uint blendARGB(uint dst, uint src, uint alpha) { class DrawBuf : RefCountedObject { protected Rect _clipRect; + /// returns current width @property int width() { return 0; } + /// returns current height @property int height() { return 0; } + /// returns clipping rectangle, when clipRect.isEmpty == true -- means no clipping. @property ref Rect clipRect() { return _clipRect; } + /// sets new clipping rectangle, when clipRect.isEmpty == true -- means no clipping. @property void clipRect(const ref Rect rect) { _clipRect = rect; _clipRect.intersect(Rect(0, 0, width, height)); @@ -40,6 +44,9 @@ class DrawBuf : RefCountedObject { } void beforeDrawing() { } void afterDrawing() { } + /// returns buffer bits per pixel + @property int bpp() { return 0; } + /// returns pointer to ARGB scanline, null if y is out of range or buffer doesn't provide access to its memory uint * scanLine(int y) { return null; } abstract void resize(int width, int height); abstract void fill(uint color); @@ -52,9 +59,13 @@ class DrawBuf : RefCountedObject { ~this() { clear(); } } +alias DrawBufRef = Ref!DrawBuf; + class ColorDrawBufBase : DrawBuf { int _dx; int _dy; + /// returns buffer bits per pixel + override @property int bpp() { return 32; } @property override int width() { return _dx; } @property override int height() { return _dy; } override void fillRect(int left, int top, int right, int bottom, uint color) { diff --git a/src/dlangui/graphics/fonts.d b/src/dlangui/graphics/fonts.d index 3ffa8351..3730595b 100644 --- a/src/dlangui/graphics/fonts.d +++ b/src/dlangui/graphics/fonts.d @@ -1,17 +1,17 @@ -module dlangui.graphics.fonts; -public import dlangui.graphics.drawbuf; -public import dlangui.core.types; -public import dlangui.core.logger; -import std.algorithm; - -enum FontFamily : int { - SansSerif, - Serif, - Fantasy, - Cursive, - MonoSpace -} - +module dlangui.graphics.fonts; +public import dlangui.graphics.drawbuf; +public import dlangui.core.types; +public import dlangui.core.logger; +import std.algorithm; + +enum FontFamily : int { + SansSerif, + Serif, + Fantasy, + Cursive, + MonoSpace +} + struct Glyph { ubyte blackBoxX; ///< 0: width of glyph @@ -82,101 +82,101 @@ struct GlyphCache clear(); } } - -class Font : RefCountedObject { - abstract @property int size(); - abstract @property int height(); - abstract @property int weight(); - abstract @property int baseline(); - abstract @property bool italic(); - abstract @property string face(); - abstract @property FontFamily family(); - abstract @property bool isNull(); - // measure text string, return accumulated widths[] (distance to end of n-th character), returns number of measured chars. - abstract int measureText(const dchar[] text, ref int[] widths, int maxWidth); - // draw text string to buffer - abstract void drawText(DrawBuf buf, int x, int y, const dchar[] text, uint color); - abstract Glyph * getCharGlyph(dchar ch); - + +class Font : RefCountedObject { + abstract @property int size(); + abstract @property int height(); + abstract @property int weight(); + abstract @property int baseline(); + abstract @property bool italic(); + abstract @property string face(); + abstract @property FontFamily family(); + abstract @property bool isNull(); + // measure text string, return accumulated widths[] (distance to end of n-th character), returns number of measured chars. + abstract int measureText(const dchar[] text, ref int[] widths, int maxWidth); + // draw text string to buffer + abstract void drawText(DrawBuf buf, int x, int y, const dchar[] text, uint color); + abstract Glyph * getCharGlyph(dchar ch); + // clear usage flags for all entries abstract void checkpoint(); // removes entries not used after last call of checkpoint() or cleanup() abstract void cleanup(); - - void clear() {} - - ~this() { clear(); } -} -alias FontRef = Ref!Font; - -struct FontList { - FontRef[] _list; - uint _len; - ~this() { - for (uint i = 0; i < _len; i++) { - _list[i].clear(); - } - } - // returns item by index - ref FontRef get(int index) { - return _list[index]; - } - // returns index of found item, -1 if not found - int find(int size, int weight, bool italic, FontFamily family, string face) { - for (int i = 0; i < _len; i++) { - Font item = _list[i].get; - if (item.family != family) - continue; - if (item.size != size) - continue; - if (item.italic != italic || item.weight != weight) - continue; - if (!equal(item.face, face)) - continue; - return i; - } - return -1; - } - ref FontRef add(Font item) { - Log.d("FontList.add() enter"); - if (_len >= _list.length) { - _list.length = _len < 16 ? 16 : _list.length * 2; - } - _list[_len++] = item; - Log.d("FontList.add() exit"); - return _list[_len - 1]; - } - // remove unused items - with reference == 1 - void cleanup() { - for (int i = 0; i < _len; i++) - if (_list[i].refCount <= 1) - _list[i].clear(); - int dst = 0; - for (int i = 0; i < _len; i++) { - if (!_list[i].isNull) - if (i != dst) - _list[dst++] = _list[i]; - } - _len = dst; - for (int i = 0; i < _len; i++) - _list[i].cleanup(); - } - void checkpoint() { - for (int i = 0; i < _len; i++) - _list[i].checkpoint(); - } -} - -class FontManager { - static __gshared FontManager _instance; - static @property void instance(FontManager manager) { - _instance = manager; - } - static @property FontManager instance() { - return _instance; - } - - abstract ref FontRef getFont(int size, int weight, bool italic, FontFamily family, string face); + + void clear() {} + + ~this() { clear(); } +} +alias FontRef = Ref!Font; + +struct FontList { + FontRef[] _list; + uint _len; + ~this() { + for (uint i = 0; i < _len; i++) { + _list[i].clear(); + } + } + // returns item by index + ref FontRef get(int index) { + return _list[index]; + } + // returns index of found item, -1 if not found + int find(int size, int weight, bool italic, FontFamily family, string face) { + for (int i = 0; i < _len; i++) { + Font item = _list[i].get; + if (item.family != family) + continue; + if (item.size != size) + continue; + if (item.italic != italic || item.weight != weight) + continue; + if (!equal(item.face, face)) + continue; + return i; + } + return -1; + } + ref FontRef add(Font item) { + Log.d("FontList.add() enter"); + if (_len >= _list.length) { + _list.length = _len < 16 ? 16 : _list.length * 2; + } + _list[_len++] = item; + Log.d("FontList.add() exit"); + return _list[_len - 1]; + } + // remove unused items - with reference == 1 + void cleanup() { + for (int i = 0; i < _len; i++) + if (_list[i].refCount <= 1) + _list[i].clear(); + int dst = 0; + for (int i = 0; i < _len; i++) { + if (!_list[i].isNull) + if (i != dst) + _list[dst++] = _list[i]; + } + _len = dst; + for (int i = 0; i < _len; i++) + _list[i].cleanup(); + } + void checkpoint() { + for (int i = 0; i < _len; i++) + _list[i].checkpoint(); + } +} + +class FontManager { + static __gshared FontManager _instance; + static @property void instance(FontManager manager) { + _instance = manager; + } + static @property FontManager instance() { + return _instance; + } + + abstract ref FontRef getFont(int size, int weight, bool italic, FontFamily family, string face); // clear usage flags for all entries abstract void checkpoint(); @@ -184,5 +184,5 @@ class FontManager { // removes entries not used after last call of checkpoint() or cleanup() abstract void cleanup(); - ~this() {} -} + ~this() {} +} diff --git a/src/dlangui/graphics/images.d b/src/dlangui/graphics/images.d index 37d5ef80..c4c2e6ca 100644 --- a/src/dlangui/graphics/images.d +++ b/src/dlangui/graphics/images.d @@ -3,7 +3,77 @@ module dlangui.graphics.images; import dlangui.graphics.drawbuf; import std.stream; import libpng.png; -import core.sys.posix.setjmp; + +/// decoded image cache +class ImageCache { + + static class ImageCacheItem { + string _filename; + DrawBufRef _drawbuf; + bool _error; // flag to avoid loading of file if it has been failed once + bool _used; + this(string filename) { + _filename = filename; + } + @property ref DrawBufRef get() { + if (!_drawbuf.isNull || _error) { + _used = true; + return _drawbuf; + } + _drawbuf = loadImage(_filename); + _used = true; + if (_drawbuf.isNull) + _error = true; + return _drawbuf; + } + /// remove from memory, will cause reload on next access + void compact() { + if (!_drawbuf.isNull) + _drawbuf.clear(); + } + /// mark as not used + void checkpoint() { + _used = false; + } + /// cleanup if unused since last checkpoint + void cleanup() { + if (!_used) + compact(); + } + } + ImageCacheItem[string] _map; + + /// get and cache image + ref DrawBufRef get(string filename) { + if (filename in _map) { + return _map[filename].get; + } + ImageCacheItem item = new ImageCacheItem(filename); + _map[filename] = item; + return item.get; + } + // clear usage flags for all entries + void checkpoint() { + foreach (item; _map) + item.checkpoint(); + } + // removes entries not used after last call of checkpoint() or cleanup() + void cleanup() { + foreach (item; _map) + item.cleanup(); + } +} + +/// load and decode image from file to ColorDrawBuf, returns null if loading or decoding is failed +ColorDrawBuf loadImage(string filename) { + try { + std.stream.File f = new std.stream.File(filename); + scope(exit) { f.close(); } + return loadImage(f); + } catch (Exception e) { + return null; + } +} /// load and decode image from stream to ColorDrawBuf, returns null if loading or decoding is failed ColorDrawBuf loadImage(InputStream stream) { @@ -37,6 +107,7 @@ extern (C) void lvpng_read_func(png_structp png, png_bytep buf, png_size_t len) throw new ImageDecodingException("Error while reading PNG image"); } +/// load and decode PNG image ColorDrawBuf loadPngImage(InputStream stream) { png_structp png_ptr = null;