Skip to content

Commit 99470e5

Browse files
committed
Recycle temporary glyph buffers in FontFreeType.
1 parent cf4de9b commit 99470e5

File tree

3 files changed

+118
-19
lines changed

3 files changed

+118
-19
lines changed

core/2d/FontAtlas.cpp

Lines changed: 12 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -385,17 +385,18 @@ bool FontAtlas::prepareLetterDefinitions(const std::u32string& utf32Text)
385385
if (charRenderer)
386386
{
387387
unsigned int glyphIndex = fallbackFaceInfo->currentGlyphIndex;
388-
bitmap =
389-
charRenderer->getGlyphBitmapByIndex(glyphIndex, bitmapWidth, bitmapHeight, tempRect, tempDef.xAdvance);
388+
bitmap = charRenderer->getGlyphBitmapByIndex(glyphIndex, bitmapWidth, bitmapHeight, tempRect,
389+
tempDef.xAdvance);
390390
_missingGlyphFallbackFonts.emplace(charCode, std::make_pair(charRenderer, glyphIndex));
391391
}
392392
}
393393
}
394394
else
395395
{ // found fallback font for missing charas, getGlyphBitmap without fallback
396-
charRenderer = missingIt->second.first;
396+
charRenderer = missingIt->second.first;
397397
unsigned int glyphIndex = missingIt->second.second;
398-
bitmap = charRenderer->getGlyphBitmapByIndex(glyphIndex, bitmapWidth, bitmapHeight, tempRect, tempDef.xAdvance);
398+
bitmap =
399+
charRenderer->getGlyphBitmapByIndex(glyphIndex, bitmapWidth, bitmapHeight, tempRect, tempDef.xAdvance);
399400
}
400401
if (bitmap && bitmapWidth > 0 && bitmapHeight > 0)
401402
{
@@ -419,11 +420,11 @@ bool FontAtlas::prepareLetterDefinitions(const std::u32string& utf32Text)
419420
addNewPage();
420421
}
421422
}
422-
glyphHeight = static_cast<int>(bitmapHeight) + _letterPadding + _letterEdgeExtend;
423+
glyphHeight = static_cast<int>(bitmapHeight) + _letterPadding + _letterEdgeExtend;
423424
_currLineHeight = std::max(glyphHeight, _currLineHeight);
424425
charRenderer->renderCharAt(_currentPageData, (int)_currentPageOrigX + adjustForExtend,
425426
(int)_currentPageOrigY + adjustForExtend, bitmap, bitmapWidth, bitmapHeight,
426-
_width, _height);
427+
_width, _height);
427428

428429
tempDef.U = _currentPageOrigX;
429430
tempDef.V = _currentPageOrigY;
@@ -438,7 +439,8 @@ bool FontAtlas::prepareLetterDefinitions(const std::u32string& utf32Text)
438439
}
439440
else
440441
{
441-
delete[] bitmap;
442+
if (bitmap)
443+
charRenderer->releaseBuffer(bitmap);
442444

443445
tempDef.validDefinition = !!tempDef.xAdvance;
444446
tempDef.width = 0;
@@ -463,8 +465,8 @@ bool FontAtlas::prepareLetterDefinitions(const std::u32string& utf32Text)
463465
void FontAtlas::updateTextureContent(backend::PixelFormat format, int startY)
464466
{
465467
auto data = _currentPageData + (_width * (int)startY << _strideShift);
466-
_atlasTextures[_currentPage]->updateWithSubData(data, 0, startY, _width,
467-
(std::min)((int)_currentPageOrigY - startY + _currLineHeight, _height));
468+
_atlasTextures[_currentPage]->updateWithSubData(
469+
data, 0, startY, _width, (std::min)((int)_currentPageOrigY - startY + _currLineHeight, _height));
468470
}
469471

470472
void FontAtlas::addNewPage()
@@ -549,4 +551,4 @@ void FontAtlas::setAntiAliasTexParameters()
549551
}
550552
}
551553

552-
}
554+
} // namespace ax

core/2d/FontFreeType.cpp

