implement OpenGl glyph cache

This commit is contained in:
Vadim Lopatin 2014-03-11 19:48:59 +04:00
parent 72f5d394ea
commit 6786e29b17
1 changed files with 305 additions and 36 deletions

View File

@ -90,17 +90,6 @@ class GLDrawBuf : DrawBuf {
}
private class GLImageCacheItem {
GLImageCachePage _page;
public:
@property GLImageCachePage page() { return _page; }
uint _objectId;
// image size
Rect _rc;
bool _deleted;
this(GLImageCachePage page, uint objectId) { _page = page; _objectId = objectId; }
};
class SceneItem {
abstract void draw();
}
@ -141,6 +130,45 @@ bool hasActiveScene() {
return activeSceneCount > 0;
}
immutable int MIN_TEX_SIZE = 64;
immutable int MAX_TEX_SIZE = 4096;
int nearestPOT(int n) {
for (int i = MIN_TEX_SIZE; i <= MAX_TEX_SIZE; i *= 2) {
if (n <= i)
return i;
}
return MIN_TEX_SIZE;
}
/// object deletion listener callback function type
void onObjectDestroyedCallback(uint pobject) {
glImageCache.onCachedObjectDeleted(pobject);
}
private __gshared GLImageCache glImageCache;
shared static this() {
glImageCache = new GLImageCache();
}
void LVGLClearImageCache() {
glImageCache.clear();
}
private class GLImageCacheItem {
GLImageCachePage _page;
public:
@property GLImageCachePage page() { return _page; }
uint _objectId;
// image size
Rect _rc;
bool _deleted;
this(GLImageCachePage page, uint objectId) { _page = page; _objectId = objectId; }
};
private class GLImageCache {
GLImageCacheItem[uint] _map;
GLImageCachePage[] _pages;
@ -247,31 +275,6 @@ public:
}
};
immutable int MIN_TEX_SIZE = 64;
immutable int MAX_TEX_SIZE = 4096;
int nearestPOT(int n) {
for (int i = MIN_TEX_SIZE; i <= MAX_TEX_SIZE; i *= 2) {
if (n <= i)
return i;
}
return MIN_TEX_SIZE;
}
/// object deletion listener callback function type
void onObjectDestroyedCallback(uint pobject) {
glImageCache.onCachedObjectDeleted(pobject);
}
private __gshared GLImageCache glImageCache;
shared static this() {
glImageCache = new GLImageCache();
}
void LVGLClearImageCache() {
glImageCache.clear();
}
private class GLImageCachePage {
GLImageCache _cache;
int _tdx;
@ -471,3 +474,269 @@ public:
}
};
private class GLGlyphCacheItem {
GLGlyphCachePage _page;
public:
@property GLGlyphCachePage page() { return _page; }
uint _objectId;
// image size
Rect _rc;
bool _deleted;
this(GLGlyphCachePage page, uint objectId) { _page = page; _objectId = objectId; }
};
private class GLGlyphCache {
GLGlyphCacheItem[uint] _map;
GLGlyphCachePage[] _pages;
GLGlyphCachePage _activePage;
int tdx;
int tdy;
void removePage(GLGlyphCachePage page) {
if (_activePage == page)
_activePage = null;
for (int i = 0; i < _pages.length; i++)
if (_pages[i] == page) {
_pages.remove(i);
break;
}
destroy(page);
}
void updateTextureSize() {
if (!tdx) {
// TODO
tdx = tdy = 1024; //getMaxTextureSize();
if (tdx > 1024)
tdx = tdy = 1024;
}
}
public:
this() {
}
~this() {
clear();
}
GLGlyphCacheItem get(uint obj) {
if (obj in _map)
return _map[obj];
return null;
}
GLGlyphCacheItem set(Glyph * glyph) {
updateTextureSize();
GLGlyphCacheItem res = null;
if (_activePage is null) {
_activePage = new GLGlyphCachePage(this, tdx, tdy);
_pages ~= _activePage;
}
res = _activePage.addItem(glyph);
if (!res) {
_activePage = new GLGlyphCachePage(this, tdx, tdy);
_pages ~= _activePage;
res = _activePage.addItem(glyph);
}
_map[glyph.id] = res;
return res;
}
void clear() {
for (int i = 0; i < _pages.length; i++) {
destroy(_pages[i]);
_pages[i] = null;
}
_pages.clear();
_map.clear();
}
/// draw cached item
void drawItem(uint objectId, Rect dstrc, Rect srcrc, uint color, int options, Rect * clip, int rotationAngle) {
if (objectId in _map) {
GLGlyphCacheItem item = _map[objectId];
item.page.drawItem(item, dstrc, srcrc, color, options, clip, rotationAngle);
}
}
/// handle cached object deletion, mark as deleted
void onCachedObjectDeleted(uint objectId) {
if (objectId in _map) {
GLGlyphCacheItem item = _map[objectId];
if (hasActiveScene()) {
item._deleted = true;
} else {
int itemsLeft = item.page.deleteItem(item);
//CRLog::trace("itemsLeft = %d", itemsLeft);
if (itemsLeft <= 0) {
//CRLog::trace("removing page");
removePage(item.page);
}
_map.remove(objectId);
delete item;
}
}
}
/// remove deleted items - remove page if contains only deleted items
void removeDeletedItems() {
uint[] list;
foreach (GLGlyphCacheItem item; _map) {
if (item._deleted)
list ~= item._objectId;
}
for (int i = 0 ; i < list.length; i++) {
onCachedObjectDeleted(list[i]);
}
}
};
private class GLGlyphCachePage {
GLGlyphCache _cache;
int _tdx;
int _tdy;
GrayDrawBuf _drawbuf;
int _currentLine;
int _nextLine;
int _x;
bool _closed;
bool _needUpdateTexture;
uint _textureId;
int _itemCount;
public:
this(GLGlyphCache cache, int dx, int dy) {
_cache = cache;
Log.v("created image cache page ", dx, "x", dy);
_tdx = nearestPOT(dx);
_tdy = nearestPOT(dy);
_itemCount = 0;
}
~this() {
if (_drawbuf) {
destroy(_drawbuf);
_drawbuf = null;
}
if (_textureId != 0) {
deleteTexture(_textureId);
_textureId = 0;
}
}
void updateTexture() {
if (_drawbuf is null)
return; // no draw buffer!!!
if (_textureId == 0) {
//CRLog::debug("updateTexture - new texture");
_textureId = genTexture();
if (!_textureId)
return;
}
//CRLog::debug("updateTexture - setting image %dx%d", _drawbuf.width, _drawbuf.height);
ubyte * pixels = _drawbuf.scanLine(0);
if (!setTextureImageAlpha(_textureId, _drawbuf.width, _drawbuf.height, pixels)) {
deleteTexture(_textureId);
_textureId = 0;
return;
}
_needUpdateTexture = false;
if (_closed) {
destroy(_drawbuf);
_drawbuf = null;
}
}
GLGlyphCacheItem reserveSpace(uint objectId, int width, int height) {
GLGlyphCacheItem cacheItem = new GLGlyphCacheItem(this, objectId);
if (_closed)
return null;
// next line if necessary
if (_x + width + 2 > _tdx) {
// move to next line
_currentLine = _nextLine;
_x = 0;
}
// check if no room left for glyph height
if (_currentLine + height + 2 > _tdy) {
_closed = true;
return null;
}
cacheItem._rc = Rect(_x + 1, _currentLine + 1, _x + width + 1, _currentLine + height + 1);
if (height && width) {
if (_nextLine < _currentLine + height + 2)
_nextLine = _currentLine + height + 2;
if (!_drawbuf) {
_drawbuf = new GrayDrawBuf(_tdx, _tdy);
//_drawbuf.SetBackgroundColor(0x000000);
//_drawbuf.SetTextColor(0xFFFFFF);
_drawbuf.fill(0x00000000);
}
_x += width + 1;
_needUpdateTexture = true;
}
_itemCount++;
return cacheItem;
}
int deleteItem(GLGlyphCacheItem item) {
_itemCount--;
return _itemCount;
}
GLGlyphCacheItem addItem(Glyph * glyph) {
GLGlyphCacheItem cacheItem = reserveSpace(glyph.id, glyph.blackBoxX, glyph.blackBoxY);
if (cacheItem is null)
return null;
_drawbuf.drawGlyph(cacheItem._rc.left, cacheItem._rc.top, glyph, 0xFFFFFF);
_needUpdateTexture = true;
return cacheItem;
}
void drawItem(GLGlyphCacheItem item, Rect dstrc, Rect srcrc, uint color, uint options, Rect * clip, int rotationAngle) {
//CRLog::trace("drawing item at %d,%d %dx%d <= %d,%d %dx%d ", x, y, dx, dy, srcx, srcy, srcdx, srcdy);
if (_needUpdateTexture)
updateTexture();
if (_textureId != 0) {
if (!isTexture(_textureId)) {
Log.e("Invalid texture ", _textureId);
return;
}
//rotationAngle = 0;
int rx = dstrc.middlex;
int ry = dstrc.middley;
if (rotationAngle) {
//rotationAngle = 0;
//setRotation(rx, ry, rotationAngle);
}
// convert coordinates to cached texture
srcrc.offset(item._rc.left, item._rc.top);
if (clip) {
int srcw = srcrc.width();
int srch = srcrc.height();
int dstw = dstrc.width();
int dsth = dstrc.height();
if (dstw) {
srcrc.left += clip.left * srcw / dstw;
srcrc.right -= clip.right * srcw / dstw;
}
if (dsth) {
srcrc.top += clip.top * srch / dsth;
srcrc.bottom -= clip.bottom * srch / dsth;
}
dstrc.left += clip.left;
dstrc.right -= clip.right;
dstrc.top += clip.top;
dstrc.bottom -= clip.bottom;
}
if (!dstrc.empty)
drawColorAndTextureRect(_textureId, _tdx, _tdy, srcrc, dstrc, color, srcrc.width() != dstrc.width() || srcrc.height() != dstrc.height());
//drawColorAndTextureRect(vertices, texcoords, color, _textureId);
if (rotationAngle) {
// unset rotation
setRotation(rx, ry, 0);
// glMatrixMode(GL_PROJECTION);
// glPopMatrix();
// checkError("pop matrix");
}
}
}
void close() {
_closed = true;
if (_needUpdateTexture)
updateTexture();
}
};