1 // Written in the D programming language. 2 3 /** 4 DLANGUI library. 5 6 This module contains opengl based drawing buffer implementation. 7 8 To enable OpenGL support, build with version(USE_OPENGL); 9 10 Synopsis: 11 12 ---- 13 import dlangui.graphics.gldrawbuf; 14 15 ---- 16 17 Copyright: Vadim Lopatin, 2014 18 License: $(WEB boost.org/LICENSE_1_0.txt, Boost License 1.0). 19 Authors: $(WEB coolreader.org, Vadim Lopatin) 20 */ 21 module dlangui.graphics.gldrawbuf; 22 23 version (USE_OPENGL) { 24 25 import dlangui.graphics.drawbuf; 26 import dlangui.core.logger; 27 private import dlangui.graphics.glsupport; 28 private import std.algorithm; 29 30 /// drawing buffer - image container which allows to perform some drawing operations 31 class GLDrawBuf : DrawBuf { 32 // width 33 protected int _dx; 34 // height 35 protected int _dy; 36 protected bool _framebuffer; // not yet supported 37 protected uint _framebufferId; // not yet supported 38 protected Scene _scene; 39 40 /// get current scene (exists only between beforeDrawing() and afterDrawing() calls 41 @property Scene scene() { return _scene; } 42 43 this(int dx, int dy, bool framebuffer = false) { 44 _dx = dx; 45 _dy = dy; 46 _framebuffer = framebuffer; 47 } 48 49 /// returns current width 50 @property override int width() { return _dx; } 51 /// returns current height 52 @property override int height() { return _dy; } 53 54 /// reserved for hardware-accelerated drawing - begins drawing batch 55 override void beforeDrawing() { 56 if (_scene !is null) { 57 _scene.reset(); 58 } 59 _scene = new Scene(); 60 } 61 62 /// reserved for hardware-accelerated drawing - ends drawing batch 63 override void afterDrawing() { 64 setOrthoProjection(_dx, _dy); 65 _scene.draw(); 66 flushGL(); 67 destroy(_scene); 68 _scene = null; 69 } 70 71 /// resize buffer 72 override void resize(int width, int height) { 73 _dx = width; 74 _dy = height; 75 } 76 77 /// fill the whole buffer with solid color (no clipping applied) 78 override void fill(uint color) { 79 assert(_scene !is null); 80 _scene.add(new SolidRectSceneItem(Rect(0, 0, _dx, _dy), color)); 81 } 82 /// fill rectangle with solid color (clipping is applied) 83 override void fillRect(Rect rc, uint color) { 84 assert(_scene !is null); 85 _scene.add(new SolidRectSceneItem(rc, color)); 86 } 87 /// draw 8bit alpha image - usually font glyph using specified color (clipping is applied) 88 override void drawGlyph(int x, int y, Glyph * glyph, uint color) { 89 assert(_scene !is null); 90 Rect dstrect = Rect(x,y, x + glyph.blackBoxX, y + glyph.blackBoxY); 91 Rect srcrect = Rect(0, 0, glyph.blackBoxX, glyph.blackBoxY); 92 //Log.v("GLDrawBuf.drawGlyph dst=", dstrect, " src=", srcrect, " color=", color); 93 if (applyClipping(dstrect, srcrect)) { 94 if (!glGlyphCache.get(glyph.id)) 95 glGlyphCache.put(glyph); 96 _scene.add(new GlyphSceneItem(glyph.id, dstrect, srcrect, color, null)); 97 } 98 } 99 /// draw source buffer rectangle contents to destination buffer 100 override void drawFragment(int x, int y, DrawBuf src, Rect srcrect) { 101 assert(_scene !is null); 102 Rect dstrect = Rect(x, y, x + srcrect.width, y + srcrect.height); 103 //Log.v("GLDrawBuf.frawFragment dst=", dstrect, " src=", srcrect); 104 if (applyClipping(dstrect, srcrect)) { 105 if (!glImageCache.get(src.id)) 106 glImageCache.put(src); 107 _scene.add(new TextureSceneItem(src.id, dstrect, srcrect, 0xFFFFFF, 0, null, 0)); 108 } 109 } 110 /// draw source buffer rectangle contents to destination buffer rectangle applying rescaling 111 override void drawRescaled(Rect dstrect, DrawBuf src, Rect srcrect) { 112 assert(_scene !is null); 113 //Log.v("GLDrawBuf.frawRescaled dst=", dstrect, " src=", srcrect); 114 if (applyClipping(dstrect, srcrect)) { 115 if (!glImageCache.get(src.id)) 116 glImageCache.put(src); 117 _scene.add(new TextureSceneItem(src.id, dstrect, srcrect, 0xFFFFFF, 0, null, 0)); 118 } 119 } 120 121 /// cleanup resources 122 override void clear() { 123 if (_framebuffer) { 124 // TODO: delete framebuffer 125 } 126 } 127 ~this() { clear(); } 128 } 129 130 /// base class for all drawing scene items. 131 class SceneItem { 132 abstract void draw(); 133 } 134 135 /// Drawing scene (operations sheduled for drawing) 136 class Scene { 137 private SceneItem[] _items; 138 this() { 139 activeSceneCount++; 140 } 141 ~this() { 142 activeSceneCount--; 143 } 144 /// add new scene item to scene 145 void add(SceneItem item) { 146 _items ~= item; 147 } 148 /// draws all scene items and removes them from list 149 void draw() { 150 foreach(SceneItem item; _items) 151 item.draw(); 152 reset(); 153 } 154 /// resets scene for new drawing - deletes all items 155 void reset() { 156 foreach(ref SceneItem item; _items) { 157 destroy(item); 158 item = null; 159 } 160 _items.length = 0; 161 } 162 } 163 164 private __gshared int activeSceneCount = 0; 165 bool hasActiveScene() { 166 return activeSceneCount > 0; 167 } 168 169 immutable int MIN_TEX_SIZE = 64; 170 immutable int MAX_TEX_SIZE = 4096; 171 private int nearestPOT(int n) { 172 for (int i = MIN_TEX_SIZE; i <= MAX_TEX_SIZE; i *= 2) { 173 if (n <= i) 174 return i; 175 } 176 return MIN_TEX_SIZE; 177 } 178 179 /// object deletion listener callback function type 180 void onObjectDestroyedCallback(uint pobject) { 181 glImageCache.onCachedObjectDeleted(pobject); 182 } 183 184 /// object deletion listener callback function type 185 void onGlyphDestroyedCallback(uint pobject) { 186 glGlyphCache.onCachedObjectDeleted(pobject); 187 } 188 189 private __gshared GLImageCache glImageCache; 190 191 private __gshared GLGlyphCache glGlyphCache; 192 193 shared static this() { 194 glImageCache = new GLImageCache(); 195 glGlyphCache = new GLGlyphCache(); 196 } 197 198 void LVGLClearImageCache() { 199 glImageCache.clear(); 200 glGlyphCache.clear(); 201 } 202 203 /// OpenGL texture cache for ColorDrawBuf objects 204 private class GLImageCache { 205 206 static class GLImageCacheItem { 207 private GLImageCachePage _page; 208 209 @property GLImageCachePage page() { return _page; } 210 211 uint _objectId; 212 Rect _rc; 213 bool _deleted; 214 215 this(GLImageCachePage page, uint objectId) { _page = page; _objectId = objectId; } 216 }; 217 218 static class GLImageCachePage { 219 private GLImageCache _cache; 220 private int _tdx; 221 private int _tdy; 222 private ColorDrawBuf _drawbuf; 223 private int _currentLine; 224 private int _nextLine; 225 private int _x; 226 private bool _closed; 227 private bool _needUpdateTexture; 228 private uint _textureId; 229 private int _itemCount; 230 231 this(GLImageCache cache, int dx, int dy) { 232 _cache = cache; 233 Log.v("created image cache page ", dx, "x", dy); 234 _tdx = nearestPOT(dx); 235 _tdy = nearestPOT(dy); 236 _itemCount = 0; 237 } 238 239 ~this() { 240 if (_drawbuf) { 241 destroy(_drawbuf); 242 _drawbuf = null; 243 } 244 if (_textureId != 0) { 245 deleteTexture(_textureId); 246 _textureId = 0; 247 } 248 } 249 250 void updateTexture() { 251 if (_drawbuf is null) 252 return; // no draw buffer!!! 253 if (_textureId == 0) { 254 //CRLog::debug("updateTexture - new texture"); 255 _textureId = genTexture(); 256 if (!_textureId) 257 return; 258 } 259 //CRLog::debug("updateTexture - setting image %dx%d", _drawbuf.width, _drawbuf.height); 260 uint * pixels = _drawbuf.scanLine(0); 261 if (!setTextureImage(_textureId, _drawbuf.width, _drawbuf.height, cast(ubyte*)pixels)) { 262 deleteTexture(_textureId); 263 _textureId = 0; 264 return; 265 } 266 _needUpdateTexture = false; 267 if (_closed) { 268 destroy(_drawbuf); 269 _drawbuf = null; 270 } 271 } 272 273 void convertPixelFormat(GLImageCacheItem item) { 274 Rect rc = item._rc; 275 for (int y = rc.top - 1; y <= rc.bottom; y++) { 276 uint * row = _drawbuf.scanLine(y); 277 for (int x = rc.left - 1; x <= rc.right; x++) { 278 uint cl = row[x]; 279 // invert A 280 cl ^= 0xFF000000; 281 // swap R and B 282 uint r = (cl & 0x00FF0000) >> 16; 283 uint b = (cl & 0x000000FF) << 16; 284 row[x] = (cl & 0xFF00FF00) | r | b; 285 } 286 } 287 } 288 289 GLImageCacheItem reserveSpace(uint objectId, int width, int height) { 290 GLImageCacheItem cacheItem = new GLImageCacheItem(this, objectId); 291 if (_closed) 292 return null; 293 294 // next line if necessary 295 if (_x + width + 2 > _tdx) { 296 // move to next line 297 _currentLine = _nextLine; 298 _x = 0; 299 } 300 // check if no room left for glyph height 301 if (_currentLine + height + 2 > _tdy) { 302 _closed = true; 303 return null; 304 } 305 cacheItem._rc = Rect(_x + 1, _currentLine + 1, _x + width + 1, _currentLine + height + 1); 306 if (height && width) { 307 if (_nextLine < _currentLine + height + 2) 308 _nextLine = _currentLine + height + 2; 309 if (!_drawbuf) { 310 _drawbuf = new ColorDrawBuf(_tdx, _tdy); 311 //_drawbuf.SetBackgroundColor(0x000000); 312 //_drawbuf.SetTextColor(0xFFFFFF); 313 _drawbuf.fill(0xFF000000); 314 } 315 _x += width + 1; 316 _needUpdateTexture = true; 317 } 318 _itemCount++; 319 return cacheItem; 320 } 321 int deleteItem(GLImageCacheItem item) { 322 _itemCount--; 323 return _itemCount; 324 } 325 GLImageCacheItem addItem(DrawBuf buf) { 326 GLImageCacheItem cacheItem = reserveSpace(buf.id, buf.width, buf.height); 327 if (cacheItem is null) 328 return null; 329 buf.onDestroyCallback = &onObjectDestroyedCallback; 330 _drawbuf.drawImage(cacheItem._rc.left, cacheItem._rc.top, buf); 331 convertPixelFormat(cacheItem); 332 _needUpdateTexture = true; 333 return cacheItem; 334 } 335 void drawItem(GLImageCacheItem item, Rect dstrc, Rect srcrc, uint color, uint options, Rect * clip, int rotationAngle) { 336 //CRLog::trace("drawing item at %d,%d %dx%d <= %d,%d %dx%d ", x, y, dx, dy, srcx, srcy, srcdx, srcdy); 337 if (_needUpdateTexture) 338 updateTexture(); 339 if (_textureId != 0) { 340 if (!isTexture(_textureId)) { 341 Log.e("Invalid texture ", _textureId); 342 return; 343 } 344 //rotationAngle = 0; 345 int rx = dstrc.middlex; 346 int ry = dstrc.middley; 347 if (rotationAngle) { 348 //rotationAngle = 0; 349 //setRotation(rx, ry, rotationAngle); 350 } 351 // convert coordinates to cached texture 352 srcrc.offset(item._rc.left, item._rc.top); 353 if (clip) { 354 int srcw = srcrc.width(); 355 int srch = srcrc.height(); 356 int dstw = dstrc.width(); 357 int dsth = dstrc.height(); 358 if (dstw) { 359 srcrc.left += clip.left * srcw / dstw; 360 srcrc.right -= clip.right * srcw / dstw; 361 } 362 if (dsth) { 363 srcrc.top += clip.top * srch / dsth; 364 srcrc.bottom -= clip.bottom * srch / dsth; 365 } 366 dstrc.left += clip.left; 367 dstrc.right -= clip.right; 368 dstrc.top += clip.top; 369 dstrc.bottom -= clip.bottom; 370 } 371 if (!dstrc.empty) 372 drawColorAndTextureRect(_textureId, _tdx, _tdy, srcrc, dstrc, color, srcrc.width() != dstrc.width() || srcrc.height() != dstrc.height()); 373 //drawColorAndTextureRect(vertices, texcoords, color, _textureId); 374 375 if (rotationAngle) { 376 // unset rotation 377 setRotation(rx, ry, 0); 378 // glMatrixMode(GL_PROJECTION); 379 // glPopMatrix(); 380 // checkError("pop matrix"); 381 } 382 383 } 384 } 385 void close() { 386 _closed = true; 387 if (_needUpdateTexture) 388 updateTexture(); 389 } 390 } 391 392 private GLImageCacheItem[uint] _map; 393 private GLImageCachePage[] _pages; 394 private GLImageCachePage _activePage; 395 private int tdx; 396 private int tdy; 397 398 private void removePage(GLImageCachePage page) { 399 if (_activePage == page) 400 _activePage = null; 401 for (int i = 0; i < _pages.length; i++) 402 if (_pages[i] == page) { 403 _pages.remove(i); 404 break; 405 } 406 destroy(page); 407 } 408 409 private void updateTextureSize() { 410 if (!tdx) { 411 // TODO 412 tdx = tdy = 1024; //getMaxTextureSize(); 413 if (tdx > 1024) 414 tdx = tdy = 1024; 415 } 416 } 417 418 this() { 419 } 420 ~this() { 421 clear(); 422 } 423 /// returns true if object exists in cache 424 bool get(uint obj) { 425 if (obj in _map) 426 return true; 427 return false; 428 } 429 /// put new object to cache 430 void put(DrawBuf img) { 431 updateTextureSize(); 432 GLImageCacheItem res = null; 433 if (img.width <= tdx / 3 && img.height < tdy / 3) { 434 // trying to reuse common page for small images 435 if (_activePage is null) { 436 _activePage = new GLImageCachePage(this, tdx, tdy); 437 _pages ~= _activePage; 438 } 439 res = _activePage.addItem(img); 440 if (!res) { 441 _activePage = new GLImageCachePage(this, tdx, tdy); 442 _pages ~= _activePage; 443 res = _activePage.addItem(img); 444 } 445 } else { 446 // use separate page for big image 447 GLImageCachePage page = new GLImageCachePage(this, img.width, img.height); 448 _pages ~= page; 449 res = page.addItem(img); 450 page.close(); 451 } 452 _map[img.id] = res; 453 } 454 /// clears cache 455 void clear() { 456 for (int i = 0; i < _pages.length; i++) { 457 destroy(_pages[i]); 458 _pages[i] = null; 459 } 460 _pages.clear(); 461 _map.clear(); 462 } 463 /// draw cached item 464 void drawItem(uint objectId, Rect dstrc, Rect srcrc, uint color, int options, Rect * clip, int rotationAngle) { 465 if (objectId in _map) { 466 GLImageCacheItem item = _map[objectId]; 467 item.page.drawItem(item, dstrc, srcrc, color, options, clip, rotationAngle); 468 } 469 } 470 /// handle cached object deletion, mark as deleted 471 void onCachedObjectDeleted(uint objectId) { 472 if (objectId in _map) { 473 GLImageCacheItem item = _map[objectId]; 474 if (hasActiveScene()) { 475 item._deleted = true; 476 } else { 477 int itemsLeft = item.page.deleteItem(item); 478 //CRLog::trace("itemsLeft = %d", itemsLeft); 479 if (itemsLeft <= 0) { 480 //CRLog::trace("removing page"); 481 removePage(item.page); 482 } 483 _map.remove(objectId); 484 delete item; 485 } 486 } 487 } 488 /// remove deleted items - remove page if contains only deleted items 489 void removeDeletedItems() { 490 uint[] list; 491 foreach (GLImageCacheItem item; _map) { 492 if (item._deleted) 493 list ~= item._objectId; 494 } 495 for (int i = 0 ; i < list.length; i++) { 496 onCachedObjectDeleted(list[i]); 497 } 498 } 499 }; 500 501 502 503 private class TextureSceneItem : SceneItem { 504 private uint objectId; 505 //CacheableObject * img; 506 private Rect dstrc; 507 private Rect srcrc; 508 private uint color; 509 private uint options; 510 private Rect * clip; 511 private int rotationAngle; 512 513 override void draw() { 514 if (glImageCache) 515 glImageCache.drawItem(objectId, dstrc, srcrc, color, options, clip, rotationAngle); 516 } 517 518 this(uint _objectId, Rect _dstrc, Rect _srcrc, uint _color, uint _options, Rect * _clip, int _rotationAngle) 519 { 520 objectId = _objectId; 521 dstrc = _dstrc; 522 srcrc = _srcrc; 523 color = _color; 524 options = _options; 525 clip = _clip; 526 rotationAngle = _rotationAngle; 527 } 528 529 ~this() { 530 } 531 }; 532 533 534 /// by some reason ALPHA texture does not work as expected 535 private immutable USE_RGBA_TEXTURE_FOR_GLYPHS = true; 536 537 private class GLGlyphCache { 538 539 static class GLGlyphCacheItem { 540 GLGlyphCachePage _page; 541 public: 542 @property GLGlyphCachePage page() { return _page; } 543 uint _objectId; 544 // image size 545 Rect _rc; 546 bool _deleted; 547 this(GLGlyphCachePage page, uint objectId) { _page = page; _objectId = objectId; } 548 }; 549 550 static class GLGlyphCachePage { 551 private GLGlyphCache _cache; 552 private int _tdx; 553 private int _tdy; 554 private GrayDrawBuf _drawbuf; 555 private int _currentLine; 556 private int _nextLine; 557 private int _x; 558 private bool _closed; 559 private bool _needUpdateTexture; 560 private uint _textureId; 561 private int _itemCount; 562 563 this(GLGlyphCache cache, int dx, int dy) { 564 _cache = cache; 565 Log.v("created image cache page ", dx, "x", dy); 566 _tdx = nearestPOT(dx); 567 _tdy = nearestPOT(dy); 568 _itemCount = 0; 569 } 570 571 ~this() { 572 if (_drawbuf) { 573 destroy(_drawbuf); 574 _drawbuf = null; 575 } 576 if (_textureId != 0) { 577 deleteTexture(_textureId); 578 _textureId = 0; 579 } 580 } 581 582 static if (USE_RGBA_TEXTURE_FOR_GLYPHS) { 583 uint[] _rgbaBuffer; 584 } 585 void updateTexture() { 586 if (_drawbuf is null) 587 return; // no draw buffer!!! 588 if (_textureId == 0) { 589 //CRLog::debug("updateTexture - new texture"); 590 _textureId = genTexture(); 591 if (!_textureId) 592 return; 593 } 594 //CRLog::debug("updateTexture - setting image %dx%d", _drawbuf.width, _drawbuf.height); 595 ubyte * pixels = _drawbuf.scanLine(0); 596 static if (USE_RGBA_TEXTURE_FOR_GLYPHS) { 597 int len = _drawbuf.width * _drawbuf.height; 598 _rgbaBuffer.length = len; 599 for (int i = 0; i < len; i++) 600 _rgbaBuffer[i] = ((cast(uint)pixels[i]) << 24) | 0x00FFFFFF; 601 if (!setTextureImage(_textureId, _drawbuf.width, _drawbuf.height, cast(ubyte*)_rgbaBuffer.ptr)) { 602 deleteTexture(_textureId); 603 _textureId = 0; 604 return; 605 } 606 } else { 607 if (!setTextureImageAlpha(_textureId, _drawbuf.width, _drawbuf.height, pixels)) { 608 deleteTexture(_textureId); 609 _textureId = 0; 610 return; 611 } 612 } 613 _needUpdateTexture = false; 614 if (_closed) { 615 destroy(_drawbuf); 616 _drawbuf = null; 617 } 618 } 619 GLGlyphCacheItem reserveSpace(uint objectId, int width, int height) { 620 GLGlyphCacheItem cacheItem = new GLGlyphCacheItem(this, objectId); 621 if (_closed) 622 return null; 623 624 // next line if necessary 625 if (_x + width + 2 > _tdx) { 626 // move to next line 627 _currentLine = _nextLine; 628 _x = 0; 629 } 630 // check if no room left for glyph height 631 if (_currentLine + height + 2 > _tdy) { 632 _closed = true; 633 return null; 634 } 635 cacheItem._rc = Rect(_x + 1, _currentLine + 1, _x + width + 1, _currentLine + height + 1); 636 if (height && width) { 637 if (_nextLine < _currentLine + height + 2) 638 _nextLine = _currentLine + height + 2; 639 if (!_drawbuf) { 640 _drawbuf = new GrayDrawBuf(_tdx, _tdy); 641 //_drawbuf.SetBackgroundColor(0x000000); 642 //_drawbuf.SetTextColor(0xFFFFFF); 643 _drawbuf.fill(0x00000000); 644 } 645 _x += width + 1; 646 _needUpdateTexture = true; 647 } 648 _itemCount++; 649 return cacheItem; 650 } 651 int deleteItem(GLGlyphCacheItem item) { 652 _itemCount--; 653 return _itemCount; 654 } 655 GLGlyphCacheItem addItem(Glyph * glyph) { 656 GLGlyphCacheItem cacheItem = reserveSpace(glyph.id, glyph.blackBoxX, glyph.blackBoxY); 657 if (cacheItem is null) 658 return null; 659 _drawbuf.drawGlyph(cacheItem._rc.left, cacheItem._rc.top, glyph, 0xFFFFFF); 660 _needUpdateTexture = true; 661 return cacheItem; 662 } 663 void drawItem(GLGlyphCacheItem item, Rect dstrc, Rect srcrc, uint color, Rect * clip) { 664 //CRLog::trace("drawing item at %d,%d %dx%d <= %d,%d %dx%d ", x, y, dx, dy, srcx, srcy, srcdx, srcdy); 665 if (_needUpdateTexture) 666 updateTexture(); 667 if (_textureId != 0) { 668 if (!isTexture(_textureId)) { 669 Log.e("Invalid texture ", _textureId); 670 return; 671 } 672 // convert coordinates to cached texture 673 srcrc.offset(item._rc.left, item._rc.top); 674 if (clip) { 675 int srcw = srcrc.width(); 676 int srch = srcrc.height(); 677 int dstw = dstrc.width(); 678 int dsth = dstrc.height(); 679 if (dstw) { 680 srcrc.left += clip.left * srcw / dstw; 681 srcrc.right -= clip.right * srcw / dstw; 682 } 683 if (dsth) { 684 srcrc.top += clip.top * srch / dsth; 685 srcrc.bottom -= clip.bottom * srch / dsth; 686 } 687 dstrc.left += clip.left; 688 dstrc.right -= clip.right; 689 dstrc.top += clip.top; 690 dstrc.bottom -= clip.bottom; 691 } 692 if (!dstrc.empty) { 693 //Log.d("drawing glyph with color ", color); 694 drawColorAndTextureRect(_textureId, _tdx, _tdy, srcrc, dstrc, color, false); 695 } 696 697 } 698 } 699 void close() { 700 _closed = true; 701 if (_needUpdateTexture) 702 updateTexture(); 703 static if (USE_RGBA_TEXTURE_FOR_GLYPHS) { 704 _rgbaBuffer = null; 705 } 706 } 707 } 708 709 GLGlyphCacheItem[uint] _map; 710 GLGlyphCachePage[] _pages; 711 GLGlyphCachePage _activePage; 712 int tdx; 713 int tdy; 714 void removePage(GLGlyphCachePage page) { 715 if (_activePage == page) 716 _activePage = null; 717 for (int i = 0; i < _pages.length; i++) 718 if (_pages[i] == page) { 719 _pages.remove(i); 720 break; 721 } 722 destroy(page); 723 } 724 private void updateTextureSize() { 725 if (!tdx) { 726 // TODO 727 tdx = tdy = 1024; //getMaxTextureSize(); 728 if (tdx > 1024) 729 tdx = tdy = 1024; 730 } 731 } 732 733 this() { 734 } 735 ~this() { 736 clear(); 737 } 738 /// check if item is in cache 739 bool get(uint obj) { 740 if (obj in _map) 741 return true; 742 return false; 743 } 744 /// put new item to cache 745 void put(Glyph * glyph) { 746 updateTextureSize(); 747 GLGlyphCacheItem res = null; 748 if (_activePage is null) { 749 _activePage = new GLGlyphCachePage(this, tdx, tdy); 750 _pages ~= _activePage; 751 } 752 res = _activePage.addItem(glyph); 753 if (!res) { 754 _activePage = new GLGlyphCachePage(this, tdx, tdy); 755 _pages ~= _activePage; 756 res = _activePage.addItem(glyph); 757 } 758 _map[glyph.id] = res; 759 } 760 void clear() { 761 for (int i = 0; i < _pages.length; i++) { 762 destroy(_pages[i]); 763 _pages[i] = null; 764 } 765 _pages.clear(); 766 _map.clear(); 767 } 768 /// draw cached item 769 void drawItem(uint objectId, Rect dstrc, Rect srcrc, uint color, Rect * clip) { 770 GLGlyphCacheItem * item = objectId in _map; 771 if (item) 772 item.page.drawItem(*item, dstrc, srcrc, color, clip); 773 } 774 /// handle cached object deletion, mark as deleted 775 void onCachedObjectDeleted(uint objectId) { 776 if (objectId in _map) { 777 GLGlyphCacheItem item = _map[objectId]; 778 if (hasActiveScene()) { 779 item._deleted = true; 780 } else { 781 int itemsLeft = item.page.deleteItem(item); 782 //CRLog::trace("itemsLeft = %d", itemsLeft); 783 if (itemsLeft <= 0) { 784 //CRLog::trace("removing page"); 785 removePage(item.page); 786 } 787 _map.remove(objectId); 788 delete item; 789 } 790 } 791 } 792 /// remove deleted items - remove page if contains only deleted items 793 void removeDeletedItems() { 794 uint[] list; 795 foreach (GLGlyphCacheItem item; _map) { 796 if (item._deleted) 797 list ~= item._objectId; 798 } 799 for (int i = 0 ; i < list.length; i++) { 800 onCachedObjectDeleted(list[i]); 801 } 802 } 803 }; 804 805 806 807 808 809 810 811 class SolidRectSceneItem : SceneItem { 812 Rect _rc; 813 uint _color; 814 this(Rect rc, uint color) { 815 _rc = rc; 816 _color = color; 817 } 818 override void draw() { 819 drawSolidFillRect(_rc, _color, _color, _color, _color); 820 } 821 } 822 823 private class GlyphSceneItem : SceneItem { 824 uint objectId; 825 Rect dstrc; 826 Rect srcrc; 827 uint color; 828 Rect * clip; 829 public: 830 override void draw() { 831 if (glGlyphCache) 832 glGlyphCache.drawItem(objectId, dstrc, srcrc, color, clip); 833 } 834 this(uint _objectId, Rect _dstrc, Rect _srcrc, uint _color, Rect * _clip) 835 { 836 objectId = _objectId; 837 dstrc = _dstrc; 838 srcrc = _srcrc; 839 color = _color; 840 clip = _clip; 841 } 842 ~this() { 843 } 844 } 845 846 847 }