@@ -27,6 +27,10 @@ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLI
2727#include < chrono>
2828#include < iostream>
2929#include < set>
30+ #include < thread>
31+ #include < utility>
32+
33+ #include < BS_thread_pool.hpp>
3034
3135namespace vsgXchange
3236{
@@ -547,12 +551,34 @@ vsg::ref_ptr<vsg::Object> freetype::Implementation::read(const vsg::Path& filena
547551 init ();
548552 if (!_library) return {};
549553
550- FT_Face face;
551- FT_Long face_index = 0 ;
554+ FT_UInt pixel_size = 48 ;
555+ FT_UInt freetype_pixel_size = pixel_size;
556+ float freetype_pixel_size_scale = float (pixel_size) / (64 .0f * float (freetype_pixel_size));
557+
558+ // create and initialize a new freetype face
559+ auto const createNewFace = [&]{
560+ FT_Face face;
561+ FT_Long face_index = 0 ;
562+ // Windows workaround for no wchar_t support in Freetype, convert vsg::Path's std::wstring to UTF8 std::string
563+ std::string filenameToUse_string = filenameToUse.string ();
564+ int error = FT_New_Face (_library, filenameToUse_string.c_str (), face_index, &face);
565+ if (error)
566+ {
567+ face = nullptr ;
568+ }
569+ else
570+ {
571+ error = FT_Set_Pixel_Sizes (face, freetype_pixel_size, freetype_pixel_size);
572+ if (error)
573+ {
574+ FT_Done_Face (face);
575+ face = nullptr ;
576+ }
577+ }
578+ return std::make_pair (error,face);
579+ };
552580
553- // Windows workaround for no wchar_t support in Freetype, convert vsg::Path's std::wstring to UTF8 std::string
554- std::string filenameToUse_string = filenameToUse.string ();
555- int error = FT_New_Face (_library, filenameToUse_string.c_str (), face_index, &face);
581+ auto [error,face] = createNewFace ();
556582 if (error == FT_Err_Unknown_File_Format)
557583 {
558584 std::cout << " Warning: FreeType unable to read font file : " << filenameToUse << " , error = " << FT_Err_Unknown_File_Format << std::endl;
@@ -564,14 +590,6 @@ vsg::ref_ptr<vsg::Object> freetype::Implementation::read(const vsg::Path& filena
564590 return {};
565591 }
566592
567- FT_UInt pixel_size = 48 ;
568- FT_UInt freetype_pixel_size = pixel_size;
569- float freetype_pixel_size_scale = float (pixel_size) / (64 .0f * float (freetype_pixel_size));
570-
571- {
572- error = FT_Set_Pixel_Sizes (face, freetype_pixel_size, freetype_pixel_size);
573- }
574-
575593 FT_Int32 load_flags = FT_LOAD_NO_BITMAP;
576594 FT_Render_Mode render_mode = FT_RENDER_MODE_NORMAL;
577595
@@ -737,26 +755,28 @@ vsg::ref_ptr<vsg::Object> freetype::Implementation::read(const vsg::Path& filena
737755 // initialize charmap to zeros.
738756 for (auto & c : *charmap) c = 0 ;
739757
740- for (auto & glyphQuad : sortedGlyphQuads)
741- {
742- error = FT_Load_Glyph (face, glyphQuad.glyph_index , load_flags);
743- if (error) continue ;
758+ // setup a thread pool for accepting contour generation tasks
759+ auto numThreads{std::thread::hardware_concurrency ()};
760+ std::vector<FT_Face> threadFaceData (numThreads, FT_Face{nullptr });
761+ BS::thread_pool threadPool{numThreads, [&](auto threadIndex){
762+ int freetype_error{0 };
763+ std::tie (freetype_error,threadFaceData[threadIndex]) = createNewFace ();
764+ }};
765+
766+ auto const generateContours = [&](GlyphQuad const & glyphQuad, unsigned int glyphXpos, unsigned int glyphYpos) {
767+ auto const threadIndex = *BS::this_thread::get_index ();
768+ auto const faceData = threadFaceData[threadIndex];
769+ error = FT_Load_Glyph (faceData, glyphQuad.glyph_index , load_flags);
770+ if (error) return ;
744771
745772 unsigned int width = glyphQuad.width ;
746773 unsigned int height = glyphQuad.height ;
747- auto metrics = face->glyph ->metrics ;
748-
749- if ((xpos + width + texel_margin) > atlas->width ())
750- {
751- // glyph doesn't fit in present row so shift to next row.
752- xpos = texel_margin;
753- ypos = ytop;
754- }
774+ auto metrics = faceData->glyph ->metrics ;
755775
756776 if (useOutline)
757777 {
758778 Contours contours;
759- generateOutlines (face ->glyph ->outline , contours);
779+ generateOutlines (faceData ->glyph ->outline , contours);
760780
761781 // scale and offset the outline geometry
762782 vsg::vec2 offset (float (metrics.horiBearingX ) * freetype_pixel_size_scale, float (metrics.horiBearingY ) * freetype_pixel_size_scale);
@@ -823,7 +843,7 @@ vsg::ref_ptr<vsg::Object> freetype::Implementation::read(const vsg::Path& filena
823843 int delta = quad_margin - 2 ;
824844 for (int r = -delta; r < static_cast <int >(height + delta); ++r)
825845 {
826- std::size_t index = atlas->index (xpos - delta, ypos + r);
846+ std::size_t index = atlas->index (glyphXpos - delta, glyphYpos + r);
827847 for (int c = -delta; c < static_cast <int >(width + delta); ++c)
828848 {
829849 vsg::vec2 v;
@@ -858,23 +878,23 @@ vsg::ref_ptr<vsg::Object> freetype::Implementation::read(const vsg::Path& filena
858878 }
859879 else
860880 {
861- if (face ->glyph->format != FT_GLYPH_FORMAT_BITMAP)
881+ if (faceData ->glyph->format != FT_GLYPH_FORMAT_BITMAP)
862882 {
863- error = FT_Render_Glyph (face ->glyph , render_mode);
864- if (error) continue ;
883+ error = FT_Render_Glyph (faceData ->glyph , render_mode);
884+ if (error) return ;
865885 }
866886
867- if (face ->glyph->format != FT_GLYPH_FORMAT_BITMAP) continue ;
887+ if (faceData ->glyph->format != FT_GLYPH_FORMAT_BITMAP) return ;
868888
869- const FT_Bitmap& bitmap = face ->glyph->bitmap;
889+ const FT_Bitmap& bitmap = faceData ->glyph->bitmap;
870890
871891 // copy pixels
872892 if (computeSDF)
873893 {
874894 int delta = quad_margin - 2 ;
875895 for (int r = -delta; r < static_cast <int >(bitmap.rows + delta); ++r)
876896 {
877- std::size_t index = atlas->index (xpos - delta, ypos + r);
897+ std::size_t index = atlas->index (glyphXpos - delta, glyphYpos + r);
878898 for (int c = -delta; c < static_cast <int >(bitmap.width + delta); ++c)
879899 {
880900 atlas->at (index++) = nearest_edge (bitmap, c, r, quad_margin);
@@ -886,14 +906,36 @@ vsg::ref_ptr<vsg::Object> freetype::Implementation::read(const vsg::Path& filena
886906 const unsigned char * ptr = bitmap.buffer ;
887907 for (unsigned int r = 0 ; r < bitmap.rows ; ++r)
888908 {
889- std::size_t index = atlas->index (xpos, ypos + r);
909+ std::size_t index = atlas->index (glyphXpos, glyphYpos + r);
890910 for (unsigned int c = 0 ; c < bitmap.width ; ++c)
891911 {
892912 atlas->at (index++) = *ptr++;
893913 }
894914 }
895915 }
896916 }
917+ };
918+
919+ for (auto & glyphQuad : sortedGlyphQuads)
920+ {
921+ error = FT_Load_Glyph (face, glyphQuad.glyph_index , load_flags);
922+ if (error) continue ;
923+
924+ unsigned int width = glyphQuad.width ;
925+ unsigned int height = glyphQuad.height ;
926+ auto metrics = face->glyph ->metrics ;
927+
928+ if ((xpos + width + texel_margin) > atlas->width ())
929+ {
930+ // glyph doesn't fit in present row so shift to next row.
931+ xpos = texel_margin;
932+ ypos = ytop;
933+ }
934+
935+ // submit the task to generate contours concurrently
936+ threadPool.detach_task ([&generateContours, glyphQuad, xpos, ypos] {
937+ generateContours (glyphQuad, xpos, ypos);
938+ });
897939
898940 vsg::vec4 uvrect (
899941 (float (xpos - quad_margin) - 1 .0f ) / float (atlas->width () - 1 ), float (ypos + height + quad_margin) / float (atlas->height () - 1 ),
@@ -929,5 +971,17 @@ vsg::ref_ptr<vsg::Object> freetype::Implementation::read(const vsg::Path& filena
929971 font->glyphMetrics = glyphMetrics;
930972 font->charmap = charmap;
931973
974+ FT_Done_Face (face);
975+
976+ // wait for all tasks to finish
977+ threadPool.wait ();
978+
979+ // cleanup thread storage
980+ for (auto & faceData : threadFaceData)
981+ {
982+ if (faceData)
983+ FT_Done_Face (faceData);
984+ }
985+
932986 return font;
933987}
0 commit comments