3737#include " simdjson/simdjson.h"
3838#include " zlib.h"
3939#include " axmol/base/ZipUtils.h"
40-
4140#include " axmol/base/json.h"
4241
4342namespace ax
@@ -121,7 +120,9 @@ void FontAtlas::loadFontAtlas(std::string_view fontatlasFile, axstd::string_map<
121120
122121FontAtlas::FontAtlas (Font* theFont)
123122 : FontAtlas(theFont, CacheTextureWidth, CacheTextureHeight, AX_CONTENT_SCALE_FACTOR())
124- {}
123+ {
124+ _newChars.reserve (128 );
125+ }
125126
126127FontAtlas::FontAtlas (Font* theFont, int atlasWidth, int atlasHeight, float scaleFactor)
127128 : _font(theFont), _width(atlasWidth), _height(atlasHeight), _scaleFactor(scaleFactor)
@@ -312,58 +313,57 @@ bool FontAtlas::getLetterDefinitionForChar(char32_t utf32Char, FontLetterDefinit
312313 }
313314}
314315
315- void FontAtlas::findNewCharacters (const std::u32string& u32Text, std::unordered_set< char32_t >& charset )
316+ bool FontAtlas::findNewCharacters (const std::u32string& u32Text)
316317{
318+ _newChars.clear ();
319+
317320 if (_letterDefinitions.empty ())
318321 {
319- std::copy (u32Text.begin (), u32Text.end (), std::inserter (charset, charset .end ()));
322+ std::copy (u32Text.begin (), u32Text.end (), std::inserter (_newChars, _newChars .end ()));
320323 }
321324 else
322325 {
323326 for (auto && charCode : u32Text)
324327 if (_letterDefinitions.find (charCode) == _letterDefinitions.end ())
325- charset .insert (charCode);
328+ _newChars .insert (charCode);
326329 }
330+
331+ return !_newChars.empty ();
327332}
328333
329334bool FontAtlas::prepareLetterDefinitions (const std::u32string& utf32Text)
330335{
331- if (_fontFreeType == nullptr )
332- {
336+ if (!_fontFreeType)
333337 return false ;
334- }
335-
336338 if (!_currentPageData)
337339 reinit ();
338340
339- std::unordered_set<char32_t > charCodeSet;
340- findNewCharacters (utf32Text, charCodeSet);
341- if (charCodeSet.empty ())
342- {
341+ if (!findNewCharacters (utf32Text))
343342 return false ;
344- }
345343
346- int adjustForDistanceMap = _letterPadding / 2 ;
347- int adjustForExtend = _letterEdgeExtend / 2 ;
348- int bitmapWidth = 0 ;
349- int bitmapHeight = 0 ;
350- int glyphHeight;
351- Rect tempRect;
352- FontLetterDefinition tempDef;
344+ const int adjustForDistanceMap = _letterPadding / 2 ;
345+ const int adjustForExtend = _letterEdgeExtend / 2 ;
353346
354- auto pixelFormat = _pixelFormat;
347+ int bitmapWidth = 0 , bitmapHeight = 0 ;
348+ Rect tempRect;
349+ FontLetterDefinition tempDef{};
355350
356- int startY = (int )_currentPageOrigY;
351+ auto pixelFormat = _pixelFormat;
352+ int startY = static_cast <int >(_currentPageOrigY);
353+ int pageUploadStartY = startY; // Upload start position of the current page
354+ int pageUploadEndY = startY; // Upload end position of the current page (updated dynamically)
357355
358- for (auto && charCode : charCodeSet )
356+ for (auto && charCode : _newChars )
359357 {
360358 auto missingIt = _missingGlyphFallbackFonts.find (charCode);
361359 uint8_t * bitmap = nullptr ;
362360 FontFreeType* charRenderer = _fontFreeType;
361+ bool sharedBitmapData{true }; // does the bitmap data shared from FontFreeType engine
363362 if (missingIt == _missingGlyphFallbackFonts.end ())
364363 {
365364 const GlyphResolution* res{nullptr };
366- bitmap = charRenderer->getGlyphBitmap (charCode, bitmapWidth, bitmapHeight, tempRect, tempDef.xAdvance , res);
365+ bitmap = charRenderer->getGlyphBitmap (charCode, bitmapWidth, bitmapHeight, tempRect, tempDef.xAdvance , res,
366+ sharedBitmapData);
367367 if (!bitmap && res)
368368 {
369369 auto fallbackIt = _missingFallbackFonts.find (res->faceInfo .family );
@@ -382,86 +382,122 @@ bool FontAtlas::prepareLetterDefinitions(const std::u32string& utf32Text)
382382 {
383383 unsigned int glyphIndex = res->glyphIndex ;
384384 bitmap = charRenderer->getGlyphBitmapByIndex (glyphIndex, bitmapWidth, bitmapHeight, tempRect,
385- tempDef.xAdvance );
385+ tempDef.xAdvance , sharedBitmapData );
386386 _missingGlyphFallbackFonts.emplace (charCode, std::make_pair (charRenderer, glyphIndex));
387387 }
388388 }
389389 }
390390 else
391- { // found fallback font for missing charas , getGlyphBitmap without fallback
391+ { // Found fallback font for missing characters , getGlyphBitmap without fallback
392392 charRenderer = missingIt->second .first ;
393393 unsigned int glyphIndex = missingIt->second .second ;
394- bitmap =
395- charRenderer-> getGlyphBitmapByIndex (glyphIndex, bitmapWidth, bitmapHeight, tempRect, tempDef.xAdvance );
394+ bitmap = charRenderer-> getGlyphBitmapByIndex (glyphIndex, bitmapWidth, bitmapHeight, tempRect,
395+ tempDef.xAdvance , sharedBitmapData );
396396 }
397+
397398 if (bitmap && bitmapWidth > 0 && bitmapHeight > 0 )
398399 {
399- tempDef.validDefinition = true ;
400- tempDef.width = tempRect.size .width + _letterPadding + _letterEdgeExtend;
401- tempDef.height = tempRect.size .height + _letterPadding + _letterEdgeExtend;
402- tempDef.offsetX = tempRect.origin .x - adjustForDistanceMap - adjustForExtend;
403- tempDef.offsetY = _fontAscender + tempRect.origin .y - adjustForDistanceMap - adjustForExtend;
400+ // Calculate occupied area using actual bitmap size
401+ const int glyphWidth = bitmapWidth + _letterPadding + _letterEdgeExtend;
402+ const int glyphHeight = bitmapHeight + _letterPadding + _letterEdgeExtend;
404403
405- if (_currentPageOrigX + tempDef.width > _width)
404+ // Not enough width in current line → wrap to next line
405+ if (_currentPageOrigX + glyphWidth > _width)
406406 {
407407 _currentPageOrigY += _currLineHeight;
408408 _currLineHeight = 0 ;
409409 _currentPageOrigX = 0 ;
410- if (_currentPageOrigY + _lineHeight + _letterPadding + _letterEdgeExtend >= _height)
411- {
412- updateTextureContent (pixelFormat, startY);
413-
414- startY = 0 ;
410+ }
415411
416- addNewPage ();
417- }
412+ // Not enough height → upload current page and start a new page
413+ if (_currentPageOrigY + glyphHeight > _height)
414+ {
415+ // Upload the written region of the current page (startY..pageUploadEndY)
416+ updateTextureContent (pixelFormat, pageUploadStartY);
417+ // Start a new page
418+ addNewPage ();
419+ _currentPageOrigX = 0 ;
420+ _currentPageOrigY = 0 ;
421+ _currLineHeight = 0 ;
422+ pageUploadStartY = 0 ;
423+ pageUploadEndY = 0 ;
418424 }
419- glyphHeight = static_cast <int >(bitmapHeight) + _letterPadding + _letterEdgeExtend;
420- _currLineHeight = std::max (glyphHeight, _currLineHeight);
421- charRenderer->renderCharAt (_currentPageData, (int )_currentPageOrigX + adjustForExtend,
422- (int )_currentPageOrigY + adjustForExtend, bitmap, bitmapWidth, bitmapHeight,
423- _width, _height);
424425
426+ // Calculate tempDef offsets (based on tempRect)
427+ tempDef.validDefinition = true ;
428+ tempDef.width = tempRect.size .width + _letterPadding + _letterEdgeExtend;
429+ tempDef.height = tempRect.size .height + _letterPadding + _letterEdgeExtend;
430+ tempDef.offsetX = tempRect.origin .x - adjustForDistanceMap - adjustForExtend;
431+ tempDef.offsetY = _fontAscender + tempRect.origin .y - adjustForDistanceMap - adjustForExtend;
432+
433+ // Render glyph into the current page
434+ charRenderer->renderCharAt (_currentPageData, static_cast <int >(_currentPageOrigX) + adjustForExtend,
435+ static_cast <int >(_currentPageOrigY) + adjustForExtend, bitmap, bitmapWidth,
436+ bitmapHeight, _width, _height);
437+
438+ // Record glyph position and page
425439 tempDef.U = _currentPageOrigX;
426440 tempDef.V = _currentPageOrigY;
427441 tempDef.textureID = _currentPage;
428- _currentPageOrigX += tempDef.width + 1 ;
429- // take from pixels to points
430- tempDef.width = tempDef.width / _scaleFactor;
431- tempDef.height = tempDef.height / _scaleFactor;
432- tempDef.U = tempDef.U / _scaleFactor;
433- tempDef.V = tempDef.V / _scaleFactor;
442+
443+ // Update line height and X pointer (leave 1 pixel spacing between glyphs)
444+ _currLineHeight = std::max (glyphHeight, _currLineHeight);
445+ _currentPageOrigX += glyphWidth + 1 ;
446+
447+ // Maintain upload range (highest Y written on this page)
448+ pageUploadEndY = std::max<int >(pageUploadEndY, _currentPageOrigY + _currLineHeight);
449+
450+ // Convert pixel dimensions to points
451+ tempDef.width /= _scaleFactor;
452+ tempDef.height /= _scaleFactor;
453+ tempDef.U /= _scaleFactor;
454+ tempDef.V /= _scaleFactor;
434455 tempDef.rotated = false ;
435456 }
436457 else
437458 {
438- delete[] bitmap;
439-
440459 tempDef.validDefinition = !!tempDef.xAdvance ;
441- tempDef.width = 0 ;
442- tempDef.height = 0 ;
443- tempDef.U = 0 ;
444- tempDef.V = 0 ;
445- tempDef.offsetX = 0 ;
446- tempDef.offsetY = 0 ;
447- tempDef.textureID = 0 ;
448- tempDef.rotated = false ;
460+ tempDef.width = tempDef.height = tempDef.U = tempDef.V = 0 ;
461+ tempDef.offsetX = tempDef.offsetY = 0 ;
462+ tempDef.textureID = 0 ;
463+ tempDef.rotated = false ;
449464 _currentPageOrigX += 1 ;
450465 }
451466
467+ if (!sharedBitmapData && bitmap)
468+ delete[] bitmap;
469+
452470 _letterDefinitions[charCode] = tempDef;
453471 }
454472
455- updateTextureContent (pixelFormat, startY);
473+ // Handle remaining upload for the current page (from startY to max written Y)
474+ if (pageUploadEndY > pageUploadStartY)
475+ {
476+ updateTextureContent (pixelFormat, pageUploadStartY);
477+ }
456478
457479 return true ;
458480}
459481
460482void FontAtlas::updateTextureContent (rhi::PixelFormat format, int startY)
461483{
462- auto data = _currentPageData + (_width * (int )startY << _strideShift);
463- _atlasTextures[_currentPage]->updateSubData (data, 0 , startY, _width,
464- (std::min)((int )_currentPageOrigY - startY + _currLineHeight, _height));
484+ // Calculate the starting data pointer
485+ auto data = _currentPageData + ((_width * (int )startY) << _strideShift);
486+
487+ // Calculate the actual upload height: from startY to _currentPageOrigY + _currLineHeight
488+ int uploadHeight = (int )_currentPageOrigY + _currLineHeight - startY;
489+
490+ // Defensive check to avoid negative values or overflow
491+ if (uploadHeight < 0 )
492+ uploadHeight = 0 ;
493+
494+ // Clamp to remaining space to prevent out-of-bounds
495+ uploadHeight = std::min (uploadHeight, _height - startY);
496+ if (uploadHeight <= 0 )
497+ return ;
498+
499+ // Perform the texture update
500+ _atlasTextures[_currentPage]->updateSubData (data, 0 , startY, _width, uploadHeight);
465501}
466502
467503void FontAtlas::addNewPage ()
0 commit comments