diff --git a/src/dlangui/graphics/gldrawbuf.d b/src/dlangui/graphics/gldrawbuf.d index 6c0e2f70..bb8e1ffe 100644 --- a/src/dlangui/graphics/gldrawbuf.d +++ b/src/dlangui/graphics/gldrawbuf.d @@ -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(); + } +};