1 /*=========================================================================
2 
3   Program:   Visualization Toolkit
4   Module:    vtkFreeTypeTools.cxx
5 
6   Copyright (c) Ken Martin, Will Schroeder, Bill Lorensen
7   All rights reserved.
8   See Copyright.txt or http://www.kitware.com/Copyright.htm for details.
9 
10      This software is distributed WITHOUT ANY WARRANTY; without even
11      the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
12      PURPOSE.  See the above copyright notice for more information.
13 
14 =========================================================================*/
15 
16 #include "vtkFreeTypeTools.h"
17 
18 #include "vtkTextProperty.h"
19 #include "vtkObjectFactory.h"
20 #include "vtkMath.h"
21 #include "vtkNew.h"
22 #include "vtkPath.h"
23 #include "vtkImageData.h"
24 #include "vtkSmartPointer.h"
25 #include "vtkVector.h"
26 #include "vtkVectorOperators.h"
27 
28 #include "vtkStdString.h"
29 #include "vtkUnicodeString.h"
30 
31 // The embedded fonts
32 #include "fonts/vtkEmbeddedFonts.h"
33 
34 #include <cstdint>
35 #include <limits>
36 #include <cassert>
37 #include <algorithm>
38 #include <map>
39 #include <vector>
40 #include <sstream>
41 #include <limits>
42 
43 // Print debug info
44 #define VTK_FTFC_DEBUG 0
45 #define VTK_FTFC_DEBUG_CD 0
46 
47 namespace {
48 // Some helper functions:
rotateVector2i(vtkVector2i & vec,float sinTheta,float cosTheta)49 void rotateVector2i(vtkVector2i &vec, float sinTheta, float cosTheta)
50 {
51   int x = static_cast<int>(std::round(cosTheta * vec[0] - sinTheta * vec[1]));
52   int y = static_cast<int>(std::round(sinTheta * vec[0] + cosTheta * vec[1]));
53   vec = vtkVector2i(x, y);
54 }
55 
56 } // end anon namespace
57 
58 class vtkTextPropertyLookup
59     : public std::map<size_t, vtkSmartPointer<vtkTextProperty> >
60 {
61 public:
contains(const size_t id)62   bool contains(const size_t id) {return this->find(id) != this->end();}
63 };
64 
65 class vtkFreeTypeTools::MetaData
66 {
67 public:
68   // Set by PrepareMetaData
69   vtkTextProperty *textProperty;
70   size_t textPropertyCacheId;
71   size_t unrotatedTextPropertyCacheId;
72   FTC_ScalerRec scaler;
73   FTC_ScalerRec unrotatedScaler;
74   FT_Face face;
75   bool faceHasKerning;
76   bool faceIsRotated;
77   FT_Matrix rotation;
78   FT_Matrix inverseRotation;
79 
80   // Set by CalculateBoundingBox
81   vtkVector2i ascent;  // Vector from baseline to top of characters
82   vtkVector2i descent; // Vector from baseline to bottom of characters
83   int height;
84   struct LineMetrics {
85     vtkVector2i origin;
86     int width;
87     // bbox relative to origin[XY]:
88     int xmin;
89     int xmax;
90     int ymin;
91     int ymax;
92   };
93   vtkVector2i dx; // Vector representing the data width after rotation
94   vtkVector2i dy; // Vector representing the data height after rotation
95   vtkVector2i TL; // Top left corner of the rotated data
96   vtkVector2i TR; // Top right corner of the rotated data
97   vtkVector2i BL; // Bottom left corner of the rotated data
98   vtkVector2i BR; // Bottom right corner of the rotated data
99   std::vector<LineMetrics> lineMetrics;
100   int maxLineWidth;
101   vtkTuple<int, 4> bbox;
102 };
103 
104 class vtkFreeTypeTools::ImageMetaData : public vtkFreeTypeTools::MetaData
105 {
106 public:
107   // Set by PrepareImageMetaData
108   int imageDimensions[3];
109   vtkIdType imageIncrements[3];
110   unsigned char rgba[4];
111 };
112 
113 //----------------------------------------------------------------------------
114 // The singleton, and the singleton cleanup counter
115 vtkFreeTypeTools* vtkFreeTypeTools::Instance;
116 static unsigned int vtkFreeTypeToolsCleanupCounter;
117 
118 //----------------------------------------------------------------------------
119 // The embedded fonts
120 // Create a lookup table between the text mapper attributes
121 // and the font buffers.
122 struct EmbeddedFontStruct
123 {
124   size_t length;
125   unsigned char *ptr;
126 };
127 
128 //------------------------------------------------------------------------------
129 // Clean up the vtkFreeTypeTools instance at exit. Using a separate class allows
130 // us to delay initialization of the vtkFreeTypeTools class.
vtkFreeTypeToolsCleanup()131 vtkFreeTypeToolsCleanup::vtkFreeTypeToolsCleanup()
132 {
133   vtkFreeTypeToolsCleanupCounter++;
134 }
135 
~vtkFreeTypeToolsCleanup()136 vtkFreeTypeToolsCleanup::~vtkFreeTypeToolsCleanup()
137 {
138   if (--vtkFreeTypeToolsCleanupCounter == 0)
139   {
140     vtkFreeTypeTools::SetInstance(nullptr);
141   }
142 }
143 
144 //----------------------------------------------------------------------------
GetInstance()145 vtkFreeTypeTools* vtkFreeTypeTools::GetInstance()
146 {
147   if (!vtkFreeTypeTools::Instance)
148   {
149     vtkFreeTypeTools::Instance = static_cast<vtkFreeTypeTools *>(
150       vtkObjectFactory::CreateInstance("vtkFreeTypeTools"));
151     if (!vtkFreeTypeTools::Instance)
152     {
153       vtkFreeTypeTools::Instance = new vtkFreeTypeTools;
154       vtkFreeTypeTools::Instance->InitializeObjectBase();
155     }
156   }
157   return vtkFreeTypeTools::Instance;
158 }
159 
160 //----------------------------------------------------------------------------
SetInstance(vtkFreeTypeTools * instance)161 void vtkFreeTypeTools::SetInstance(vtkFreeTypeTools* instance)
162 {
163   if (vtkFreeTypeTools::Instance == instance)
164   {
165     return;
166   }
167 
168   if (vtkFreeTypeTools::Instance)
169   {
170     vtkFreeTypeTools::Instance->Delete();
171   }
172 
173   vtkFreeTypeTools::Instance = instance;
174 
175   // User will call ->Delete() after setting instance
176 
177   if (instance)
178   {
179     instance->Register(nullptr);
180   }
181 }
182 
183 //----------------------------------------------------------------------------
vtkFreeTypeTools()184 vtkFreeTypeTools::vtkFreeTypeTools()
185 {
186 #if VTK_FTFC_DEBUG_CD
187   printf("vtkFreeTypeTools::vtkFreeTypeTools\n");
188 #endif
189   // Force use of compiled fonts by default.
190   this->ForceCompiledFonts = true;
191   this->DebugTextures = false;
192   this->MaximumNumberOfFaces = 30; // combinations of family+bold+italic
193   this->MaximumNumberOfSizes = this->MaximumNumberOfFaces * 20; // sizes
194   this->MaximumNumberOfBytes = 300000UL * this->MaximumNumberOfSizes;
195   this->TextPropertyLookup = new vtkTextPropertyLookup ();
196   this->CacheManager = nullptr;
197   this->ImageCache   = nullptr;
198   this->CMapCache    = nullptr;
199   this->ScaleToPowerTwo = true;
200 
201   // Ideally this should be thread-local to support SMP:
202   FT_Error err;
203   this->Library = new FT_Library;
204   err = FT_Init_FreeType(this->Library);
205   if (err)
206   {
207     vtkErrorMacro("FreeType library initialization failed with error code: "
208                   << err << ".");
209     delete this->Library;
210     this->Library = nullptr;
211   }
212 }
213 
214 //----------------------------------------------------------------------------
~vtkFreeTypeTools()215 vtkFreeTypeTools::~vtkFreeTypeTools()
216 {
217 #if VTK_FTFC_DEBUG_CD
218   printf("vtkFreeTypeTools::~vtkFreeTypeTools\n");
219 #endif
220   this->ReleaseCacheManager();
221   delete TextPropertyLookup;
222 
223   FT_Done_FreeType(*this->Library);
224   delete this->Library;
225   this->Library = nullptr;
226 }
227 
228 //----------------------------------------------------------------------------
GetLibrary()229 FT_Library* vtkFreeTypeTools::GetLibrary()
230 {
231 #if VTK_FTFC_DEBUG_CD
232   printf("vtkFreeTypeTools::GetLibrary\n");
233 #endif
234 
235   return this->Library;
236 }
237 
238 //----------------------------------------------------------------------------
239 vtkFreeTypeTools::FaceMetrics
GetFaceMetrics(vtkTextProperty * tprop)240 vtkFreeTypeTools::GetFaceMetrics(vtkTextProperty *tprop)
241 {
242   FT_Face face;
243   this->GetFace(tprop, &face);
244 
245   FaceMetrics metrics;
246   metrics.UnitsPerEM = face->units_per_EM;
247   metrics.Ascender = face->ascender;
248   metrics.Descender = face->descender;
249   metrics.HorizAdvance = face->max_advance_width;
250   metrics.BoundingBox = { { static_cast<int>(face->bbox.xMin),
251                             static_cast<int>(face->bbox.xMax),
252                             static_cast<int>(face->bbox.yMin),
253                             static_cast<int>(face->bbox.yMax) } };
254   metrics.FamilyName = face->family_name;
255   metrics.Scalable = (face->face_flags & FT_FACE_FLAG_SCALABLE) != 0;
256   metrics.Bold = (face->style_flags & FT_STYLE_FLAG_BOLD) != 0;
257   metrics.Italic = (face->style_flags & FT_STYLE_FLAG_ITALIC) != 0;
258 
259   return metrics;
260 }
261 
262 //----------------------------------------------------------------------------
263 vtkFreeTypeTools::GlyphOutline
GetUnscaledGlyphOutline(vtkTextProperty * tprop,vtkUnicodeStringValueType charId)264 vtkFreeTypeTools::GetUnscaledGlyphOutline(vtkTextProperty *tprop,
265                                           vtkUnicodeStringValueType charId)
266 {
267   size_t tpropCacheId;
268   this->MapTextPropertyToId(tprop, &tpropCacheId);
269   FTC_FaceID faceId = reinterpret_cast<FTC_FaceID>(tpropCacheId);
270   GlyphOutline result;
271   result.HorizAdvance = 0;
272 
273   FTC_CMapCache *cmapCache = this->GetCMapCache();
274   if (!cmapCache)
275   {
276     vtkErrorMacro("CMapCache not found!");
277     return result;
278   }
279 
280   FT_UInt glyphId = FTC_CMapCache_Lookup(*cmapCache, faceId, 0, charId);
281 
282   FTC_ImageCache *imgCache = this->GetImageCache();
283   if (!imgCache)
284   {
285     vtkErrorMacro("ImageCache not found!");
286     return result;
287   }
288 
289   FTC_ImageTypeRec type;
290   type.face_id = faceId;
291   type.width = 0;
292   type.height = 0;
293   type.flags = FT_LOAD_NO_SCALE | FT_LOAD_IGNORE_TRANSFORM;
294 
295   FT_Glyph glyph;
296   FT_Error error = FTC_ImageCache_Lookup(*imgCache, &type, glyphId, &glyph, nullptr);
297   if (!error && glyph && glyph->format == ft_glyph_format_outline)
298   {
299     FT_OutlineGlyph outlineGlyph = reinterpret_cast<FT_OutlineGlyph>(glyph);
300     result.HorizAdvance = (glyph->advance.x + 0x8000) >> 16;
301     result.Path = vtkSmartPointer<vtkPath>::New();
302     this->OutlineToPath(0, 0, &outlineGlyph->outline, result.Path);
303   }
304 
305   return result;
306 }
307 
308 //----------------------------------------------------------------------------
309 std::array<int, 2>
GetUnscaledKerning(vtkTextProperty * tprop,vtkUnicodeStringValueType leftChar,vtkUnicodeStringValueType rightChar)310 vtkFreeTypeTools::GetUnscaledKerning(vtkTextProperty *tprop,
311                                      vtkUnicodeStringValueType leftChar,
312                                      vtkUnicodeStringValueType rightChar)
313 {
314   std::array<int, 2> result{ {0, 0} };
315   if (leftChar == 0 || rightChar == 0)
316   {
317     return result;
318   }
319 
320   size_t tpropCacheId;
321   this->MapTextPropertyToId(tprop, &tpropCacheId);
322   FT_Face face = nullptr;
323 
324   if (!this->GetFace(tpropCacheId, &face) || !face)
325   {
326     vtkErrorMacro("Error loading font face.");
327     return result;
328   }
329 
330 
331   if (FT_HAS_KERNING(face) != 0)
332   {
333     FTC_FaceID faceId = reinterpret_cast<FTC_FaceID>(tpropCacheId);
334     FTC_CMapCache *cmapCache = this->GetCMapCache();
335     if (!cmapCache)
336     {
337       vtkErrorMacro("CMapCache not found!");
338       return result;
339     }
340 
341     FT_UInt leftGIdx = FTC_CMapCache_Lookup(*cmapCache, faceId, 0, leftChar);
342     FT_UInt rightGIdx = FTC_CMapCache_Lookup(*cmapCache, faceId, 0, rightChar);
343     FT_Vector kerning;
344     FT_Error error =  FT_Get_Kerning(face, leftGIdx, rightGIdx,
345                                      FT_KERNING_UNSCALED, &kerning);
346     if (!error)
347     {
348       result[0] = kerning.x >> 6;
349       result[1] = kerning.y >> 6;
350     }
351   }
352 
353   return result;
354 }
355 
356 //----------------------------------------------------------------------------
GetCacheManager()357 FTC_Manager* vtkFreeTypeTools::GetCacheManager()
358 {
359   if (!this->CacheManager)
360   {
361     this->InitializeCacheManager();
362   }
363 
364   return this->CacheManager;
365 }
366 
367 //----------------------------------------------------------------------------
GetImageCache()368 FTC_ImageCache* vtkFreeTypeTools::GetImageCache()
369 {
370   if (!this->ImageCache)
371   {
372     this->InitializeCacheManager();
373   }
374 
375   return this->ImageCache;
376 }
377 
378 //----------------------------------------------------------------------------
GetCMapCache()379 FTC_CMapCache* vtkFreeTypeTools::GetCMapCache()
380 {
381   if (!this->CMapCache)
382   {
383     this->InitializeCacheManager();
384   }
385 
386   return this->CMapCache;
387 }
388 
389 //----------------------------------------------------------------------------
390 static FT_Error
vtkFreeTypeToolsFaceRequester(FTC_FaceID face_id,FT_Library lib,FT_Pointer request_data,FT_Face * face)391 vtkFreeTypeToolsFaceRequester(FTC_FaceID face_id,
392                               FT_Library lib,
393                               FT_Pointer request_data,
394                               FT_Face* face)
395 {
396 #if VTK_FTFC_DEBUG_CD
397   printf("vtkFreeTypeToolsFaceRequester()\n");
398 #endif
399 
400   // Get a pointer to the current vtkFreeTypeTools object
401   vtkFreeTypeTools *self =
402     reinterpret_cast<vtkFreeTypeTools*>(request_data);
403 
404   // Map the ID to a text property
405   vtkSmartPointer<vtkTextProperty> tprop =
406       vtkSmartPointer<vtkTextProperty>::New();
407   self->MapIdToTextProperty(reinterpret_cast<intptr_t>(face_id), tprop);
408 
409   bool faceIsSet = self->LookupFace(tprop, lib, face);
410 
411   if (!faceIsSet)
412   {
413     return static_cast<FT_Error>(1);
414   }
415 
416   if ( tprop->GetOrientation() != 0.0 )
417   {
418     // FreeType documentation says that the transform should not be set
419     // but we cache faces also by transform, so that there is a unique
420     // (face, orientation) cache entry
421     FT_Matrix matrix;
422     float angle = vtkMath::RadiansFromDegrees( tprop->GetOrientation() );
423     matrix.xx = (FT_Fixed)( cos(angle) * 0x10000L);
424     matrix.xy = (FT_Fixed)(-sin(angle) * 0x10000L);
425     matrix.yx = (FT_Fixed)( sin(angle) * 0x10000L);
426     matrix.yy = (FT_Fixed)( cos(angle) * 0x10000L);
427     FT_Set_Transform(*face, &matrix, nullptr);
428   }
429 
430   return static_cast<FT_Error>(0);
431 }
432 
433 //----------------------------------------------------------------------------
InitializeCacheManager()434 void vtkFreeTypeTools::InitializeCacheManager()
435 {
436 #if VTK_FTFC_DEBUG_CD
437   printf("vtkFreeTypeTools::InitializeCacheManager()\n");
438 #endif
439 
440   this->ReleaseCacheManager();
441 
442   FT_Error error;
443 
444   // Create the cache manager itself
445   this->CacheManager = new FTC_Manager;
446 
447   error = this->CreateFTCManager();
448 
449   if (error)
450   {
451     vtkErrorMacro(<< "Failed allocating a new FreeType Cache Manager");
452   }
453 
454   // The image cache
455   this->ImageCache = new FTC_ImageCache;
456   error = FTC_ImageCache_New(*this->CacheManager, this->ImageCache);
457 
458   if (error)
459   {
460     vtkErrorMacro(<< "Failed allocating a new FreeType Image Cache");
461   }
462 
463   // The charmap cache
464   this->CMapCache = new FTC_CMapCache;
465   error = FTC_CMapCache_New(*this->CacheManager, this->CMapCache);
466 
467   if (error)
468   {
469     vtkErrorMacro(<< "Failed allocating a new FreeType CMap Cache");
470   }
471 }
472 
473 //----------------------------------------------------------------------------
ReleaseCacheManager()474 void vtkFreeTypeTools::ReleaseCacheManager()
475 {
476 #if VTK_FTFC_DEBUG_CD
477   printf("vtkFreeTypeTools::ReleaseCacheManager()\n");
478 #endif
479 
480   if (this->CacheManager)
481   {
482     FTC_Manager_Done(*this->CacheManager);
483 
484     delete this->CacheManager;
485     this->CacheManager = nullptr;
486   }
487 
488   delete this->ImageCache;
489   this->ImageCache = nullptr;
490 
491   delete this->CMapCache;
492   this->CMapCache = nullptr;
493 }
494 
495 //----------------------------------------------------------------------------
GetBoundingBox(vtkTextProperty * tprop,const vtkStdString & str,int dpi,int bbox[4])496 bool vtkFreeTypeTools::GetBoundingBox(vtkTextProperty *tprop,
497                                       const vtkStdString& str, int dpi,
498                                       int bbox[4])
499 {
500   // We need the tprop and bbox
501   if (!tprop || !bbox)
502   {
503     vtkErrorMacro(<< "Wrong parameters, one of them is nullptr or zero");
504     return false;
505   }
506 
507   if (str.empty())
508   {
509     std::fill(bbox, bbox + 4, 0);
510     return true;
511   }
512 
513   MetaData metaData;
514   bool result = this->PrepareMetaData(tprop, dpi, metaData);
515   if (result)
516   {
517     result = this->CalculateBoundingBox(str, metaData);
518     if (result)
519     {
520       memcpy(bbox, metaData.bbox.GetData(), sizeof(int) * 4);
521     }
522   }
523   return result;
524 }
525 
526 //----------------------------------------------------------------------------
GetBoundingBox(vtkTextProperty * tprop,const vtkUnicodeString & str,int dpi,int bbox[4])527 bool vtkFreeTypeTools::GetBoundingBox(vtkTextProperty *tprop,
528                                       const vtkUnicodeString& str, int dpi,
529                                       int bbox[4])
530 {
531   // We need the tprop and bbox
532   if (!tprop || !bbox)
533   {
534     vtkErrorMacro(<< "Wrong parameters, one of them is nullptr or zero");
535     return false;
536   }
537 
538   if (str.empty())
539   {
540     std::fill(bbox, bbox + 4, 0);
541     return true;
542   }
543 
544   MetaData metaData;
545   bool result = this->PrepareMetaData(tprop, dpi, metaData);
546   if (result)
547   {
548     result = this->CalculateBoundingBox(str, metaData);
549     if (result)
550     {
551       memcpy(bbox, metaData.bbox.GetData(), sizeof(int) * 4);
552     }
553   }
554   return result;
555 }
556 
557 //----------------------------------------------------------------------------
GetMetrics(vtkTextProperty * tprop,const vtkStdString & str,int dpi,vtkTextRenderer::Metrics & metrics)558 bool vtkFreeTypeTools::GetMetrics(vtkTextProperty *tprop,
559                                   const vtkStdString &str, int dpi,
560                                   vtkTextRenderer::Metrics &metrics)
561 {
562   if (!tprop)
563   {
564     vtkErrorMacro(<< "nullptr text property.");
565     return false;
566   }
567 
568   if (str.empty())
569   {
570     metrics = vtkTextRenderer::Metrics();
571     return true;
572   }
573 
574   MetaData metaData;
575   bool result = this->PrepareMetaData(tprop, dpi, metaData);
576   if (result)
577   {
578     result = this->CalculateBoundingBox(str, metaData);
579     if (result)
580     {
581       metrics.BoundingBox = metaData.bbox;
582       metrics.TopLeft     = metaData.TL;
583       metrics.TopRight    = metaData.TR;
584       metrics.BottomLeft  = metaData.BL;
585       metrics.BottomRight = metaData.BR;
586       metrics.Ascent      = metaData.ascent;
587       metrics.Descent     = metaData.descent;
588     }
589   }
590   return result;
591 }
592 
593 //----------------------------------------------------------------------------
GetMetrics(vtkTextProperty * tprop,const vtkUnicodeString & str,int dpi,vtkTextRenderer::Metrics & metrics)594 bool vtkFreeTypeTools::GetMetrics(vtkTextProperty *tprop,
595                                   const vtkUnicodeString &str, int dpi,
596                                   vtkTextRenderer::Metrics &metrics)
597 {
598   if (!tprop)
599   {
600     vtkErrorMacro(<< "nullptr text property.");
601     return false;
602   }
603 
604   if (str.empty())
605   {
606     metrics = vtkTextRenderer::Metrics();
607     return true;
608   }
609 
610   MetaData metaData;
611   bool result = this->PrepareMetaData(tprop, dpi, metaData);
612   if (result)
613   {
614     result = this->CalculateBoundingBox(str, metaData);
615     if (result)
616     {
617       metrics.BoundingBox = metaData.bbox;
618       metrics.TopLeft     = metaData.TL;
619       metrics.TopRight    = metaData.TR;
620       metrics.BottomLeft  = metaData.BL;
621       metrics.BottomRight = metaData.BR;
622       metrics.Ascent      = metaData.ascent;
623       metrics.Descent     = metaData.descent;
624     }
625   }
626   return result;
627 }
628 
629 //----------------------------------------------------------------------------
RenderString(vtkTextProperty * tprop,const vtkStdString & str,int dpi,vtkImageData * data,int textDims[2])630 bool vtkFreeTypeTools::RenderString(vtkTextProperty *tprop,
631                                     const vtkStdString& str, int dpi,
632                                     vtkImageData *data, int textDims[2])
633 {
634   return this->RenderStringInternal(tprop, str, dpi, data, textDims);
635 }
636 
637 //----------------------------------------------------------------------------
RenderString(vtkTextProperty * tprop,const vtkUnicodeString & str,int dpi,vtkImageData * data,int textDims[2])638 bool vtkFreeTypeTools::RenderString(vtkTextProperty *tprop,
639                                     const vtkUnicodeString& str, int dpi,
640                                     vtkImageData *data, int textDims[2])
641 {
642   return this->RenderStringInternal(tprop, str, dpi, data, textDims);
643 }
644 
645 //----------------------------------------------------------------------------
StringToPath(vtkTextProperty * tprop,const vtkStdString & str,int dpi,vtkPath * path)646 bool vtkFreeTypeTools::StringToPath(vtkTextProperty *tprop,
647                                     const vtkStdString &str, int dpi,
648                                     vtkPath *path)
649 {
650   return this->StringToPathInternal(tprop, str, dpi, path);
651 }
652 
653 //----------------------------------------------------------------------------
StringToPath(vtkTextProperty * tprop,const vtkUnicodeString & str,int dpi,vtkPath * path)654 bool vtkFreeTypeTools::StringToPath(vtkTextProperty *tprop,
655                                     const vtkUnicodeString &str, int dpi,
656                                     vtkPath *path)
657 {
658   return this->StringToPathInternal(tprop, str, dpi, path);
659 }
660 
661 //----------------------------------------------------------------------------
GetConstrainedFontSize(const vtkStdString & str,vtkTextProperty * tprop,int dpi,int targetWidth,int targetHeight)662 int vtkFreeTypeTools::GetConstrainedFontSize(const vtkStdString &str,
663                                              vtkTextProperty *tprop, int dpi,
664                                              int targetWidth, int targetHeight)
665 {
666   MetaData metaData;
667   if (!this->PrepareMetaData(tprop, dpi, metaData))
668   {
669     vtkErrorMacro(<<"Could not prepare metadata.");
670     return false;
671   }
672   return this->FitStringToBBox(str, metaData, targetWidth, targetHeight);
673 }
674 
675 //----------------------------------------------------------------------------
GetConstrainedFontSize(const vtkUnicodeString & str,vtkTextProperty * tprop,int dpi,int targetWidth,int targetHeight)676 int vtkFreeTypeTools::GetConstrainedFontSize(const vtkUnicodeString &str,
677                                              vtkTextProperty *tprop, int dpi,
678                                              int targetWidth, int targetHeight)
679 {
680   MetaData metaData;
681   if (!this->PrepareMetaData(tprop, dpi, metaData))
682   {
683     vtkErrorMacro(<<"Could not prepare metadata.");
684     return false;
685   }
686   return this->FitStringToBBox(str, metaData, targetWidth, targetHeight);
687 }
688 
689 //----------------------------------------------------------------------------
HashString(const char * str)690 vtkTypeUInt16 vtkFreeTypeTools::HashString(const char *str)
691 {
692   if (str == nullptr)
693     return 0;
694 
695   vtkTypeUInt16 hash = 0;
696   while (*str != 0)
697   {
698     vtkTypeUInt8 high = ((hash<<8)^hash) >> 8;
699     vtkTypeUInt8 low = tolower(*str)^(hash<<2);
700     hash = (high<<8) ^ low;
701     ++str;
702   }
703 
704   return hash;
705 }
706 
707 //----------------------------------------------------------------------------
HashBuffer(const void * buffer,size_t n,vtkTypeUInt32 hash)708 vtkTypeUInt32 vtkFreeTypeTools::HashBuffer(const void *buffer, size_t n, vtkTypeUInt32 hash)
709 {
710   if (buffer == nullptr)
711   {
712     return 0;
713   }
714 
715   const char* key = reinterpret_cast<const char*>(buffer);
716 
717   // Jenkins hash function
718   for (size_t i = 0; i < n; ++i)
719   {
720     hash += key[i];
721     hash += (hash << 10);
722     hash += (hash << 15);
723   }
724 
725   return hash;
726 }
727 
728 //----------------------------------------------------------------------------
MapTextPropertyToId(vtkTextProperty * tprop,size_t * id)729 void vtkFreeTypeTools::MapTextPropertyToId(vtkTextProperty *tprop,
730                                            size_t *id)
731 {
732   if (!tprop || !id)
733   {
734     vtkErrorMacro(<< "Wrong parameters, one of them is nullptr");
735     return;
736   }
737 
738   // The font family is hashed into 16 bits (= 17 bits so far)
739   const char* fontFamily = tprop->GetFontFamily() != VTK_FONT_FILE
740     ? tprop->GetFontFamilyAsString()
741     : tprop->GetFontFile();
742   size_t fontFamilyLength = 0;
743   if (fontFamily)
744   {
745     fontFamilyLength = strlen(fontFamily);
746   }
747   vtkTypeUInt32 hash =
748     vtkFreeTypeTools::HashBuffer(fontFamily, fontFamilyLength);
749 
750   // Create a "string" of text properties
751   unsigned char ucValue = tprop->GetBold();
752   hash = vtkFreeTypeTools::HashBuffer(&ucValue, sizeof(unsigned char), hash);
753   ucValue = tprop->GetItalic();
754   hash = vtkFreeTypeTools::HashBuffer(&ucValue, sizeof(unsigned char), hash);
755   ucValue = tprop->GetShadow();
756   hash = vtkFreeTypeTools::HashBuffer(&ucValue, sizeof(unsigned char), hash);
757   hash = vtkFreeTypeTools::HashBuffer(
758     tprop->GetColor(), 3*sizeof(double), hash);
759   double dValue = tprop->GetOpacity();
760   hash = vtkFreeTypeTools::HashBuffer(&dValue, sizeof(double), hash);
761   hash = vtkFreeTypeTools::HashBuffer(
762     tprop->GetBackgroundColor(), 3*sizeof(double), hash);
763   dValue = tprop->GetBackgroundOpacity();
764   hash = vtkFreeTypeTools::HashBuffer(&dValue, sizeof(double), hash);
765   hash = vtkFreeTypeTools::HashBuffer(
766     tprop->GetFrameColor(), 3*sizeof(double), hash);
767   ucValue = tprop->GetFrame();
768   hash = vtkFreeTypeTools::HashBuffer(&ucValue, sizeof(unsigned char), hash);
769   int iValue = tprop->GetFrameWidth();
770   hash = vtkFreeTypeTools::HashBuffer(&iValue, sizeof(int), hash);
771   iValue = tprop->GetFontSize();
772   hash = vtkFreeTypeTools::HashBuffer(&iValue, sizeof(int), hash);
773   hash = vtkFreeTypeTools::HashBuffer(
774     tprop->GetShadowOffset(), 2*sizeof(int), hash);
775   dValue = tprop->GetOrientation();
776   hash = vtkFreeTypeTools::HashBuffer(&dValue, sizeof(double), hash);
777   hash = vtkFreeTypeTools::HashBuffer(&dValue, sizeof(double), hash);
778   dValue = tprop->GetLineSpacing();
779   hash = vtkFreeTypeTools::HashBuffer(&dValue, sizeof(double), hash);
780   dValue = tprop->GetLineOffset();
781   hash = vtkFreeTypeTools::HashBuffer(&dValue, sizeof(double), hash);
782   iValue = tprop->GetUseTightBoundingBox();
783   hash = vtkFreeTypeTools::HashBuffer(&iValue, sizeof(int), hash);
784 
785   // Set the first bit to avoid id = 0
786   // (the id will be mapped to a pointer, FTC_FaceID, so let's avoid nullptr)
787   *id = 1;
788 
789   // Add in the hash.
790   // We're dropping a bit here, but that should be okay.
791   *id |= hash << 1;
792 
793   // Insert the TextProperty into the lookup table
794   if (!this->TextPropertyLookup->contains(*id))
795     (*this->TextPropertyLookup)[*id] = tprop;
796 }
797 
798 //----------------------------------------------------------------------------
MapIdToTextProperty(size_t id,vtkTextProperty * tprop)799 void vtkFreeTypeTools::MapIdToTextProperty(size_t id,
800                                            vtkTextProperty *tprop)
801 {
802   if (!tprop)
803   {
804     vtkErrorMacro(<< "Wrong parameters, one of them is nullptr");
805     return;
806   }
807 
808   vtkTextPropertyLookup::const_iterator tpropIt =
809       this->TextPropertyLookup->find(id);
810 
811   if (tpropIt == this->TextPropertyLookup->end())
812   {
813     vtkErrorMacro(<<"Unknown id; call MapTextPropertyToId first!");
814     return;
815   }
816 
817   tprop->ShallowCopy(tpropIt->second);
818 }
819 
820 //----------------------------------------------------------------------------
GetSize(size_t tprop_cache_id,int font_size,FT_Size * size)821 bool vtkFreeTypeTools::GetSize(size_t tprop_cache_id,
822                                int font_size,
823                                FT_Size *size)
824 {
825   if (!size || font_size <= 0)
826   {
827     vtkErrorMacro(<< "Wrong parameters, size is nullptr or invalid font size");
828     return 0;
829   }
830 
831   // Map the id of a text property in the cache to a FTC_FaceID
832   FTC_FaceID face_id = reinterpret_cast<FTC_FaceID>(tprop_cache_id);
833 
834   FTC_ScalerRec scaler_rec;
835   scaler_rec.face_id = face_id;
836   scaler_rec.width = font_size;
837   scaler_rec.height = font_size;
838   scaler_rec.pixel = 1;
839 
840   return this->GetSize(&scaler_rec, size);
841 }
842 
843 //----------------------------------------------------------------------------
GetSize(FTC_Scaler scaler,FT_Size * size)844 bool vtkFreeTypeTools::GetSize(FTC_Scaler scaler, FT_Size *size)
845 {
846 #if VTK_FTFC_DEBUG_CD
847   printf("vtkFreeTypeTools::GetSize()\n");
848 #endif
849 
850   if (!size)
851   {
852     vtkErrorMacro(<< "Size is nullptr.");
853     return 0;
854   }
855 
856   FTC_Manager *manager = this->GetCacheManager();
857   if (!manager)
858   {
859     vtkErrorMacro(<< "Failed querying the cache manager !");
860     return 0;
861   }
862 
863   FT_Error error = FTC_Manager_LookupSize(*manager, scaler, size);
864   if (error)
865   {
866     vtkErrorMacro(<< "Failed looking up a FreeType Size");
867   }
868 
869   return error ? false : true;
870 }
871 
872 //----------------------------------------------------------------------------
GetSize(vtkTextProperty * tprop,FT_Size * size)873 bool vtkFreeTypeTools::GetSize(vtkTextProperty *tprop,
874                                FT_Size *size)
875 {
876   if (!tprop)
877   {
878     vtkErrorMacro(<< "Wrong parameters, text property is nullptr");
879     return 0;
880   }
881 
882   // Map the text property to a unique id that will be used as face id
883   size_t tprop_cache_id;
884   this->MapTextPropertyToId(tprop, &tprop_cache_id);
885 
886   return this->GetSize(tprop_cache_id, tprop->GetFontSize(), size);
887 }
888 
889 //----------------------------------------------------------------------------
GetFace(size_t tprop_cache_id,FT_Face * face)890 bool vtkFreeTypeTools::GetFace(size_t tprop_cache_id,
891                                FT_Face *face)
892 {
893 #if VTK_FTFC_DEBUG_CD
894   printf("vtkFreeTypeTools::GetFace()\n");
895 #endif
896 
897   if (!face)
898   {
899     vtkErrorMacro(<< "Wrong parameters, face is nullptr");
900     return false;
901   }
902 
903   FTC_Manager *manager = this->GetCacheManager();
904   if (!manager)
905   {
906     vtkErrorMacro(<< "Failed querying the cache manager !");
907     return false;
908   }
909 
910   // Map the id of a text property in the cache to a FTC_FaceID
911   FTC_FaceID face_id = reinterpret_cast<FTC_FaceID>(tprop_cache_id);
912 
913   FT_Error error = FTC_Manager_LookupFace(*manager, face_id, face);
914   if (error)
915   {
916     vtkErrorMacro(<< "Failed looking up a FreeType Face");
917   }
918 
919   return error ? false : true;
920 }
921 
922 //----------------------------------------------------------------------------
GetFace(vtkTextProperty * tprop,FT_Face * face)923 bool vtkFreeTypeTools::GetFace(vtkTextProperty *tprop,
924                                FT_Face *face)
925 {
926   if (!tprop)
927   {
928     vtkErrorMacro(<< "Wrong parameters, face is nullptr");
929     return 0;
930   }
931 
932   // Map the text property to a unique id that will be used as face id
933   size_t tprop_cache_id;
934   this->MapTextPropertyToId(tprop, &tprop_cache_id);
935 
936   return this->GetFace(tprop_cache_id, face);
937 }
938 
939 //----------------------------------------------------------------------------
GetGlyphIndex(size_t tprop_cache_id,FT_UInt32 c,FT_UInt * gindex)940 bool vtkFreeTypeTools::GetGlyphIndex(size_t tprop_cache_id,
941                                      FT_UInt32 c,
942                                      FT_UInt *gindex)
943 {
944 #if VTK_FTFC_DEBUG_CD
945   printf("vtkFreeTypeTools::GetGlyphIndex()\n");
946 #endif
947 
948   if (!gindex)
949   {
950     vtkErrorMacro(<< "Wrong parameters, gindex is nullptr");
951     return 0;
952   }
953 
954   FTC_CMapCache *cmap_cache = this->GetCMapCache();
955   if (!cmap_cache)
956   {
957     vtkErrorMacro(<< "Failed querying the charmap cache manager !");
958     return 0;
959   }
960 
961   // Map the id of a text property in the cache to a FTC_FaceID
962   FTC_FaceID face_id = reinterpret_cast<FTC_FaceID>(tprop_cache_id);
963 
964   // Lookup the glyph index
965   *gindex = FTC_CMapCache_Lookup(*cmap_cache, face_id, 0, c);
966 
967   return *gindex ? true : false;
968 }
969 
970 //----------------------------------------------------------------------------
GetGlyphIndex(vtkTextProperty * tprop,FT_UInt32 c,FT_UInt * gindex)971 bool vtkFreeTypeTools::GetGlyphIndex(vtkTextProperty *tprop,
972                                      FT_UInt32 c,
973                                      FT_UInt *gindex)
974 {
975   if (!tprop)
976   {
977     vtkErrorMacro(<< "Wrong parameters, text property is nullptr");
978     return 0;
979   }
980 
981   // Map the text property to a unique id that will be used as face id
982   size_t tprop_cache_id;
983   this->MapTextPropertyToId(tprop, &tprop_cache_id);
984 
985   return this->GetGlyphIndex(tprop_cache_id, c, gindex);
986 }
987 
988 //----------------------------------------------------------------------------
GetGlyph(size_t tprop_cache_id,int font_size,FT_UInt gindex,FT_Glyph * glyph,int request)989 bool vtkFreeTypeTools::GetGlyph(size_t tprop_cache_id,
990                                 int font_size,
991                                 FT_UInt gindex,
992                                 FT_Glyph *glyph,
993                                 int request)
994 {
995 #if VTK_FTFC_DEBUG_CD
996   printf("vtkFreeTypeTools::GetGlyph()\n");
997 #endif
998 
999   if (!glyph)
1000   {
1001     vtkErrorMacro(<< "Wrong parameters, one of them is nullptr");
1002     return false;
1003   }
1004 
1005   FTC_ImageCache *image_cache = this->GetImageCache();
1006   if (!image_cache)
1007   {
1008     vtkErrorMacro(<< "Failed querying the image cache manager !");
1009     return false;
1010   }
1011 
1012   // Map the id of a text property in the cache to a FTC_FaceID
1013   FTC_FaceID face_id = reinterpret_cast<FTC_FaceID>(tprop_cache_id);
1014 
1015   // Which font are we looking for
1016   FTC_ImageTypeRec image_type_rec;
1017   image_type_rec.face_id = face_id;
1018   image_type_rec.width = font_size;
1019   image_type_rec.height = font_size;
1020   image_type_rec.flags = FT_LOAD_DEFAULT;
1021   if (request == GLYPH_REQUEST_BITMAP)
1022   {
1023     image_type_rec.flags |= FT_LOAD_RENDER;
1024   }
1025   else if (request == GLYPH_REQUEST_OUTLINE)
1026   {
1027     image_type_rec.flags |= FT_LOAD_NO_BITMAP;
1028   }
1029 
1030   // Lookup the glyph
1031   FT_Error error = FTC_ImageCache_Lookup(
1032     *image_cache, &image_type_rec, gindex, glyph, nullptr);
1033 
1034   return error ? false : true;
1035 }
1036 
1037 //----------------------------------------------------------------------------
GetGlyph(FTC_Scaler scaler,FT_UInt gindex,FT_Glyph * glyph,int request)1038 bool vtkFreeTypeTools::GetGlyph(FTC_Scaler scaler, FT_UInt gindex,
1039                                 FT_Glyph *glyph, int request)
1040 {
1041 #if VTK_FTFC_DEBUG_CD
1042   printf("vtkFreeTypeTools::GetGlyph()\n");
1043 #endif
1044 
1045   if (!glyph)
1046   {
1047     vtkErrorMacro(<< "Wrong parameters, one of them is nullptr");
1048     return false;
1049   }
1050 
1051   FTC_ImageCache *image_cache = this->GetImageCache();
1052   if (!image_cache)
1053   {
1054     vtkErrorMacro(<< "Failed querying the image cache manager !");
1055     return false;
1056   }
1057 
1058   FT_ULong loadFlags = FT_LOAD_DEFAULT;
1059   if (request == GLYPH_REQUEST_BITMAP)
1060   {
1061     loadFlags |= FT_LOAD_RENDER;
1062   }
1063   else if (request == GLYPH_REQUEST_OUTLINE)
1064   {
1065     loadFlags |= FT_LOAD_NO_BITMAP;
1066   }
1067 
1068   // Lookup the glyph
1069   FT_Error error = FTC_ImageCache_LookupScaler(
1070         *image_cache, scaler, loadFlags, gindex, glyph, nullptr);
1071 
1072   return error ? false : true;
1073 }
1074 
1075 //----------------------------------------------------------------------------
LookupFace(vtkTextProperty * tprop,FT_Library lib,FT_Face * face)1076 bool vtkFreeTypeTools::LookupFace(vtkTextProperty *tprop, FT_Library lib,
1077                                   FT_Face *face)
1078 {
1079   // Fonts, organized by [Family][Bold][Italic]
1080   static EmbeddedFontStruct EmbeddedFonts[3][2][2] =
1081     {
1082     {
1083       {
1084         { // VTK_ARIAL: Bold [ ] Italic [ ]
1085             face_arial_buffer_length, face_arial_buffer
1086         },
1087         { // VTK_ARIAL: Bold [ ] Italic [x]
1088             face_arial_italic_buffer_length, face_arial_italic_buffer
1089         }
1090       },
1091       {
1092         { // VTK_ARIAL: Bold [x] Italic [ ]
1093             face_arial_bold_buffer_length, face_arial_bold_buffer
1094         },
1095         { // VTK_ARIAL: Bold [x] Italic [x]
1096             face_arial_bold_italic_buffer_length, face_arial_bold_italic_buffer
1097         }
1098       }
1099     },
1100     {
1101       {
1102         { // VTK_COURIER: Bold [ ] Italic [ ]
1103             face_courier_buffer_length, face_courier_buffer
1104         },
1105         { // VTK_COURIER: Bold [ ] Italic [x]
1106             face_courier_italic_buffer_length, face_courier_italic_buffer
1107         }
1108       },
1109       {
1110         { // VTK_COURIER: Bold [x] Italic [ ]
1111             face_courier_bold_buffer_length, face_courier_bold_buffer
1112         },
1113         { // VTK_COURIER: Bold [x] Italic [x]
1114             face_courier_bold_italic_buffer_length,
1115             face_courier_bold_italic_buffer
1116         }
1117       }
1118     },
1119     {
1120       {
1121         { // VTK_TIMES: Bold [ ] Italic [ ]
1122             face_times_buffer_length, face_times_buffer
1123         },
1124         { // VTK_TIMES: Bold [ ] Italic [x]
1125             face_times_italic_buffer_length, face_times_italic_buffer
1126         }
1127       },
1128       {
1129         { // VTK_TIMES: Bold [x] Italic [ ]
1130             face_times_bold_buffer_length, face_times_bold_buffer
1131         },
1132         { // VTK_TIMES: Bold [x] Italic [x]
1133             face_times_bold_italic_buffer_length, face_times_bold_italic_buffer
1134         }
1135       }
1136     }
1137     };
1138 
1139   int family = tprop->GetFontFamily();
1140   // If font family is unknown, fall back to Arial.
1141   if (family == VTK_UNKNOWN_FONT)
1142   {
1143     vtkDebugWithObjectMacro(
1144           tprop,
1145           << "Requested font '" << tprop->GetFontFamilyAsString() << "'"
1146           " unavailable. Substituting Arial.");
1147     family = VTK_ARIAL;
1148   }
1149   else if (family == VTK_FONT_FILE)
1150   {
1151     vtkDebugWithObjectMacro(tprop,
1152                             << "Attempting to load font from file: "
1153                             << tprop->GetFontFile());
1154 
1155     if (FT_New_Face(lib, tprop->GetFontFile(), 0, face) == 0)
1156     {
1157       return true;
1158     }
1159 
1160     vtkDebugWithObjectMacro(
1161           tprop,
1162           << "Error loading font from file '" << tprop->GetFontFile()
1163           << "'. Falling back to arial.");
1164     family = VTK_ARIAL;
1165   }
1166 
1167   FT_Long length = EmbeddedFonts
1168     [family][tprop->GetBold()][tprop->GetItalic()].length;
1169   FT_Byte *ptr = EmbeddedFonts
1170     [family][tprop->GetBold()][tprop->GetItalic()].ptr;
1171 
1172   // Create a new face from the embedded fonts if possible
1173   FT_Error error = FT_New_Memory_Face(lib, ptr, length, 0, face);
1174 
1175   if (error)
1176   {
1177     vtkErrorWithObjectMacro(
1178           tprop,
1179           << "Unable to create font !" << " (family: " << family
1180           << ", bold: " << tprop->GetBold() << ", italic: " << tprop->GetItalic()
1181           << ", length: " << length << ")");
1182     return false;
1183   }
1184   else
1185   {
1186 #if VTK_FTFC_DEBUG
1187     cout << "Requested: " << *face
1188          << " (F: " << tprop->GetFontFamily()
1189          << ", B: " << tprop->GetBold()
1190          << ", I: " << tprop->GetItalic()
1191          << ", O: " << tprop->GetOrientation() << ")" << endl;
1192 #endif
1193   }
1194 
1195   return true;
1196 }
1197 
1198 //----------------------------------------------------------------------------
GetGlyph(vtkTextProperty * tprop,FT_UInt32 c,FT_Glyph * glyph,int request)1199 bool vtkFreeTypeTools::GetGlyph(vtkTextProperty *tprop,
1200                                 FT_UInt32 c,
1201                                 FT_Glyph *glyph,
1202                                 int request)
1203 {
1204   if (!tprop)
1205   {
1206     vtkErrorMacro(<< "Wrong parameters, text property is nullptr");
1207     return 0;
1208   }
1209 
1210   // Map the text property to a unique id that will be used as face id
1211   size_t tprop_cache_id;
1212   this->MapTextPropertyToId(tprop, &tprop_cache_id);
1213 
1214   // Get the character/glyph index
1215   FT_UInt gindex;
1216   if (!this->GetGlyphIndex(tprop_cache_id, c, &gindex))
1217   {
1218     vtkErrorMacro(<< "Failed querying a glyph index");
1219     return false;
1220   }
1221 
1222   // Get the glyph
1223   return this->GetGlyph(
1224     tprop_cache_id, tprop->GetFontSize(), gindex, glyph, request);
1225 }
1226 
1227 //----------------------------------------------------------------------------
PrintSelf(ostream & os,vtkIndent indent)1228 void vtkFreeTypeTools::PrintSelf(ostream& os, vtkIndent indent)
1229 {
1230   this->Superclass::PrintSelf(os,indent);
1231 
1232   os << indent << "MaximumNumberOfFaces: "
1233      << this->MaximumNumberOfFaces << endl;
1234 
1235   os << indent << "MaximumNumberOfSizes: "
1236      << this->MaximumNumberOfSizes << endl;
1237 
1238   os << indent << "MaximumNumberOfBytes: "
1239      << this->MaximumNumberOfBytes << endl;
1240 
1241   os << indent << "Scale to nearest power of 2 for image sizes: "
1242      << this->ScaleToPowerTwo << endl;
1243 }
1244 
1245 //----------------------------------------------------------------------------
CreateFTCManager()1246 FT_Error vtkFreeTypeTools::CreateFTCManager()
1247 {
1248   return FTC_Manager_New(*this->GetLibrary(),
1249                          this->MaximumNumberOfFaces,
1250                          this->MaximumNumberOfSizes,
1251                          this->MaximumNumberOfBytes,
1252                          vtkFreeTypeToolsFaceRequester,
1253                          static_cast<FT_Pointer>(this),
1254                          this->CacheManager);
1255 }
1256 
1257 //----------------------------------------------------------------------------
PrepareImageMetaData(vtkTextProperty * tprop,vtkImageData * image,ImageMetaData & metaData)1258 inline bool vtkFreeTypeTools::PrepareImageMetaData(vtkTextProperty *tprop,
1259                                                    vtkImageData *image,
1260                                                    ImageMetaData &metaData)
1261 {
1262   // Image properties
1263   image->GetIncrements(metaData.imageIncrements);
1264   image->GetDimensions(metaData.imageDimensions);
1265 
1266   double color[3];
1267   tprop->GetColor(color);
1268   metaData.rgba[0] = static_cast<unsigned char>(color[0] * 255);
1269   metaData.rgba[1] = static_cast<unsigned char>(color[1] * 255);
1270   metaData.rgba[2] = static_cast<unsigned char>(color[2] * 255);
1271   metaData.rgba[3] = static_cast<unsigned char>(tprop->GetOpacity() * 255);
1272 
1273   return true;
1274 }
1275 
1276 //----------------------------------------------------------------------------
PrepareMetaData(vtkTextProperty * tprop,int dpi,MetaData & metaData)1277 inline bool vtkFreeTypeTools::PrepareMetaData(vtkTextProperty *tprop, int dpi,
1278                                               MetaData &metaData)
1279 {
1280   // Text properties
1281   metaData.textProperty = tprop;
1282   this->MapTextPropertyToId(tprop, &metaData.textPropertyCacheId);
1283 
1284   metaData.scaler.face_id =
1285       reinterpret_cast<FTC_FaceID>(metaData.textPropertyCacheId);
1286   metaData.scaler.width = tprop->GetFontSize() * 64; // 26.6 format point size
1287   metaData.scaler.height = tprop->GetFontSize() * 64;
1288   metaData.scaler.pixel = 0;
1289   metaData.scaler.x_res = dpi;
1290   metaData.scaler.y_res = dpi;
1291 
1292   FT_Size size;
1293   if (!this->GetSize(&metaData.scaler, &size))
1294   {
1295     return false;
1296   }
1297 
1298   metaData.face = size->face;
1299   metaData.faceHasKerning = (FT_HAS_KERNING(metaData.face) != 0);
1300 
1301   // Store an unrotated version of this font, as we'll need this to get accurate
1302   // ascenders/descenders (see CalculateBoundingBox).
1303   if (tprop->GetOrientation() != 0.0)
1304   {
1305     vtkNew<vtkTextProperty> unrotatedTProp;
1306     unrotatedTProp->ShallowCopy(tprop);
1307     unrotatedTProp->SetOrientation(0);
1308     this->MapTextPropertyToId(unrotatedTProp,
1309                               &metaData.unrotatedTextPropertyCacheId);
1310 
1311     metaData.unrotatedScaler.face_id =
1312         reinterpret_cast<FTC_FaceID>(metaData.unrotatedTextPropertyCacheId);
1313     metaData.unrotatedScaler.width = tprop->GetFontSize() * 64; // 26.6 format point size
1314     metaData.unrotatedScaler.height = tprop->GetFontSize() * 64;
1315     metaData.unrotatedScaler.pixel = 0;
1316     metaData.unrotatedScaler.x_res = dpi;
1317     metaData.unrotatedScaler.y_res = dpi;
1318   }
1319   else
1320   {
1321     metaData.unrotatedTextPropertyCacheId = metaData.textPropertyCacheId;
1322     metaData.unrotatedScaler = metaData.scaler;
1323   }
1324 
1325   // Rotation matrices:
1326   metaData.faceIsRotated =
1327       (fabs(metaData.textProperty->GetOrientation()) > 1e-5);
1328   if (metaData.faceIsRotated)
1329   {
1330     float angle = vtkMath::RadiansFromDegrees(
1331           static_cast<float>(metaData.textProperty->GetOrientation()));
1332     // 0 -> orientation (used to adjust kerning, PR#15301)
1333     float c = cos(angle);
1334     float s = sin(angle);
1335     metaData.rotation.xx = (FT_Fixed)( c * 0x10000L);
1336     metaData.rotation.xy = (FT_Fixed)(-s * 0x10000L);
1337     metaData.rotation.yx = (FT_Fixed)( s * 0x10000L);
1338     metaData.rotation.yy = (FT_Fixed)( c * 0x10000L);
1339 
1340     // orientation -> 0 (used for width calculations)
1341     c = cos(-angle);
1342     s = sin(-angle);
1343     metaData.inverseRotation.xx = (FT_Fixed)( c * 0x10000L);
1344     metaData.inverseRotation.xy = (FT_Fixed)(-s * 0x10000L);
1345     metaData.inverseRotation.yx = (FT_Fixed)( s * 0x10000L);
1346     metaData.inverseRotation.yy = (FT_Fixed)( c * 0x10000L);
1347   }
1348 
1349   return true;
1350 }
1351 
1352 //----------------------------------------------------------------------------
1353 template <typename StringType>
RenderStringInternal(vtkTextProperty * tprop,const StringType & str,int dpi,vtkImageData * data,int textDims[2])1354 bool vtkFreeTypeTools::RenderStringInternal(vtkTextProperty *tprop,
1355                                             const StringType &str,
1356                                             int dpi,
1357                                             vtkImageData *data,
1358                                             int textDims[2])
1359 {
1360   // Check parameters
1361   if (!tprop || !data)
1362   {
1363     vtkErrorMacro(<< "Wrong parameters, one of them is nullptr or zero");
1364     return false;
1365   }
1366 
1367   if (data->GetNumberOfScalarComponents() > 4)
1368   {
1369     vtkErrorMacro("The image data must have a maximum of four components");
1370     return false;
1371   }
1372 
1373   if (str.empty())
1374   {
1375     data->Initialize();
1376     if (textDims)
1377     {
1378       textDims[0] = 0;
1379       textDims[1] = 0;
1380     }
1381     return true;
1382   }
1383 
1384   ImageMetaData metaData;
1385 
1386   // Setup the metadata cache
1387   if (!this->PrepareMetaData(tprop, dpi, metaData))
1388   {
1389     vtkErrorMacro(<<"Error prepare text metadata.");
1390     return false;
1391   }
1392 
1393   // Calculate the bounding box.
1394   if (!this->CalculateBoundingBox(str, metaData))
1395   {
1396     vtkErrorMacro(<<"Could not get a valid bounding box.");
1397     return false;
1398   }
1399 
1400   // Calculate the text dimensions:
1401   if (textDims)
1402   {
1403     textDims[0] = metaData.bbox[1] - metaData.bbox[0] + 1;
1404     textDims[1] = metaData.bbox[3] - metaData.bbox[2] + 1;
1405   }
1406 
1407   // Prepare the ImageData to receive the text
1408   this->PrepareImageData(data, metaData.bbox.GetData());
1409 
1410   // Setup the image metadata
1411   if (!this->PrepareImageMetaData(tprop, data, metaData))
1412   {
1413     vtkErrorMacro(<<"Error prepare image metadata.");
1414     return false;
1415   }
1416 
1417   // Render the background:
1418   this->RenderBackground(tprop, data, metaData);
1419 
1420   // Render shadow if needed
1421   if (metaData.textProperty->GetShadow())
1422   {
1423     // Modify the line offsets with the shadow offset
1424     vtkVector2i shadowOffset;
1425     metaData.textProperty->GetShadowOffset(shadowOffset.GetData());
1426     std::vector<MetaData::LineMetrics> origMetrics = metaData.lineMetrics;
1427     metaData.lineMetrics.clear();
1428     for (std::vector<MetaData::LineMetrics>::const_iterator
1429          it = origMetrics.begin(), itEnd = origMetrics.end(); it < itEnd; ++it)
1430     {
1431       MetaData::LineMetrics line = *it;
1432       line.origin = line.origin + shadowOffset;
1433       metaData.lineMetrics.push_back(line);
1434     }
1435 
1436     // Set the color
1437     unsigned char origColor[3] = {metaData.rgba[0], metaData.rgba[1],
1438                                   metaData.rgba[2]};
1439     double shadowColor[3];
1440     metaData.textProperty->GetShadowColor(shadowColor);
1441     metaData.rgba[0] = static_cast<unsigned char>(shadowColor[0] * 255);
1442     metaData.rgba[1] = static_cast<unsigned char>(shadowColor[1] * 255);
1443     metaData.rgba[2] = static_cast<unsigned char>(shadowColor[2] * 255);
1444 
1445     if (!this->PopulateData(str, data, metaData))
1446     {
1447       vtkErrorMacro(<<"Error rendering shadow");
1448       return false;
1449     }
1450 
1451     // Restore color and line metrics
1452     metaData.lineMetrics = origMetrics;
1453     memcpy(metaData.rgba, origColor, 3 * sizeof(unsigned char));
1454   }
1455 
1456   // Mark the image data as modified, as it is possible that only
1457   // vtkImageData::Get*Pointer methods will be called, which do not update the
1458   // MTime.
1459   data->Modified();
1460 
1461   // Render image
1462   if (!this->PopulateData(str, data, metaData))
1463   {
1464     vtkErrorMacro(<<"Error rendering text.");
1465     return false;
1466   }
1467 
1468   // Draw a red dot at the anchor point:
1469   if (this->DebugTextures)
1470   {
1471     unsigned char *ptr =
1472         static_cast<unsigned char *>(data->GetScalarPointer(0, 0, 0));
1473     if (ptr)
1474     {
1475       ptr[0] = 255;
1476       ptr[1] = 0;
1477       ptr[2] = 0;
1478       ptr[3] = 255;
1479     }
1480   }
1481 
1482   return true;
1483 }
1484 
1485 //----------------------------------------------------------------------------
1486 template <typename StringType>
StringToPathInternal(vtkTextProperty * tprop,const StringType & str,int dpi,vtkPath * path)1487 bool vtkFreeTypeTools::StringToPathInternal(vtkTextProperty *tprop,
1488                                             const StringType &str,
1489                                             int dpi,
1490                                             vtkPath *path)
1491 {
1492   // Setup the metadata
1493   MetaData metaData;
1494   if (!this->PrepareMetaData(tprop, dpi, metaData))
1495   {
1496     vtkErrorMacro(<<"Could not prepare metadata.");
1497     return false;
1498   }
1499 
1500   // Layout the text, calculate bounding box
1501   if (!this->CalculateBoundingBox(str, metaData))
1502   {
1503     vtkErrorMacro(<<"Could not calculate bounding box.");
1504     return false;
1505   }
1506 
1507   // Create the path
1508   if (!this->PopulateData(str, path, metaData))
1509   {
1510     vtkErrorMacro(<<"Could not populate path.");
1511     return false;
1512   }
1513 
1514   return true;
1515 }
1516 
1517 namespace
1518 {
1519 const char* DEFAULT_HEIGHT_STRING = "_/7Agfy";
1520 }
1521 
1522 //----------------------------------------------------------------------------
CalculateBoundingBox(const vtkUnicodeString & str,MetaData & metaData)1523 bool vtkFreeTypeTools::CalculateBoundingBox(const vtkUnicodeString& str, MetaData &metaData)
1524 {
1525   return CalculateBoundingBox(str, metaData, vtkUnicodeString::from_utf8(DEFAULT_HEIGHT_STRING));
1526 }
1527 
1528 //----------------------------------------------------------------------------
CalculateBoundingBox(const vtkStdString & str,MetaData & metaData)1529 bool vtkFreeTypeTools::CalculateBoundingBox(const vtkStdString& str, MetaData &metaData)
1530 {
1531   return CalculateBoundingBox(str, metaData, vtkStdString(DEFAULT_HEIGHT_STRING));
1532 }
1533 
1534 //----------------------------------------------------------------------------
1535 template <typename T>
CalculateBoundingBox(const T & str,MetaData & metaData,const T & defaultHeightString)1536 bool vtkFreeTypeTools::CalculateBoundingBox(const T& str,
1537                                             MetaData &metaData, const T& defaultHeightString)
1538 {
1539   // Calculate the metrics for each line. These will be used to calculate
1540   // a bounding box, but first we need to know the maximum line length to
1541   // get justification right.
1542   metaData.lineMetrics.clear();
1543   metaData.maxLineWidth = 0;
1544 
1545   // Go through the string, line by line, and build the metrics data.
1546   typename T::const_iterator beginLine = str.begin();
1547   typename T::const_iterator endLine = std::find(beginLine, str.end(), '\n');
1548   while (endLine != str.end())
1549   {
1550     metaData.lineMetrics.push_back(MetaData::LineMetrics());
1551     this->GetLineMetrics(beginLine, endLine, metaData,
1552                          metaData.lineMetrics.back().width,
1553                          &metaData.lineMetrics.back().xmin);
1554     metaData.maxLineWidth = std::max(metaData.maxLineWidth,
1555                                      metaData.lineMetrics.back().width);
1556     beginLine = endLine;
1557     ++beginLine;
1558     endLine = std::find(beginLine, str.end(), '\n');
1559   }
1560   // Last line...
1561   metaData.lineMetrics.push_back(MetaData::LineMetrics());
1562   this->GetLineMetrics(beginLine, endLine, metaData,
1563                        metaData.lineMetrics.back().width,
1564                        &metaData.lineMetrics.back().xmin);
1565   metaData.maxLineWidth = std::max(metaData.maxLineWidth,
1566                                    metaData.lineMetrics.back().width);
1567 
1568   int numLines = metaData.lineMetrics.size();
1569   T heightString;
1570   if (metaData.textProperty->GetUseTightBoundingBox() && numLines == 1)
1571   {
1572     // Calculate line height from actual characters. This works only for single line text
1573     // and may result in a height that does not include descent. It is used to get
1574     // a centered label.
1575     heightString = str;
1576   }
1577   else
1578   {
1579     // Calculate line height from a reference set of characters, since the global
1580     // face values are usually way too big.
1581     heightString = defaultHeightString;
1582   }
1583   int ascent = 0;
1584   int descent = 0;
1585   typename T::const_iterator it = heightString.begin();
1586   while (it != heightString.end())
1587   {
1588     FT_BitmapGlyph bitmapGlyph;
1589     FT_UInt glyphIndex;
1590     // Use the unrotated face to get correct metrics:
1591     FT_Bitmap *bitmap = this->GetBitmap(
1592           *it, &metaData.unrotatedScaler, glyphIndex, bitmapGlyph);
1593     if (bitmap)
1594     {
1595       ascent = std::max(bitmapGlyph->top, ascent);
1596       descent = std::min(-static_cast<int>((bitmap->rows -
1597                                             bitmapGlyph->top - 1)),
1598                                             descent);
1599     }
1600     ++it;
1601   }
1602   // Set line height. Descent is negative.
1603   metaData.height = ascent - descent + 1;
1604 
1605   // The unrotated height of the text
1606   int interLineSpacing = (metaData.textProperty->GetLineSpacing() - 1) * metaData.height;
1607   int fullHeight = numLines * metaData.height +
1608                    (numLines - 1) * interLineSpacing +
1609                    metaData.textProperty->GetLineOffset();
1610 
1611   // Will we be rendering a background?
1612   bool hasBackground = (static_cast<unsigned char>(
1613         metaData.textProperty->GetBackgroundOpacity() * 255) > 0);
1614   bool hasFrame = metaData.textProperty->GetFrame() && metaData.textProperty->GetFrameWidth() > 0;
1615   int padWidth = hasFrame ? 1 + metaData.textProperty->GetFrameWidth() : 2;
1616 
1617   int pad = (hasBackground || hasFrame) ? padWidth : 0; // pixels on each side.
1618 
1619   // sin, cos of orientation
1620   float angle = vtkMath::RadiansFromDegrees(
1621         metaData.textProperty->GetOrientation());
1622   float c = cos(angle);
1623   float s = sin(angle);
1624 
1625   // The width and height of the text + background/frame, as rotated vectors:
1626   metaData.dx = vtkVector2i(metaData.maxLineWidth + 2 * pad, 0);
1627   metaData.dy = vtkVector2i(0, fullHeight + 2 * pad);
1628   rotateVector2i(metaData.dx, s, c);
1629   rotateVector2i(metaData.dy, s, c);
1630 
1631   // Rotate the ascent/descent:
1632   metaData.ascent = vtkVector2i(0, ascent);
1633   metaData.descent = vtkVector2i(0, descent);
1634   rotateVector2i(metaData.ascent, s, c);
1635   rotateVector2i(metaData.descent, s, c);
1636 
1637   // The rotated padding on the text's vertical and horizontal axes:
1638   vtkVector2i hPad(pad, 0);
1639   vtkVector2i vPad(0, pad);
1640   vtkVector2i hOne(1, 0);
1641   vtkVector2i vOne(0, 1);
1642   rotateVector2i(hPad, s, c);
1643   rotateVector2i(vPad, s, c);
1644   rotateVector2i(hOne, s, c);
1645   rotateVector2i(vOne, s, c);
1646 
1647   // Calculate the bottom left corner of the data rect. Start at anchor point
1648   // (0, 0) and subtract out justification. Account for background/frame padding to
1649   // ensure that we're aligning to the text, not the background/frame.
1650   metaData.BL = vtkVector2i(0, 0);
1651   switch (metaData.textProperty->GetJustification())
1652   {
1653     case VTK_TEXT_CENTERED:
1654       metaData.BL = metaData.BL - (metaData.dx * 0.5);
1655       break;
1656     case VTK_TEXT_RIGHT:
1657       metaData.BL = metaData.BL - metaData.dx + hPad + hOne;
1658       break;
1659     case VTK_TEXT_LEFT:
1660       metaData.BL = metaData.BL - hPad;
1661       break;
1662     default:
1663       vtkErrorMacro(<< "Bad horizontal alignment flag: "
1664                     << metaData.textProperty->GetJustification());
1665       break;
1666   }
1667   switch (metaData.textProperty->GetVerticalJustification())
1668   {
1669     case VTK_TEXT_CENTERED:
1670       metaData.BL = metaData.BL - (metaData.dy * 0.5);
1671       break;
1672     case VTK_TEXT_BOTTOM:
1673       metaData.BL = metaData.BL - vPad;
1674       break;
1675     case VTK_TEXT_TOP:
1676       metaData.BL = metaData.BL - metaData.dy + vPad + vOne;
1677       break;
1678     default:
1679       vtkErrorMacro(<< "Bad vertical alignment flag: "
1680                     << metaData.textProperty->GetVerticalJustification());
1681       break;
1682   }
1683 
1684   // Compute the other corners of the data:
1685   metaData.TL = metaData.BL + metaData.dy - vOne;
1686   metaData.TR = metaData.TL + metaData.dx - hOne;
1687   metaData.BR = metaData.BL + metaData.dx - hOne;
1688 
1689   // First baseline offset from top-left corner.
1690   vtkVector2i penOffset(pad, -pad);
1691   // Account for line spacing to center the text vertically in the bbox:
1692   penOffset[1] -= ascent;
1693   penOffset[1] -= metaData.textProperty->GetLineOffset();
1694   rotateVector2i(penOffset, s, c);
1695 
1696   vtkVector2i pen = metaData.TL + penOffset;
1697 
1698   // Calculate bounding box of text:
1699   vtkTuple<int, 4> textBbox;
1700   textBbox[0] = textBbox[1] = pen[0];
1701   textBbox[2] = textBbox[3] = pen[1];
1702 
1703   // Calculate line offset:
1704   vtkVector2i lineFeed(0, -(metaData.height + interLineSpacing));
1705   rotateVector2i(lineFeed, s, c);
1706 
1707   // Compile the metrics data to determine the final bounding box. Set line
1708   // origins here, too.
1709   vtkVector2i origin;
1710   int justification = metaData.textProperty->GetJustification();
1711   for (size_t i = 0; i < metaData.lineMetrics.size(); ++i)
1712   {
1713     MetaData::LineMetrics &metrics = metaData.lineMetrics[i];
1714 
1715     // Apply justification
1716     origin = pen;
1717     if (justification != VTK_TEXT_LEFT)
1718     {
1719       int xShift = metaData.maxLineWidth - metrics.width;
1720       if (justification == VTK_TEXT_CENTERED)
1721       {
1722         xShift /= 2;
1723       }
1724       origin[0] += static_cast<int>(std::round(c * xShift));
1725       origin[1] += static_cast<int>(std::round(s * xShift));
1726     }
1727 
1728     // Set line origin
1729     metrics.origin = origin;
1730 
1731     // Merge bounding boxes
1732     textBbox[0] = std::min(textBbox[0], metrics.xmin + origin[0]);
1733     textBbox[1] = std::max(textBbox[1], metrics.xmax + origin[0]);
1734     textBbox[2] = std::min(textBbox[2], metrics.ymin + origin[1]);
1735     textBbox[3] = std::max(textBbox[3], metrics.ymax + origin[1]);
1736 
1737     // Update pen position
1738     pen = pen + lineFeed;
1739   }
1740 
1741   // Adjust for shadow
1742   if (metaData.textProperty->GetShadow())
1743   {
1744     int shadowOffset[2];
1745     metaData.textProperty->GetShadowOffset(shadowOffset);
1746     if (shadowOffset[0] < 0)
1747     {
1748       textBbox[0] += shadowOffset[0];
1749     }
1750     else
1751     {
1752       textBbox[1] += shadowOffset[0];
1753     }
1754     if (shadowOffset[1] < 0)
1755     {
1756       textBbox[2] += shadowOffset[1];
1757     }
1758     else
1759     {
1760       textBbox[3] += shadowOffset[1];
1761     }
1762   }
1763 
1764   // Compute the background/frame bounding box.
1765   vtkTuple<int, 4> bgBbox;
1766   bgBbox[0] = std::min(std::min(metaData.TL[0], metaData.TR[0]),
1767                        std::min(metaData.BL[0], metaData.BR[0]));
1768   bgBbox[1] = std::max(std::max(metaData.TL[0], metaData.TR[0]),
1769                        std::max(metaData.BL[0], metaData.BR[0]));
1770   bgBbox[2] = std::min(std::min(metaData.TL[1], metaData.TR[1]),
1771                        std::min(metaData.BL[1], metaData.BR[1]));
1772   bgBbox[3] = std::max(std::max(metaData.TL[1], metaData.TR[1]),
1773                        std::max(metaData.BL[1], metaData.BR[1]));
1774 
1775   // Calculate the final bounding box (should just be the bg, but just in
1776   // case...)
1777   metaData.bbox[0] = std::min(textBbox[0], bgBbox[0]);
1778   metaData.bbox[1] = std::max(textBbox[1], bgBbox[1]);
1779   metaData.bbox[2] = std::min(textBbox[2], bgBbox[2]);
1780   metaData.bbox[3] = std::max(textBbox[3], bgBbox[3]);
1781 
1782   return true;
1783 }
1784 
1785 //----------------------------------------------------------------------------
PrepareImageData(vtkImageData * data,int textBbox[4])1786 void vtkFreeTypeTools::PrepareImageData(vtkImageData *data, int textBbox[4])
1787 {
1788   // Calculate the bbox's dimensions
1789   int textDims[2];
1790   textDims[0] = (textBbox[1] - textBbox[0] + 1);
1791   textDims[1] = (textBbox[3] - textBbox[2] + 1);
1792 
1793   // Calculate the size the image needs to be.
1794   int targetDims[3];
1795   targetDims[0] = textDims[0];
1796   targetDims[1] = textDims[1];
1797   targetDims[2] = 1;
1798   // Scale to the next highest power of 2 if required.
1799   if (this->ScaleToPowerTwo)
1800   {
1801     targetDims[0] = vtkMath::NearestPowerOfTwo(targetDims[0]);
1802     targetDims[1] = vtkMath::NearestPowerOfTwo(targetDims[1]);
1803   }
1804 
1805   // Calculate the target extent of the image.
1806   int targetExtent[6];
1807   targetExtent[0] = textBbox[0];
1808   targetExtent[1] = textBbox[0] + targetDims[0] - 1;
1809   targetExtent[2] = textBbox[2];
1810   targetExtent[3] = textBbox[2] + targetDims[1] - 1;
1811   targetExtent[4] = 0;
1812   targetExtent[5] = 0;
1813 
1814   // Get the actual image extents and increments
1815   int imageExtent[6];
1816   double imageSpacing[3];
1817   data->GetExtent(imageExtent);
1818   data->GetSpacing(imageSpacing);
1819 
1820   // Do we need to reallocate the image memory?
1821   if (data->GetScalarType() != VTK_UNSIGNED_CHAR ||
1822       data->GetNumberOfScalarComponents() != 4 ||
1823       imageExtent[0] != targetExtent[0] ||
1824       imageExtent[1] != targetExtent[1] ||
1825       imageExtent[2] != targetExtent[2] ||
1826       imageExtent[3] != targetExtent[3] ||
1827       imageExtent[4] != targetExtent[4] ||
1828       imageExtent[5] != targetExtent[5] ||
1829       fabs(imageSpacing[0] - 1.0) > 1e-10 ||
1830       fabs(imageSpacing[1] - 1.0) > 1e-10 ||
1831       fabs(imageSpacing[2] - 1.0) > 1e-10 )
1832   {
1833     data->SetSpacing(1.0, 1.0, 1.0);
1834     data->SetExtent(targetExtent);
1835     data->AllocateScalars(VTK_UNSIGNED_CHAR, 4);
1836   }
1837 
1838   // Clear the image buffer
1839   memset(data->GetScalarPointer(), this->DebugTextures ? 64 : 0,
1840          (data->GetNumberOfPoints() * data->GetNumberOfScalarComponents()));
1841 }
1842 
1843 // Helper functions for rasterizing the background/frame quad:
1844 namespace RasterScanQuad {
1845 
1846 // Return true and set t1 (if 0 <= t1 <= 1) for the intersection of lines:
1847 //
1848 // P1(t1) = p1 + t1 * v1 and
1849 // P2(t2) = p2 + t2 * v2.
1850 //
1851 // This method is specialized for the case of P2(t2) always being a horizontal
1852 // line (v2 = {1, 0}) with p1 defined as {0, y}.
1853 //
1854 // If the lines do not intersect or t1 is outside of the specified range, return
1855 // false.
getIntersectionParameter(const vtkVector2i & p1,const vtkVector2i & v1,int y,float & t1)1856 inline bool getIntersectionParameter(const vtkVector2i &p1,
1857                                      const vtkVector2i &v1,
1858                                      int y, float &t1)
1859 {
1860   // First check if the input vector is parallel to the scan line, returning
1861   // false if it is:
1862   if (v1[1] == 0)
1863   {
1864     return false;
1865   }
1866 
1867   // Given the lines:
1868   // P1(t1) = p1 + t1 * v1 (The polygon edge)
1869   // P2(t2) = p2 + t2 * v2 (The horizontal scan line)
1870   //
1871   // And defining the vector:
1872   // w = p1 - p2
1873   //
1874   // The value of t1 at the intersection of P1 and P2 is:
1875   // t1 = (v2[1] * w[0] - v2[0] * w[1]) / (v2[0] * v1[1] - v2[1] * v1[0])
1876   //
1877   // We know that p2 = {0, y} and v2 = {1, 0}, since we're scanning along the
1878   // x axis, so the above becomes:
1879   // t1 = (-w[1]) / (v1[1])
1880   //
1881   // Expanding the definition of w, w[1] --> (p1[1] - p2[1]) --> p1[1] - y,
1882   // resulting in the final:
1883   // t1 = -(p1[1] - y) / v1[1], or
1884   // t1 = (y - p1[1]) / v1[1]
1885 
1886   t1 = (y - p1[1]) / static_cast<float>(v1[1]);
1887   return t1 >= 0.f && t1 <= 1.f;
1888 }
1889 
1890 // Evaluate the line equation P(t) = p + t * v at the supplied t, and return
1891 // the x value of the resulting point.
evaluateLineXOnly(const vtkVector2i & p,const vtkVector2i & v,float t)1892 inline int evaluateLineXOnly(const vtkVector2i &p, const vtkVector2i &v,
1893                              float t)
1894 {
1895   return p.GetX() + static_cast<int>(std::round(v.GetX() * t));
1896 }
1897 
1898 // Given the corners of a rectangle (TL, TR, BL, BR), the vectors that
1899 // separate them (dx = TR - TL = BR - BL, dy = TR - BR = TL - BL), and the
1900 // y value to scan, return the minimum and maximum x values that the rectangle
1901 // contains.
findScanRange(const vtkVector2i & TL,const vtkVector2i & TR,const vtkVector2i & BL,const vtkVector2i & BR,const vtkVector2i & dx,const vtkVector2i & dy,int y,int & min,int & max)1902 bool findScanRange(const vtkVector2i &TL, const vtkVector2i &TR,
1903                    const vtkVector2i &BL, const vtkVector2i &BR,
1904                    const vtkVector2i &dx, const vtkVector2i &dy,
1905                    int y, int &min, int &max)
1906 {
1907   // Initialize the min and max to a known invalid range using the bounds of the
1908   // rectangle:
1909   min = std::max(std::max(TL[0], TR[0]), std::max(BL[0], BR[0]));
1910   max = std::min(std::min(TL[0], TR[0]), std::min(BL[0], BR[0]));
1911 
1912   float lineParam;
1913   int numIntersections = 0;
1914 
1915   // Top
1916   if (getIntersectionParameter(TL, dx, y, lineParam))
1917   {
1918     int x = evaluateLineXOnly(TL, dx, lineParam);
1919     min = std::min(min, x);
1920     max = std::max(max, x);
1921     ++numIntersections;
1922   }
1923   // Bottom
1924   if (getIntersectionParameter(BL, dx, y, lineParam))
1925   {
1926     int x = evaluateLineXOnly(BL, dx, lineParam);
1927     min = std::min(min, x);
1928     max = std::max(max, x);
1929     ++numIntersections;
1930   }
1931   // Left
1932   if (getIntersectionParameter(BL, dy, y, lineParam))
1933   {
1934     int x = evaluateLineXOnly(BL, dy, lineParam);
1935     min = std::min(min, x);
1936     max = std::max(max, x);
1937     ++numIntersections;
1938   }
1939   // Right
1940   if (getIntersectionParameter(BR, dy, y, lineParam))
1941   {
1942     int x = evaluateLineXOnly(BR, dy, lineParam);
1943     min = std::min(min, x);
1944     max = std::max(max, x);
1945     ++numIntersections;
1946   }
1947 
1948   return numIntersections != 0;
1949 }
1950 
1951 // Clamp value to stay between the minimum and maximum extent for the
1952 // specified dimension.
clampToExtent(int extent[6],int dim,int & value)1953 inline void clampToExtent(int extent[6], int dim, int &value)
1954 {
1955   value = std::min(extent[2*dim+1], std::max(extent[2*dim], value));
1956 }
1957 
1958 } // end namespace RasterScanQuad
1959 
1960 //----------------------------------------------------------------------------
RenderBackground(vtkTextProperty * tprop,vtkImageData * image,ImageMetaData & metaData)1961 void vtkFreeTypeTools::RenderBackground(vtkTextProperty *tprop,
1962                                         vtkImageData *image,
1963                                         ImageMetaData &metaData)
1964 {
1965   unsigned char* color;
1966   unsigned char backgroundColor[4] = {
1967     static_cast<unsigned char>(tprop->GetBackgroundColor()[0] * 255),
1968     static_cast<unsigned char>(tprop->GetBackgroundColor()[1] * 255),
1969     static_cast<unsigned char>(tprop->GetBackgroundColor()[2] * 255),
1970     static_cast<unsigned char>(tprop->GetBackgroundOpacity()  * 255)
1971   };
1972   unsigned char frameColor[4] = {
1973     static_cast<unsigned char>(tprop->GetFrameColor()[0] * 255),
1974     static_cast<unsigned char>(tprop->GetFrameColor()[1] * 255),
1975     static_cast<unsigned char>(tprop->GetFrameColor()[2] * 255),
1976     static_cast<unsigned char>(tprop->GetFrame() ? 255 : 0)
1977   };
1978 
1979   if (backgroundColor[3] == 0 && frameColor[3] == 0)
1980   {
1981     return;
1982   }
1983 
1984   const vtkVector2i &dx = metaData.dx;
1985   const vtkVector2i &dy = metaData.dy;
1986   const vtkVector2i &TL = metaData.TL;
1987   const vtkVector2i &TR = metaData.TR;
1988   const vtkVector2i &BL = metaData.BL;
1989   const vtkVector2i &BR = metaData.BR;
1990 
1991   // Find the minimum and maximum y values:
1992   int yMin = std::min(std::min(TL[1], TR[1]), std::min(BL[1], BR[1]));
1993   int yMax = std::max(std::max(TL[1], TR[1]), std::max(BL[1], BR[1]));
1994 
1995   // Clamp these to prevent out of bounds errors:
1996   int extent[6];
1997   image->GetExtent(extent);
1998   RasterScanQuad::clampToExtent(extent, 1, yMin);
1999   RasterScanQuad::clampToExtent(extent, 1, yMax);
2000 
2001   // Scan from yMin to yMax, finding the x values on that horizontal line that
2002   // are contained by the data rectangle, then paint them with the background
2003   // color.
2004   int frameWidth = tprop->GetFrameWidth();
2005   for (int y = yMin; y <= yMax; ++y)
2006   {
2007     int xMin, xMax;
2008     if (RasterScanQuad::findScanRange(TL, TR, BL, BR, dx, dy, y, xMin, xMax))
2009     {
2010       // Clamp to prevent out of bounds errors:
2011       RasterScanQuad::clampToExtent(extent, 0, xMin);
2012       RasterScanQuad::clampToExtent(extent, 0, xMax);
2013 
2014       // Get a pointer into the image data:
2015       unsigned char *dataPtr = static_cast<unsigned char*>(
2016             image->GetScalarPointer(xMin, y, 0));
2017       for (int x = xMin; x <= xMax; ++x)
2018       {
2019         color =
2020           (frameColor[3] != 0 && (y < (yMin + frameWidth) || y > (yMax - frameWidth)
2021             || x < (xMin + frameWidth) || x > (xMax - frameWidth))) ?
2022           frameColor : backgroundColor;
2023         *(dataPtr++) = color[0];
2024         *(dataPtr++) = color[1];
2025         *(dataPtr++) = color[2];
2026         *(dataPtr++) = color[3];
2027       }
2028     }
2029   }
2030 }
2031 
2032 //----------------------------------------------------------------------------
2033 template <typename StringType, typename DataType>
PopulateData(const StringType & str,DataType data,MetaData & metaData)2034 bool vtkFreeTypeTools::PopulateData(const StringType &str, DataType data,
2035                                     MetaData &metaData)
2036 {
2037   // Go through the string, line by line
2038   typename StringType::const_iterator beginLine = str.begin();
2039   typename StringType::const_iterator endLine =
2040       std::find(beginLine, str.end(), '\n');
2041 
2042   int lineIndex = 0;
2043   while (endLine != str.end())
2044   {
2045     if (!this->RenderLine(beginLine, endLine, lineIndex, data, metaData))
2046     {
2047       return false;
2048     }
2049 
2050     beginLine = endLine;
2051     ++beginLine;
2052     endLine = std::find(beginLine, str.end(), '\n');
2053     ++lineIndex;
2054   }
2055 
2056   // Render the last line:
2057   return this->RenderLine(beginLine, endLine, lineIndex, data, metaData);
2058 }
2059 
2060 //----------------------------------------------------------------------------
2061 template <typename IteratorType, typename DataType>
RenderLine(IteratorType begin,IteratorType end,int lineIndex,DataType data,MetaData & metaData)2062 bool vtkFreeTypeTools::RenderLine(IteratorType begin, IteratorType end,
2063                                   int lineIndex, DataType data,
2064                                   MetaData &metaData)
2065 {
2066   int x = metaData.lineMetrics[lineIndex].origin.GetX();
2067   int y = metaData.lineMetrics[lineIndex].origin.GetY();
2068 
2069   // Render char by char
2070   FT_UInt previousGlyphIndex = 0; // for kerning
2071   for (; begin != end; ++begin)
2072   {
2073     this->RenderCharacter(*begin, x, y, previousGlyphIndex, data, metaData);
2074   }
2075 
2076   return true;
2077 }
2078 
2079 //----------------------------------------------------------------------------
2080 template <typename CharType>
RenderCharacter(CharType character,int & x,int & y,FT_UInt & previousGlyphIndex,vtkImageData * image,MetaData & metaData)2081 bool vtkFreeTypeTools::RenderCharacter(CharType character, int &x, int &y,
2082                                        FT_UInt &previousGlyphIndex,
2083                                        vtkImageData *image,
2084                                        MetaData &metaData)
2085 {
2086   ImageMetaData *iMetaData = reinterpret_cast<ImageMetaData*>(&metaData);
2087   FT_BitmapGlyph bitmapGlyph = nullptr;
2088   FT_UInt glyphIndex;
2089   FT_Bitmap *bitmap = this->GetBitmap(character, &iMetaData->scaler,
2090                                       glyphIndex, bitmapGlyph);
2091 
2092   // Add the kerning
2093   if (iMetaData->faceHasKerning && previousGlyphIndex && glyphIndex)
2094   {
2095     FT_Vector kerningDelta;
2096     if (FT_Get_Kerning(iMetaData->face, previousGlyphIndex, glyphIndex,
2097                        FT_KERNING_DEFAULT, &kerningDelta) == 0)
2098     {
2099       if (metaData.faceIsRotated) // PR#15301
2100       {
2101         FT_Vector_Transform(&kerningDelta, &metaData.rotation);
2102       }
2103       x += kerningDelta.x >> 6;
2104       y += kerningDelta.y >> 6;
2105     }
2106   }
2107   previousGlyphIndex = glyphIndex;
2108 
2109   if (!bitmap)
2110   {
2111     // TODO This should draw an empty rectangle.
2112     return false;
2113   }
2114 
2115   if (bitmap->width && bitmap->rows)
2116   {
2117     // Starting position given the bearings.
2118     vtkVector2i pen(x + bitmapGlyph->left, y + bitmapGlyph->top);
2119 
2120     // Render the current glyph into the image
2121     unsigned char *ptr = static_cast<unsigned char *>(
2122           image->GetScalarPointer(pen[0], pen[1], 0));
2123     if (ptr)
2124     {
2125       int dataPitch = (-iMetaData->imageDimensions[0] - bitmap->width) *
2126           iMetaData->imageIncrements[0];
2127       unsigned char *glyphPtrRow = bitmap->buffer;
2128       unsigned char *glyphPtr;
2129       const unsigned char *fgRGB = iMetaData->rgba;
2130       const float fgA = static_cast<float>(metaData.textProperty->GetOpacity());
2131 
2132       for (int j = 0; j < static_cast<int>(bitmap->rows); ++j)
2133       {
2134         glyphPtr = glyphPtrRow;
2135 
2136         for (int i = 0; i < static_cast<int>(bitmap->width); ++i)
2137         {
2138           if (*glyphPtr == 0) // Pixel is not drawn
2139           {
2140             ptr += 4;
2141           }
2142           else if (ptr[3] > 0) // Need to overblend
2143           {
2144             // This is a pixel we've drawn before since it has non-zero alpha.
2145             // We must therefore blend the colors.
2146             const float glyphA = *glyphPtr / 255.f;
2147             const float bgA = ptr[3] / 255.f;
2148 
2149             const float fg_blend = fgA * glyphA;
2150             const float bg_blend = bgA * (1.f - fg_blend);
2151 
2152             // src_a + dst_a ( 1 - src_a )
2153             const float a = 255.f * (fg_blend + bg_blend);
2154             const float invA = 1.f / (fg_blend + bg_blend);
2155 
2156             // (src_c * src_a + dst_c * dst_a * (1 - src_a)) / out_a
2157             const float r = (bg_blend * ptr[0] + fg_blend * fgRGB[0]) * invA;
2158             const float g = (bg_blend * ptr[1] + fg_blend * fgRGB[1]) * invA;
2159             const float b = (bg_blend * ptr[2] + fg_blend * fgRGB[2]) * invA;
2160 
2161             // Update the buffer:
2162             ptr[0] = static_cast<unsigned char>(r);
2163             ptr[1] = static_cast<unsigned char>(g);
2164             ptr[2] = static_cast<unsigned char>(b);
2165             ptr[3] = static_cast<unsigned char>(a);
2166 
2167             ptr += 4;
2168           }
2169           else // No existing color
2170           {
2171             *ptr = fgRGB[0];
2172             ++ptr;
2173             *ptr = fgRGB[1];
2174             ++ptr;
2175             *ptr = fgRGB[2];
2176             ++ptr;
2177             *ptr = static_cast<unsigned char>((*glyphPtr) * fgA);
2178             ++ptr;
2179           }
2180           ++glyphPtr;
2181         }
2182         glyphPtrRow += bitmap->pitch;
2183         ptr += dataPitch;
2184       }
2185     }
2186   }
2187 
2188   // Advance to next char
2189   x += (bitmapGlyph->root.advance.x + 0x8000) >> 16;
2190   y += (bitmapGlyph->root.advance.y + 0x8000) >> 16;
2191   return true;
2192 }
2193 
2194 //----------------------------------------------------------------------------
2195 template <typename CharType>
RenderCharacter(CharType character,int & x,int & y,FT_UInt & previousGlyphIndex,vtkPath * path,MetaData & metaData)2196 bool vtkFreeTypeTools::RenderCharacter(CharType character, int &x, int &y,
2197                                        FT_UInt &previousGlyphIndex,
2198                                        vtkPath *path, MetaData &metaData)
2199 {
2200   FT_UInt glyphIndex = 0;
2201   FT_OutlineGlyph outlineGlyph = nullptr;
2202   FT_Outline *outline = this->GetOutline(character, &metaData.scaler,
2203                                          glyphIndex, outlineGlyph);
2204 
2205   // Add the kerning
2206   if (metaData.faceHasKerning && previousGlyphIndex && glyphIndex)
2207   {
2208     FT_Vector kerningDelta;
2209     FT_Get_Kerning(metaData.face, previousGlyphIndex, glyphIndex,
2210                    FT_KERNING_DEFAULT, &kerningDelta);
2211     if (metaData.faceIsRotated) // PR#15301
2212     {
2213       FT_Vector_Transform(&kerningDelta, &metaData.rotation);
2214     }
2215     x += kerningDelta.x >> 6;
2216     y += kerningDelta.y >> 6;
2217   }
2218   previousGlyphIndex = glyphIndex;
2219 
2220   if (!outline)
2221   {
2222     // TODO render an empty box.
2223     return false;
2224   }
2225 
2226   this->OutlineToPath(x, y, outline, path);
2227 
2228   // Advance to next char
2229   x += (outlineGlyph->root.advance.x + 0x8000) >> 16;
2230   y += (outlineGlyph->root.advance.y + 0x8000) >> 16;
2231 
2232   return true;
2233 }
2234 
2235 //----------------------------------------------------------------------------
OutlineToPath(int x,int y,FT_Outline * outline,vtkPath * path)2236 void vtkFreeTypeTools::OutlineToPath(int x, int y, FT_Outline *outline,
2237                                      vtkPath *path)
2238 {
2239   // The FT_CURVE defines don't really work in a switch...only the first two
2240   // bits are meaningful, and the rest appear to be garbage. We'll convert them
2241   // into values in the enum below:
2242   enum controlType
2243   {
2244     FIRST_POINT,
2245     ON_POINT,
2246     CUBIC_POINT,
2247     CONIC_POINT
2248   };
2249 
2250   if (outline->n_points > 0)
2251   {
2252     short point = 0;
2253     for (short contour = 0; contour < outline->n_contours; ++contour)
2254     {
2255       short contourEnd = outline->contours[contour];
2256       controlType lastTag = FIRST_POINT;
2257       double contourStartVec[2];
2258       contourStartVec[0] = contourStartVec[1] = 0.0;
2259       double lastVec[2];
2260       lastVec[0] = lastVec[1] = 0.0;
2261       for (; point <= contourEnd; ++point)
2262       {
2263         FT_Vector ftvec = outline->points[point];
2264         char fttag = outline->tags[point];
2265         controlType tag = FIRST_POINT;
2266 
2267         // Mask the tag and convert to our known-good control types:
2268         // (0x3 mask is because these values often have trailing garbage --
2269         // see note above controlType enum).
2270         switch (fttag & 0x3)
2271         {
2272           case (FT_CURVE_TAG_ON & 0x3): // 0b01
2273             tag = ON_POINT;
2274             break;
2275           case (FT_CURVE_TAG_CUBIC & 0x3): // 0b11
2276             tag = CUBIC_POINT;
2277             break;
2278           case (FT_CURVE_TAG_CONIC & 0x3): // 0b00
2279             tag = CONIC_POINT;
2280             break;
2281           default:
2282             vtkWarningMacro("Invalid control code returned from FreeType: "
2283                             << static_cast<int>(fttag) << " (masked: "
2284                             << static_cast<int>(fttag & 0x3));
2285             return;
2286         }
2287 
2288         double vec[2];
2289         vec[0] = ftvec.x / 64.0 + x;
2290         vec[1] = ftvec.y / 64.0 + y;
2291 
2292         // Handle the first point here, unless it is a CONIC point, in which
2293         // case the switches below handle it.
2294         if (lastTag == FIRST_POINT && tag != CONIC_POINT)
2295         {
2296           path->InsertNextPoint(vec[0], vec[1], 0.0, vtkPath::MOVE_TO);
2297           lastTag = tag;
2298           lastVec[0] = vec[0];
2299           lastVec[1] = vec[1];
2300           contourStartVec[0] = vec[0];
2301           contourStartVec[1] = vec[1];
2302           continue;
2303         }
2304 
2305         switch (tag)
2306         {
2307           case ON_POINT:
2308             switch(lastTag)
2309             {
2310               case ON_POINT:
2311                 path->InsertNextPoint(vec[0], vec[1], 0.0, vtkPath::LINE_TO);
2312                 break;
2313               case CONIC_POINT:
2314                 path->InsertNextPoint(vec[0], vec[1], 0.0,
2315                     vtkPath::CONIC_CURVE);
2316                 break;
2317               case CUBIC_POINT:
2318                 path->InsertNextPoint(vec[0], vec[1], 0.0,
2319                     vtkPath::CUBIC_CURVE);
2320                 break;
2321               case FIRST_POINT:
2322               default:
2323                 break;
2324             }
2325             break;
2326           case CONIC_POINT:
2327             switch(lastTag)
2328             {
2329               case ON_POINT:
2330                 path->InsertNextPoint(vec[0], vec[1], 0.0,
2331                     vtkPath::CONIC_CURVE);
2332                 break;
2333               case CONIC_POINT: {
2334                 // Two conic points indicate a virtual "ON" point between
2335                 // them. Insert both points.
2336                 double virtualOn[2] = {(vec[0] + lastVec[0]) * 0.5,
2337                                        (vec[1] + lastVec[1]) * 0.5};
2338                 path->InsertNextPoint(virtualOn[0], virtualOn[1], 0.0,
2339                     vtkPath::CONIC_CURVE);
2340                 path->InsertNextPoint(vec[0], vec[1], 0.0,
2341                     vtkPath::CONIC_CURVE);
2342               }
2343                 break;
2344               case FIRST_POINT: {
2345                 // The first point in the contour can be a conic control
2346                 // point. Use the last point of the contour as the starting
2347                 // point. If the last point is a conic point as well, start
2348                 // on a virtual point between the two:
2349                 FT_Vector lastContourFTVec = outline->points[contourEnd];
2350                 double lastContourVec[2] = {lastContourFTVec.x / 64.0 + x,
2351                                             lastContourFTVec.y / 64.0 + y};
2352                 char lastContourFTTag = outline->tags[contourEnd];
2353                 if (lastContourFTTag & FT_CURVE_TAG_CONIC)
2354                 {
2355                   double virtualOn[2] = {(vec[0] + lastContourVec[0]) * 0.5,
2356                                          (vec[1] + lastContourVec[1]) * 0.5};
2357                   path->InsertNextPoint(virtualOn[0], virtualOn[1],
2358                       0.0, vtkPath::MOVE_TO);
2359                   path->InsertNextPoint(vec[0], vec[1], 0.0,
2360                       vtkPath::CONIC_CURVE);
2361                 }
2362                 else
2363                 {
2364                   path->InsertNextPoint(lastContourVec[0], lastContourVec[1],
2365                       0.0, vtkPath::MOVE_TO);
2366                   path->InsertNextPoint(vec[0], vec[1], 0.0,
2367                       vtkPath::CONIC_CURVE);
2368                 }
2369               }
2370                 break;
2371               case CUBIC_POINT:
2372               default:
2373                 break;
2374             }
2375             break;
2376           case CUBIC_POINT:
2377             switch(lastTag)
2378             {
2379               case ON_POINT:
2380               case CUBIC_POINT:
2381                 path->InsertNextPoint(vec[0], vec[1], 0.0,
2382                     vtkPath::CUBIC_CURVE);
2383                 break;
2384               case CONIC_POINT:
2385               case FIRST_POINT:
2386               default:
2387                 break;
2388             }
2389             break;
2390           case FIRST_POINT:
2391           default:
2392             break;
2393         } // end switch
2394 
2395         lastTag = tag;
2396         lastVec[0] = vec[0];
2397         lastVec[1] = vec[1];
2398       } // end contour
2399 
2400       // The contours are always implicitly closed to the start point of the
2401       // contour:
2402       switch (lastTag)
2403       {
2404         case ON_POINT:
2405           path->InsertNextPoint(contourStartVec[0], contourStartVec[1], 0.0,
2406               vtkPath::LINE_TO);
2407           break;
2408         case CUBIC_POINT:
2409           path->InsertNextPoint(contourStartVec[0], contourStartVec[1], 0.0,
2410               vtkPath::CUBIC_CURVE);
2411           break;
2412         case CONIC_POINT:
2413           path->InsertNextPoint(contourStartVec[0], contourStartVec[1], 0.0,
2414               vtkPath::CONIC_CURVE);
2415           break;
2416         case FIRST_POINT:
2417         default:
2418           break;
2419       } // end switch (lastTag)
2420     } // end contour points iteration
2421   } // end contour iteration
2422 }
2423 
2424 //----------------------------------------------------------------------------
2425 // Similar to implementations in vtkFreeTypeUtilities and vtkTextMapper.
2426 template <typename T>
FitStringToBBox(const T & str,MetaData & metaData,int targetWidth,int targetHeight)2427 int vtkFreeTypeTools::FitStringToBBox(const T &str, MetaData &metaData,
2428                                       int targetWidth, int targetHeight)
2429 {
2430   if (str.empty() || targetWidth == 0 || targetHeight == 0 ||
2431       metaData.textProperty == nullptr)
2432   {
2433     return 0;
2434   }
2435 
2436   // Use the current font size as a first guess
2437   int size[2];
2438   double fontSize = metaData.textProperty->GetFontSize();
2439   if (!this->CalculateBoundingBox(str, metaData))
2440   {
2441     return -1;
2442   }
2443   size[0] = metaData.bbox[1] - metaData.bbox[0];
2444   size[1] = metaData.bbox[3] - metaData.bbox[2];
2445 
2446   // Bad assumption but better than nothing -- assume the bbox grows linearly
2447   // with the font size:
2448   if (size[0] != 0 && size[1] != 0)
2449   {
2450     fontSize *= std::min(
2451           static_cast<double>(targetWidth)  / static_cast<double>(size[0]),
2452         static_cast<double>(targetHeight) / static_cast<double>(size[1]));
2453     metaData.textProperty->SetFontSize(static_cast<int>(fontSize));
2454     metaData.scaler.height = fontSize * 64; // 26.6 format points
2455     metaData.scaler.width = fontSize * 64; // 26.6 format points
2456     metaData.unrotatedScaler.height = fontSize * 64; // 26.6 format points
2457     metaData.unrotatedScaler.width = fontSize * 64; // 26.6 format points
2458     if (!this->CalculateBoundingBox(str, metaData))
2459     {
2460       return -1;
2461     }
2462     size[0] = metaData.bbox[1] - metaData.bbox[0];
2463     size[1] = metaData.bbox[3] - metaData.bbox[2];
2464   }
2465 
2466   // Now just step up/down until the bbox matches the target.
2467   while (size[0] < targetWidth && size[1] < targetHeight && fontSize < 200.)
2468   {
2469     fontSize += 1.;
2470     metaData.textProperty->SetFontSize(fontSize);
2471     metaData.scaler.height = fontSize * 64; // 26.6 format points
2472     metaData.scaler.width = fontSize * 64; // 26.6 format points
2473     metaData.unrotatedScaler.height = fontSize * 64; // 26.6 format points
2474     metaData.unrotatedScaler.width = fontSize * 64; // 26.6 format points
2475     if (!this->CalculateBoundingBox(str, metaData))
2476     {
2477       return -1;
2478     }
2479     size[0] = metaData.bbox[1] - metaData.bbox[0];
2480     size[1] = metaData.bbox[3] - metaData.bbox[2];
2481   }
2482 
2483   while ((size[0] > targetWidth || size[1] > targetHeight) && fontSize > 1.)
2484   {
2485     fontSize -= 1.;
2486     metaData.textProperty->SetFontSize(fontSize);
2487     metaData.scaler.height = fontSize * 64; // 26.6 format points
2488     metaData.scaler.width = fontSize * 64; // 26.6 format points
2489     metaData.unrotatedScaler.height = fontSize * 64; // 26.6 format points
2490     metaData.unrotatedScaler.width = fontSize * 64; // 26.6 format points
2491     if (!this->CalculateBoundingBox(str, metaData))
2492     {
2493       return -1;
2494     }
2495     size[0] = metaData.bbox[1] - metaData.bbox[0];
2496     size[1] = metaData.bbox[3] - metaData.bbox[2];
2497   }
2498 
2499   return fontSize;
2500 }
2501 
2502 //----------------------------------------------------------------------------
GetFace(vtkTextProperty * prop,size_t & prop_cache_id,FT_Face & face,bool & face_has_kerning)2503 inline bool vtkFreeTypeTools::GetFace(vtkTextProperty *prop,
2504                                       size_t &prop_cache_id,
2505                                       FT_Face &face, bool &face_has_kerning)
2506 {
2507   this->MapTextPropertyToId(prop, &prop_cache_id);
2508   if (!this->GetFace(prop_cache_id, &face))
2509   {
2510     vtkErrorMacro(<< "Failed retrieving the face");
2511     return false;
2512   }
2513   face_has_kerning = (FT_HAS_KERNING(face) != 0);
2514   return true;
2515 }
2516 
2517 //----------------------------------------------------------------------------
GetBitmap(FT_UInt32 c,size_t prop_cache_id,int prop_font_size,FT_UInt & gindex,FT_BitmapGlyph & bitmap_glyph)2518 inline FT_Bitmap* vtkFreeTypeTools::GetBitmap(FT_UInt32 c,
2519                                               size_t prop_cache_id,
2520                                               int prop_font_size,
2521                                               FT_UInt &gindex,
2522                                               FT_BitmapGlyph &bitmap_glyph)
2523 {
2524   // Get the glyph index
2525   if (!this->GetGlyphIndex(prop_cache_id, c, &gindex))
2526   {
2527     return nullptr;
2528   }
2529   FT_Glyph glyph;
2530   // Get the glyph as a bitmap
2531   if (!this->GetGlyph(prop_cache_id,
2532                       prop_font_size,
2533                       gindex,
2534                       &glyph,
2535                       vtkFreeTypeTools::GLYPH_REQUEST_BITMAP) ||
2536                         glyph->format != ft_glyph_format_bitmap)
2537   {
2538     return nullptr;
2539   }
2540 
2541   bitmap_glyph = reinterpret_cast<FT_BitmapGlyph>(glyph);
2542   FT_Bitmap *bitmap = &bitmap_glyph->bitmap;
2543 
2544   if (bitmap->pixel_mode != ft_pixel_mode_grays)
2545   {
2546     return nullptr;
2547   }
2548 
2549   return bitmap;
2550 }
2551 
2552 //----------------------------------------------------------------------------
GetBitmap(FT_UInt32 c,FTC_Scaler scaler,FT_UInt & gindex,FT_BitmapGlyph & bitmap_glyph)2553 FT_Bitmap *vtkFreeTypeTools::GetBitmap(FT_UInt32 c, FTC_Scaler scaler,
2554                                        FT_UInt &gindex,
2555                                        FT_BitmapGlyph &bitmap_glyph)
2556 {
2557   // Get the glyph index
2558   if (!this->GetGlyphIndex(reinterpret_cast<size_t>(scaler->face_id), c,
2559                            &gindex))
2560   {
2561     return nullptr;
2562   }
2563 
2564   // Get the glyph as a bitmap
2565   FT_Glyph glyph;
2566   if (!this->GetGlyph(scaler, gindex, &glyph,
2567                       vtkFreeTypeTools::GLYPH_REQUEST_BITMAP)
2568       || glyph->format != ft_glyph_format_bitmap)
2569   {
2570     return nullptr;
2571   }
2572 
2573   bitmap_glyph = reinterpret_cast<FT_BitmapGlyph>(glyph);
2574   FT_Bitmap *bitmap = &bitmap_glyph->bitmap;
2575 
2576   if (bitmap->pixel_mode != ft_pixel_mode_grays)
2577   {
2578     return nullptr;
2579   }
2580 
2581   return bitmap;
2582 }
2583 
2584 //----------------------------------------------------------------------------
GetOutline(FT_UInt32 c,size_t prop_cache_id,int prop_font_size,FT_UInt & gindex,FT_OutlineGlyph & outline_glyph)2585 inline FT_Outline *vtkFreeTypeTools::GetOutline(FT_UInt32 c,
2586                                                 size_t prop_cache_id,
2587                                                 int prop_font_size,
2588                                                 FT_UInt &gindex,
2589                                                 FT_OutlineGlyph &outline_glyph)
2590 {
2591   // Get the glyph index
2592   if (!this->GetGlyphIndex(prop_cache_id, c, &gindex))
2593   {
2594     return nullptr;
2595   }
2596   FT_Glyph glyph;
2597   // Get the glyph as a outline
2598   if (!this->GetGlyph(prop_cache_id,
2599                       prop_font_size,
2600                       gindex,
2601                       &glyph,
2602                       vtkFreeTypeTools::GLYPH_REQUEST_OUTLINE) ||
2603                         glyph->format != ft_glyph_format_outline)
2604   {
2605     return nullptr;
2606   }
2607 
2608   outline_glyph = reinterpret_cast<FT_OutlineGlyph>(glyph);
2609   FT_Outline *outline= &outline_glyph->outline;
2610 
2611   return outline;
2612 }
2613 
2614 //----------------------------------------------------------------------------
GetOutline(FT_UInt32 c,FTC_Scaler scaler,FT_UInt & gindex,FT_OutlineGlyph & outline_glyph)2615 FT_Outline *vtkFreeTypeTools::GetOutline(FT_UInt32 c, FTC_Scaler scaler,
2616                                          FT_UInt &gindex,
2617                                          FT_OutlineGlyph &outline_glyph)
2618 {
2619   // Get the glyph index
2620   if (!this->GetGlyphIndex(reinterpret_cast<size_t>(scaler->face_id), c,
2621                            &gindex))
2622   {
2623     return nullptr;
2624   }
2625 
2626   // Get the glyph as a outline
2627   FT_Glyph glyph;
2628   if (!this->GetGlyph(scaler, gindex, &glyph,
2629                       vtkFreeTypeTools::GLYPH_REQUEST_OUTLINE)
2630       || glyph->format != ft_glyph_format_outline)
2631   {
2632     return nullptr;
2633   }
2634 
2635   outline_glyph = reinterpret_cast<FT_OutlineGlyph>(glyph);
2636   FT_Outline *outline= &outline_glyph->outline;
2637 
2638   return outline;
2639 }
2640 
2641 //----------------------------------------------------------------------------
2642 template<typename T>
GetLineMetrics(T begin,T end,MetaData & metaData,int & width,int bbox[4])2643 void vtkFreeTypeTools::GetLineMetrics(T begin, T end, MetaData &metaData,
2644                                       int &width, int bbox[4])
2645 {
2646   FT_BitmapGlyph bitmapGlyph = nullptr;
2647   FT_UInt gindex = 0;
2648   FT_UInt gindexLast = 0;
2649   FT_Vector delta;
2650   width = 0;
2651   int pen[2] = {0, 0};
2652   bbox[0] = bbox[1] = pen[0];
2653   bbox[2] = bbox[3] = pen[1];
2654 
2655   for (; begin != end; ++begin)
2656   {
2657     // Get the bitmap and glyph index:
2658     FT_Bitmap *bitmap = this->GetBitmap(*begin, &metaData.scaler, gindex,
2659                                         bitmapGlyph);
2660 
2661     // Adjust the pen location for kerning
2662     if (metaData.faceHasKerning && gindexLast && gindex)
2663     {
2664       if (FT_Get_Kerning(metaData.face, gindexLast, gindex, FT_KERNING_DEFAULT,
2665                          &delta) == 0)
2666       {
2667         // Kerning is not rotated with the face, no need to rotate/adjust for
2668         // width:
2669         width += delta.x >> 6;
2670         // But we do need to rotate for pen location (see PR#15301)
2671         if (metaData.faceIsRotated)
2672         {
2673           FT_Vector_Transform(&delta, &metaData.rotation);
2674         }
2675         pen[0] += delta.x >> 6;
2676         pen[1] += delta.y >> 6;
2677       }
2678     }
2679     gindexLast = gindex;
2680 
2681     // Use the dimensions of the bitmap glyph to get a tight bounding box.
2682     if (bitmap)
2683     {
2684       bbox[0] = std::min(bbox[0], pen[0] + bitmapGlyph->left);
2685       bbox[1] = std::max(bbox[1], pen[0] + bitmapGlyph->left + static_cast<int>(bitmap->width) - 1);
2686       bbox[2] = std::min(bbox[2], pen[1] + bitmapGlyph->top + 1 - static_cast<int>(bitmap->rows));
2687       bbox[3] = std::max(bbox[3], pen[1] + bitmapGlyph->top);
2688     }
2689     else
2690     {
2691       // FIXME: do something more elegant here.
2692       // We should render an empty rectangle to adhere to the specs...
2693       vtkDebugMacro(<<"Unrecognized character: " << *begin);
2694       continue;
2695     }
2696 
2697     // Update advance.
2698     delta = bitmapGlyph->root.advance;
2699     pen[0] += (delta.x + 0x8000) >> 16;
2700     pen[1] += (delta.y + 0x8000) >> 16;
2701 
2702     if (metaData.faceIsRotated)
2703     {
2704       FT_Vector_Transform(&delta, &metaData.inverseRotation);
2705     }
2706     width += (delta.x + 0x8000) >> 16;
2707   }
2708 }
2709