mirror of https://github.com/buggins/dlangui.git
implement OpenGl glyph cache
This commit is contained in:
parent
72f5d394ea
commit
6786e29b17
|
|
@ -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();
|
||||
}
|
||||
};
|
||||
|
|
|
|||
Loading…
Reference in New Issue