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