Skip to content

Commit 3c6af7c

Browse files
committed
Recycle temporary glyph buffers in FontFreeType.
1 parent 8bfe19a commit 3c6af7c

File tree

3 files changed

+114
-9
lines changed

3 files changed

+114
-9
lines changed

core/2d/FontAtlas.cpp

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -438,7 +438,8 @@ bool FontAtlas::prepareLetterDefinitions(const std::u32string& utf32Text)
438438
}
439439
else
440440
{
441-
delete[] bitmap;
441+
if (bitmap)
442+
charRenderer->releaseBuffer(bitmap);
442443

443444
tempDef.validDefinition = !!tempDef.xAdvance;
444445
tempDef.width = 0;

core/2d/FontFreeType.cpp

Lines changed: 93 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -210,6 +210,14 @@ 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+
{
217+
for (uint8_t* b : p.buffers)
218+
delete[] b;
219+
}
220+
213221
if (_FTInitialized)
214222
{
215223
if (_stroker)
@@ -503,15 +511,15 @@ unsigned char* FontFreeType::getGlyphBitmapByIndex(unsigned int glyphIndex,
503511

504512
if (_outlineSize > 0 && outWidth > 0 && outHeight > 0)
505513
{
506-
auto copyBitmap = new unsigned char[outWidth * outHeight];
507-
memcpy(copyBitmap, ret, outWidth * outHeight * sizeof(unsigned char));
514+
uint8_t* const copyBitmap = acquireBuffer(outWidth * outHeight);
515+
memcpy(copyBitmap, ret, outWidth * outHeight * sizeof(uint8_t));
508516

509517
FT_BBox bbox;
510518
auto outlineBitmap = getGlyphBitmapWithOutline(glyphIndex, bbox);
511519
if (outlineBitmap == nullptr)
512520
{
513521
ret = nullptr;
514-
delete[] copyBitmap;
522+
releaseBuffer(copyBitmap);
515523
break;
516524
}
517525

@@ -540,7 +548,7 @@ unsigned char* FontFreeType::getGlyphBitmapByIndex(unsigned int glyphIndex,
540548
{
541549
FT_Pos index, index2;
542550
auto imageSize = blendWidth * blendHeight * 2;
543-
blendImage = new unsigned char[imageSize];
551+
blendImage = acquireBuffer(imageSize);
544552
memset(blendImage, 0, imageSize);
545553

546554
auto px = outlineMinX - blendImageMinX;
@@ -573,8 +581,8 @@ unsigned char* FontFreeType::getGlyphBitmapByIndex(unsigned int glyphIndex,
573581
outWidth = static_cast<int>(blendWidth);
574582
outHeight = static_cast<int>(blendHeight);
575583

576-
delete[] outlineBitmap;
577-
delete[] copyBitmap;
584+
releaseBuffer(outlineBitmap);
585+
releaseBuffer(copyBitmap);
578586
ret = blendImage;
579587
}
580588

@@ -588,6 +596,26 @@ unsigned char* FontFreeType::getGlyphBitmapByIndex(unsigned int glyphIndex,
588596
return nullptr;
589597
}
590598

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

609637
FT_Bitmap bmp;
610-
bmp.buffer = new unsigned char[width * rows];
638+
bmp.buffer = acquireBuffer(width * rows);
611639
memset(bmp.buffer, 0, width * rows);
612640
bmp.width = (int)width;
613641
bmp.rows = (int)rows;
@@ -653,7 +681,7 @@ void FontFreeType::renderCharAt(unsigned char* dest,
653681
memcpy(dest + (iX + (iY * atlasWidth)) * 2, bitmap + bitmap_y * 2, bitmapWidth * 2);
654682
++iY;
655683
}
656-
delete[] bitmap;
684+
releaseBuffer(bitmap);
657685
}
658686
else
659687
{
@@ -710,4 +738,61 @@ void FontFreeType::releaseFont(std::string_view fontName)
710738
}
711739
}
712740

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

core/2d/FontFreeType.h

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,9 @@
2828

2929
#include "2d/Font.h"
3030
#include "2d/IFontEngine.h"
31+
3132
#include <string>
33+
#include <unordered_map>
3234

3335
namespace ax
3436
{
@@ -133,6 +135,7 @@ class AX_DLL FontFreeType : public Font
133135
int& outHeight,
134136
Rect& outRect,
135137
int& xAdvance);
138+
void releaseBuffer(uint8_t* buffer);
136139

137140
int getFontAscender() const;
138141
const char* getFontFamily() const;
@@ -169,6 +172,8 @@ class AX_DLL FontFreeType : public Font
169172

170173
void setGlyphCollection(GlyphCollection glyphs, std::string_view customGlyphs);
171174

175+
uint8_t* acquireBuffer(size_t size);
176+
172177
FT_Face _fontFace;
173178
FT_Stream _fontStream;
174179
FT_Stroker _stroker;
@@ -183,6 +188,20 @@ class AX_DLL FontFreeType : public Font
183188

184189
GlyphCollection _usedGlyphs;
185190
std::string _customGlyphs;
191+
192+
struct BufferPool
193+
{
194+
/// All the buffers in this pool have this capacity.
195+
size_t capacity;
196+
std::vector<uint8_t*> buffers;
197+
};
198+
199+
using BufferPoolVector = std::vector<BufferPool>;
200+
using UsedBuffersMap = std::unordered_map<uint8_t*, size_t>;
201+
202+
/// Those are sorted by increasing capacity.
203+
BufferPoolVector _availableBuffers;
204+
UsedBuffersMap _usedBuffers;
186205
};
187206

188207
// end of _2d group

0 commit comments

Comments
 (0)