image cache:

This commit is contained in:
Vadim Lopatin 2014-03-05 11:36:23 +04:00
parent dccd5d8f88
commit a41841b2bc
3 changed files with 190 additions and 108 deletions

View File

@ -18,9 +18,13 @@ uint blendARGB(uint dst, uint src, uint alpha) {
class DrawBuf : RefCountedObject { class DrawBuf : RefCountedObject {
protected Rect _clipRect; protected Rect _clipRect;
/// returns current width
@property int width() { return 0; } @property int width() { return 0; }
/// returns current height
@property int height() { return 0; } @property int height() { return 0; }
/// returns clipping rectangle, when clipRect.isEmpty == true -- means no clipping.
@property ref Rect clipRect() { return _clipRect; } @property ref Rect clipRect() { return _clipRect; }
/// sets new clipping rectangle, when clipRect.isEmpty == true -- means no clipping.
@property void clipRect(const ref Rect rect) { @property void clipRect(const ref Rect rect) {
_clipRect = rect; _clipRect = rect;
_clipRect.intersect(Rect(0, 0, width, height)); _clipRect.intersect(Rect(0, 0, width, height));
@ -40,6 +44,9 @@ class DrawBuf : RefCountedObject {
} }
void beforeDrawing() { } void beforeDrawing() { }
void afterDrawing() { } 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; } uint * scanLine(int y) { return null; }
abstract void resize(int width, int height); abstract void resize(int width, int height);
abstract void fill(uint color); abstract void fill(uint color);
@ -52,9 +59,13 @@ class DrawBuf : RefCountedObject {
~this() { clear(); } ~this() { clear(); }
} }
alias DrawBufRef = Ref!DrawBuf;
class ColorDrawBufBase : DrawBuf { class ColorDrawBufBase : DrawBuf {
int _dx; int _dx;
int _dy; int _dy;
/// returns buffer bits per pixel
override @property int bpp() { return 32; }
@property override int width() { return _dx; } @property override int width() { return _dx; }
@property override int height() { return _dy; } @property override int height() { return _dy; }
override void fillRect(int left, int top, int right, int bottom, uint color) { override void fillRect(int left, int top, int right, int bottom, uint color) {

View File

@ -1,17 +1,17 @@
module dlangui.graphics.fonts; module dlangui.graphics.fonts;
public import dlangui.graphics.drawbuf; public import dlangui.graphics.drawbuf;
public import dlangui.core.types; public import dlangui.core.types;
public import dlangui.core.logger; public import dlangui.core.logger;
import std.algorithm; import std.algorithm;
enum FontFamily : int { enum FontFamily : int {
SansSerif, SansSerif,
Serif, Serif,
Fantasy, Fantasy,
Cursive, Cursive,
MonoSpace MonoSpace
} }
struct Glyph struct Glyph
{ {
ubyte blackBoxX; ///< 0: width of glyph ubyte blackBoxX; ///< 0: width of glyph
@ -82,101 +82,101 @@ struct GlyphCache
clear(); clear();
} }
} }
class Font : RefCountedObject { class Font : RefCountedObject {
abstract @property int size(); abstract @property int size();
abstract @property int height(); abstract @property int height();
abstract @property int weight(); abstract @property int weight();
abstract @property int baseline(); abstract @property int baseline();
abstract @property bool italic(); abstract @property bool italic();
abstract @property string face(); abstract @property string face();
abstract @property FontFamily family(); abstract @property FontFamily family();
abstract @property bool isNull(); abstract @property bool isNull();
// measure text string, return accumulated widths[] (distance to end of n-th character), returns number of measured chars. // 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); abstract int measureText(const dchar[] text, ref int[] widths, int maxWidth);
// draw text string to buffer // draw text string to buffer
abstract void drawText(DrawBuf buf, int x, int y, const dchar[] text, uint color); abstract void drawText(DrawBuf buf, int x, int y, const dchar[] text, uint color);
abstract Glyph * getCharGlyph(dchar ch); abstract Glyph * getCharGlyph(dchar ch);
// clear usage flags for all entries // clear usage flags for all entries
abstract void checkpoint(); abstract void checkpoint();
// removes entries not used after last call of checkpoint() or cleanup() // removes entries not used after last call of checkpoint() or cleanup()
abstract void cleanup(); abstract void cleanup();
void clear() {} void clear() {}
~this() { clear(); } ~this() { clear(); }
} }
alias FontRef = Ref!Font; alias FontRef = Ref!Font;
struct FontList { struct FontList {
FontRef[] _list; FontRef[] _list;
uint _len; uint _len;
~this() { ~this() {
for (uint i = 0; i < _len; i++) { for (uint i = 0; i < _len; i++) {
_list[i].clear(); _list[i].clear();
} }
} }
// returns item by index // returns item by index
ref FontRef get(int index) { ref FontRef get(int index) {
return _list[index]; return _list[index];
} }
// returns index of found item, -1 if not found // returns index of found item, -1 if not found
int find(int size, int weight, bool italic, FontFamily family, string face) { int find(int size, int weight, bool italic, FontFamily family, string face) {
for (int i = 0; i < _len; i++) { for (int i = 0; i < _len; i++) {
Font item = _list[i].get; Font item = _list[i].get;
if (item.family != family) if (item.family != family)
continue; continue;
if (item.size != size) if (item.size != size)
continue; continue;
if (item.italic != italic || item.weight != weight) if (item.italic != italic || item.weight != weight)
continue; continue;
if (!equal(item.face, face)) if (!equal(item.face, face))
continue; continue;
return i; return i;
} }
return -1; return -1;
} }
ref FontRef add(Font item) { ref FontRef add(Font item) {
Log.d("FontList.add() enter"); Log.d("FontList.add() enter");
if (_len >= _list.length) { if (_len >= _list.length) {
_list.length = _len < 16 ? 16 : _list.length * 2; _list.length = _len < 16 ? 16 : _list.length * 2;
} }
_list[_len++] = item; _list[_len++] = item;
Log.d("FontList.add() exit"); Log.d("FontList.add() exit");
return _list[_len - 1]; return _list[_len - 1];
} }
// remove unused items - with reference == 1 // remove unused items - with reference == 1
void cleanup() { void cleanup() {
for (int i = 0; i < _len; i++) for (int i = 0; i < _len; i++)
if (_list[i].refCount <= 1) if (_list[i].refCount <= 1)
_list[i].clear(); _list[i].clear();
int dst = 0; int dst = 0;
for (int i = 0; i < _len; i++) { for (int i = 0; i < _len; i++) {
if (!_list[i].isNull) if (!_list[i].isNull)
if (i != dst) if (i != dst)
_list[dst++] = _list[i]; _list[dst++] = _list[i];
} }
_len = dst; _len = dst;
for (int i = 0; i < _len; i++) for (int i = 0; i < _len; i++)
_list[i].cleanup(); _list[i].cleanup();
} }
void checkpoint() { void checkpoint() {
for (int i = 0; i < _len; i++) for (int i = 0; i < _len; i++)
_list[i].checkpoint(); _list[i].checkpoint();
} }
} }
class FontManager { class FontManager {
static __gshared FontManager _instance; static __gshared FontManager _instance;
static @property void instance(FontManager manager) { static @property void instance(FontManager manager) {
_instance = manager; _instance = manager;
} }
static @property FontManager instance() { static @property FontManager instance() {
return _instance; return _instance;
} }
abstract ref FontRef getFont(int size, int weight, bool italic, FontFamily family, string face); abstract ref FontRef getFont(int size, int weight, bool italic, FontFamily family, string face);
// clear usage flags for all entries // clear usage flags for all entries
abstract void checkpoint(); abstract void checkpoint();
@ -184,5 +184,5 @@ class FontManager {
// removes entries not used after last call of checkpoint() or cleanup() // removes entries not used after last call of checkpoint() or cleanup()
abstract void cleanup(); abstract void cleanup();
~this() {} ~this() {}
} }

View File

@ -3,7 +3,77 @@ module dlangui.graphics.images;
import dlangui.graphics.drawbuf; import dlangui.graphics.drawbuf;
import std.stream; import std.stream;
import libpng.png; 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 /// load and decode image from stream to ColorDrawBuf, returns null if loading or decoding is failed
ColorDrawBuf loadImage(InputStream stream) { 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"); throw new ImageDecodingException("Error while reading PNG image");
} }
/// load and decode PNG image
ColorDrawBuf loadPngImage(InputStream stream) ColorDrawBuf loadPngImage(InputStream stream)
{ {
png_structp png_ptr = null; png_structp png_ptr = null;