Lines changed: 84 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -210,6 +210,12 @@ FontFreeType::FontFreeType(bool distanceFieldEnabled /* = false */, float outlin
210210

211211
FontFreeType::~FontFreeType()
212212
{
213+
AX_ASSERT(_usedBuffers.empty());
214+
215+
for (const BufferPool& p : _availableBuffers)
216+
for (uint8_t* b : p.buffers)
217+
delete[] b;
218+
213219
if (_FTInitialized)
214220
{
215221
if (_stroker)
@@ -503,15 +509,15 @@ unsigned char* FontFreeType::getGlyphBitmapByIndex(unsigned int glyphIndex,
503509

504510
if (_outlineSize > 0 && outWidth > 0 && outHeight > 0)
505511
{
506-
auto copyBitmap = new unsigned char[outWidth * outHeight];
507-
memcpy(copyBitmap, ret, outWidth * outHeight * sizeof(unsigned char));
512+
uint8_t* const copyBitmap = acquireBuffer(outWidth * outHeight);
513+
memcpy(copyBitmap, ret, outWidth * outHeight * sizeof(uint8_t));
508514

509515
FT_BBox bbox;
510516
auto outlineBitmap = getGlyphBitmapWithOutline(glyphIndex, bbox);
511517
if (outlineBitmap == nullptr)
512518
{
513519
ret = nullptr;
514-
delete[] copyBitmap;
520+
releaseBuffer(copyBitmap);
515521
break;
516522
}
517523

@@ -540,7 +546,7 @@ unsigned char* FontFreeType::getGlyphBitmapByIndex(unsigned int glyphIndex,
540546
{
541547
FT_Pos index, index2;
542548
auto imageSize = blendWidth * blendHeight * 2;
543-
blendImage = new unsigned char[imageSize];
549+
blendImage = acquireBuffer(imageSize);
544550
memset(blendImage, 0, imageSize);
545551

546552
auto px = outlineMinX - blendImageMinX;
@@ -573,8 +579,8 @@ unsigned char* FontFreeType::getGlyphBitmapByIndex(unsigned int glyphIndex,
573579
outWidth = static_cast<int>(blendWidth);
574580
outHeight = static_cast<int>(blendHeight);
575581

576-
delete[] outlineBitmap;
577-
delete[] copyBitmap;
582+
releaseBuffer(outlineBitmap);
583+
releaseBuffer(copyBitmap);
578584
ret = blendImage;
579585
}
580586

@@ -588,6 +594,24 @@ unsigned char* FontFreeType::getGlyphBitmapByIndex(unsigned int glyphIndex,
588594
return nullptr;
589595
}
590596

597+
/**
598+
* Put the given buffer back into the pool of buffers.
599+
*/
600+
void FontFreeType::releaseBuffer(uint8_t* buffer)
601+
{
602+
const UsedBuffersMap::iterator it = _usedBuffers.find(buffer);
603+
AX_ASSERT(it != _usedBuffers.end());
604+
605+
for (BufferPool& p : _availableBuffers)
606+
if (p.capacity == it->second)
607+
{
608+
p.buffers.emplace_back(buffer);
609+
break;
610+
}
611+
612+
_usedBuffers.erase(it);
613+
}
614+
591615
unsigned char* FontFreeType::getGlyphBitmapWithOutline(unsigned int glyphIndex, FT_BBox& bbox)
592616
{
593617
unsigned char* ret = nullptr;
@@ -607,7 +631,7 @@ unsigned char* FontFreeType::getGlyphBitmapWithOutline(unsigned int glyphIndex,
607631
int32_t rows = static_cast<int32_t>((bbox.yMax - bbox.yMin) >> 6);
608632

609633
FT_Bitmap bmp;
610-
bmp.buffer = new unsigned char[width * rows];
634+
bmp.buffer = acquireBuffer(width * rows);
611635
memset(bmp.buffer, 0, width * rows);
612636
bmp.width = (int)width;
613637
bmp.rows = (int)rows;
@@ -653,7 +677,7 @@ void FontFreeType::renderCharAt(unsigned char* dest,
653677
memcpy(dest + (iX + (iY * atlasWidth)) * 2, bitmap + bitmap_y * 2, bitmapWidth * 2);
654678
++iY;
655679
}
656-
delete[] bitmap;
680+
releaseBuffer(bitmap);
657681
}
658682
else
659683
{
@@ -710,4 +734,56 @@ void FontFreeType::releaseFont(std::string_view fontName)
710734
}
711735
}
712736

737+
/**
738+
* Find a buffer with a capacity larger or equal to the given size. If there is
739+
* no such buffer, a new one is created. In all cases the returned buffer is
740+
* added to the used buffers list and will have to be released via
741+
* releaseBuffer.
742+
*/
743+
uint8_t* FontFreeType::acquireBuffer(size_t size)
744+
{
745+
const size_t n = _availableBuffers.size();
746+
747+
// Index of the first pool of large enough buffers. If we could not find a
748+
// buffer for the given size, this is also the index where a new pool will
749+
// be inserted.
750+
size_t new_pool_index = n;
751+
752+
// Find the place where the best pool for our size should be.
753+
for (size_t i = 0; i != n; ++i)
754+
if (_availableBuffers[i].capacity >= size)
755+
{
756+
new_pool_index = i;
757+
break;
758+
}
759+
760+
// Now search for a pool with an available buffer.
761+
for (size_t i = new_pool_index; i != n; ++i)
762+
{
763+
BufferPool& p = _availableBuffers[i];
764+
765+
// A pool with large enough buffers and an available one.
766+
if (!p.buffers.empty())
767+
{
768+
uint8_t* const buffer = p.buffers.back();
769+
p.buffers.pop_back();
770+
_usedBuffers[buffer] = p.capacity;
771+
772+
return buffer;
773+
}
774+
}
775+
776+
if ((new_pool_index == n) || (_availableBuffers[new_pool_index].capacity != size))
777+
// Either all pools have capacities smaller than the requested size, or else
778+
// We have pools with capacity larger than the requested size but none had
779+
// an available buffer. We create a new pool for the requested size so
780+
// we'll be ready to receive the buffer in releaseBuffer.
781+
_availableBuffers.emplace(_availableBuffers.begin() + new_pool_index)->capacity = size;
782+
783+
uint8_t* const buffer(new uint8_t[size]);
784+
_usedBuffers[buffer] = size;
785+
786+
return buffer;
787+
}
788+
713789
} // namespace ax

core/2d/FontFreeType.h

Lines changed: 22 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,11 @@
2828

2929
#include "2d/Font.h"
3030
#include "2d/IFontEngine.h"
31+
32+
#include <tsl/robin_map.h>
33+
3134
#include <string>
35+
#include <memory>
3236

3337
namespace ax
3438
{
@@ -133,6 +137,7 @@ class AX_DLL FontFreeType : public Font
133137
int& outHeight,
134138
Rect& outRect,
135139
int& xAdvance);
140+
void releaseBuffer(uint8_t* buffer);
136141

137142
int getFontAscender() const;
138143
const char* getFontFamily() const;
@@ -169,6 +174,8 @@ class AX_DLL FontFreeType : public Font
169174

170175
void setGlyphCollection(GlyphCollection glyphs, std::string_view customGlyphs);
171176

177+
uint8_t* acquireBuffer(size_t size);
178+
172179
FT_Face _fontFace;
173180
FT_Stream _fontStream;
174181
FT_Stroker _stroker;
@@ -183,9 +190,23 @@ class AX_DLL FontFreeType : public Font
183190

184191
GlyphCollection _usedGlyphs;
185192
std::string _customGlyphs;
193+
194+
struct BufferPool
195+
{
196+
size_t capacity;
197+
std::vector<uint8_t*> buffers;
198+
};
199+
200+
using BufferPoolVector = std::vector<BufferPool>;
201+
// using UsedBuffersMap = tsl::robin_map<uint8_t*, size_t>;
202+
using UsedBuffersMap = std::unordered_map<uint8_t*, size_t>;
203+
204+
/// Those are sorted by increasing capacity.
205+
BufferPoolVector _availableBuffers;
206+
UsedBuffersMap _usedBuffers;
186207
};
187208

188209
// end of _2d group
189210
/// @}
190211

191-
}
212+
} // namespace ax

0 commit comments

Comments
 (0)