You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
1622 lines
43 KiB
1622 lines
43 KiB
/*=========================================================================
|
|
|
|
Program: Visualization Toolkit
|
|
Module: $RCSfile: vtkFreeTypeUtilities.cxx,v $
|
|
|
|
Copyright (c) Ken Martin, Will Schroeder, Bill Lorensen
|
|
All rights reserved.
|
|
See Copyright.txt or http://www.kitware.com/Copyright.htm for details.
|
|
|
|
This software is distributed WITHOUT ANY WARRANTY; without even
|
|
the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
|
|
PURPOSE. See the above copyright notice for more information.
|
|
|
|
=========================================================================*/
|
|
|
|
#include "vtkFreeTypeUtilities.h"
|
|
|
|
#include "vtkTextProperty.h"
|
|
#include "vtkObjectFactory.h"
|
|
#include "vtkMath.h"
|
|
#include "vtkImageData.h"
|
|
//#include "vtkDebugLeaks.h"
|
|
|
|
// FTGL
|
|
|
|
#include "vtkftglConfig.h"
|
|
#include "FTLibrary.h"
|
|
#include "FTGLPixmapFont.h"
|
|
|
|
// The embedded fonts
|
|
|
|
#include "fonts/vtkEmbeddedFonts.h"
|
|
|
|
#include <sys/stat.h>
|
|
|
|
// Print debug info
|
|
|
|
#define VTK_FTFC_DEBUG 0
|
|
#define VTK_FTFC_DEBUG_CD 0
|
|
|
|
//----------------------------------------------------------------------------
|
|
vtkCxxRevisionMacro(vtkFreeTypeUtilities, "$Revision: 1.12 $");
|
|
vtkInstantiatorNewMacro(vtkFreeTypeUtilities);
|
|
|
|
//----------------------------------------------------------------------------
|
|
// The singleton, and the singleton cleanup
|
|
|
|
vtkFreeTypeUtilities* vtkFreeTypeUtilities::Instance = NULL;
|
|
vtkFreeTypeUtilitiesCleanup vtkFreeTypeUtilities::Cleanup;
|
|
|
|
//----------------------------------------------------------------------------
|
|
// The embedded fonts
|
|
// Create a lookup table between the text mapper attributes
|
|
// and the font buffers.
|
|
|
|
struct EmbeddedFontStruct
|
|
{
|
|
size_t length;
|
|
unsigned char *ptr;
|
|
};
|
|
|
|
//----------------------------------------------------------------------------
|
|
// This callback will be called by the FTGLibrary singleton cleanup destructor
|
|
// if it happens to be destroyed before our singleton (this order is not
|
|
// deterministic). It will destroy our singleton, if needed.
|
|
|
|
void vtkFreeTypeUtilitiesCleanupCallback ()
|
|
{
|
|
#if VTK_FTFC_DEBUG_CD
|
|
printf("vtkFreeTypeUtilitiesCleanupCallback\n");
|
|
#endif
|
|
vtkFreeTypeUtilities::SetInstance(NULL);
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
// Create the singleton cleanup
|
|
// Register our singleton cleanup callback against the FTLibrary so that
|
|
// it might be called before the FTLibrary singleton is destroyed.
|
|
|
|
vtkFreeTypeUtilitiesCleanup::vtkFreeTypeUtilitiesCleanup()
|
|
{
|
|
#if VTK_FTFC_DEBUG_CD
|
|
printf("vtkFreeTypeUtilitiesCleanup::vtkFreeTypeUtilitiesCleanup\n");
|
|
#endif
|
|
FTLibraryCleanup::AddDependency(&vtkFreeTypeUtilitiesCleanupCallback);
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
// Delete the singleton cleanup
|
|
// The callback called here might have been called by the FTLibrary singleton
|
|
// cleanup first (depending on the destruction order), but in case ours is
|
|
// destroyed first, let's call it too.
|
|
|
|
vtkFreeTypeUtilitiesCleanup::~vtkFreeTypeUtilitiesCleanup()
|
|
{
|
|
#if VTK_FTFC_DEBUG_CD
|
|
printf("vtkFreeTypeUtilitiesCleanup::~vtkFreeTypeUtilitiesCleanup\n");
|
|
#endif
|
|
vtkFreeTypeUtilitiesCleanupCallback();
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
vtkFreeTypeUtilities* vtkFreeTypeUtilities::GetInstance()
|
|
{
|
|
if (!vtkFreeTypeUtilities::Instance)
|
|
{
|
|
vtkFreeTypeUtilities::Instance = (vtkFreeTypeUtilities*)
|
|
vtkObjectFactory::CreateInstance("vtkFreeTypeUtilities");
|
|
if (!vtkFreeTypeUtilities::Instance)
|
|
{
|
|
#ifdef VTK_DEBUG_LEAKS
|
|
vtkDebugLeaks::DestructClass("vtkFreeTypeUtilities");
|
|
#endif
|
|
vtkFreeTypeUtilities::Instance = new vtkFreeTypeUtilities;
|
|
}
|
|
}
|
|
return vtkFreeTypeUtilities::Instance;
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
void vtkFreeTypeUtilities::SetInstance(vtkFreeTypeUtilities* instance)
|
|
{
|
|
if (vtkFreeTypeUtilities::Instance == instance)
|
|
{
|
|
return;
|
|
}
|
|
|
|
if (vtkFreeTypeUtilities::Instance)
|
|
{
|
|
vtkFreeTypeUtilities::Instance->Delete();
|
|
}
|
|
|
|
vtkFreeTypeUtilities::Instance = instance;
|
|
|
|
// User will call ->Delete() after setting instance
|
|
|
|
if (instance)
|
|
{
|
|
instance->Register(NULL);
|
|
}
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
vtkFreeTypeUtilities* vtkFreeTypeUtilities::New()
|
|
{
|
|
vtkFreeTypeUtilities* ret = vtkFreeTypeUtilities::GetInstance();
|
|
ret->Register(NULL);
|
|
return ret;
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
vtkFreeTypeUtilities::vtkFreeTypeUtilities()
|
|
{
|
|
#if VTK_FTFC_DEBUG_CD
|
|
printf("vtkFreeTypeUtilities::vtkFreeTypeUtilities\n");
|
|
#endif
|
|
|
|
this->MaximumNumberOfFaces = 30; // combinations of family+bold+italic
|
|
this->MaximumNumberOfSizes = this->MaximumNumberOfFaces * 20; // sizes
|
|
this->MaximumNumberOfBytes = 300000UL * this->MaximumNumberOfSizes;
|
|
|
|
this->CacheManager = NULL;
|
|
this->ImageCache = NULL;
|
|
this->CMapCache = NULL;
|
|
|
|
this->NumberOfEntries = 0;
|
|
this->InitializeCache();
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
vtkFreeTypeUtilities::~vtkFreeTypeUtilities()
|
|
{
|
|
#if VTK_FTFC_DEBUG_CD
|
|
printf("vtkFreeTypeUtilities::~vtkFreeTypeUtilities\n");
|
|
#endif
|
|
|
|
this->ReleaseCache();
|
|
this->ReleaseCacheManager();
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
FT_Library* vtkFreeTypeUtilities::GetLibrary()
|
|
{
|
|
#if VTK_FTFC_DEBUG_CD
|
|
printf("vtkFreeTypeUtilities::GetLibrary\n");
|
|
#endif
|
|
|
|
FTLibrary *ftgl_lib = FTLibrary::GetInstance();
|
|
if (ftgl_lib)
|
|
{
|
|
return ftgl_lib->GetLibrary();
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
FTC_Manager* vtkFreeTypeUtilities::GetCacheManager()
|
|
{
|
|
if (!this->CacheManager)
|
|
{
|
|
this->InitializeCacheManager();
|
|
}
|
|
|
|
return this->CacheManager;
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
FTC_ImageCache* vtkFreeTypeUtilities::GetImageCache()
|
|
{
|
|
if (!this->ImageCache)
|
|
{
|
|
this->InitializeCacheManager();
|
|
}
|
|
|
|
return this->ImageCache;
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
FTC_CMapCache* vtkFreeTypeUtilities::GetCMapCache()
|
|
{
|
|
if (!this->CMapCache)
|
|
{
|
|
this->InitializeCacheManager();
|
|
}
|
|
|
|
return this->CMapCache;
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
void vtkFreeTypeUtilities::MapTextPropertyToId(vtkTextProperty *tprop,
|
|
unsigned long *id)
|
|
{
|
|
if (!tprop || !id)
|
|
{
|
|
vtkErrorMacro(<< "Wrong parameters, one of them is NULL");
|
|
return;
|
|
}
|
|
|
|
// Set the first bit to avoid id = 0
|
|
// (the id will be mapped to a pointer, FTC_FaceID, so let's avoid NULL)
|
|
|
|
*id = 1;
|
|
int bits = 1;
|
|
|
|
// The font family is in 4 bits (= 5 bits so far)
|
|
// (2 would be enough right now, but who knows, it might grow)
|
|
|
|
int fam = (tprop->GetFontFamily() - tprop->GetFontFamilyMinValue()) << bits;
|
|
bits += 4;
|
|
|
|
// Bold is in 1 bit (= 6 bits so far)
|
|
|
|
int bold = (tprop->GetBold() ? 1 : 0) << bits;
|
|
bits++;
|
|
|
|
// Italic is in 1 bit (= 7 bits so far)
|
|
|
|
int italic = (tprop->GetItalic() ? 1 : 0) << bits;
|
|
bits++;
|
|
|
|
// Orientation (in degrees)
|
|
// We need 9 bits for 0 to 360. What do we need for more precisions:
|
|
// - 1/10th degree: 12 bits (11.8)
|
|
|
|
int angle = (vtkMath::Round(tprop->GetOrientation() * 10.0) % 3600) << bits;
|
|
|
|
// We really should not use more than 32 bits
|
|
|
|
// Now final id
|
|
|
|
*id |= fam | bold | italic | angle;
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
void vtkFreeTypeUtilities::MapIdToTextProperty(unsigned long id,
|
|
vtkTextProperty *tprop)
|
|
{
|
|
if (!tprop)
|
|
{
|
|
vtkErrorMacro(<< "Wrong parameters, one of them is NULL");
|
|
return;
|
|
}
|
|
|
|
// The first was set to avoid id = 0
|
|
|
|
int bits = 1;
|
|
|
|
// The font family is in 4 bits
|
|
// (2 would be enough right now, but who knows, it might grow)
|
|
|
|
int fam = id >> bits;
|
|
bits += 4;
|
|
tprop->SetFontFamily((fam & ((1 << 4) - 1))+ tprop->GetFontFamilyMinValue());
|
|
|
|
// Bold is in 1 bit
|
|
|
|
int bold = id >> bits;
|
|
bits++;
|
|
tprop->SetBold(bold & 0x1);
|
|
|
|
// Italic is in 1 bit
|
|
|
|
int italic = id >> bits;
|
|
bits++;
|
|
tprop->SetItalic(italic & 0x1);
|
|
|
|
// Orientation (in degrees)
|
|
// We need 9 bits for 0 to 360. What do we need for more precisions:
|
|
// - 1/10th degree: 12 bits (11.8)
|
|
|
|
int angle = id >> bits;
|
|
tprop->SetOrientation((float)(angle & ((1 << 12) - 1)) / 10.0);
|
|
|
|
// We really should not use more than 32 bits
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
FT_CALLBACK_DEF(FT_Error)
|
|
vtkFreeTypeUtilitiesFaceRequester(FTC_FaceID face_id,
|
|
FT_Library lib,
|
|
FT_Pointer request_data,
|
|
FT_Face* face)
|
|
{
|
|
#if VTK_FTFC_DEBUG_CD
|
|
printf("vtkFreeTypeUtilitiesFaceRequester()\n");
|
|
#endif
|
|
|
|
FT_UNUSED(request_data);
|
|
|
|
vtkFreeTypeUtilities *self =
|
|
reinterpret_cast<vtkFreeTypeUtilities*>(request_data);
|
|
|
|
// Map the ID to a text property
|
|
|
|
vtkTextProperty *tprop = vtkTextProperty::New();
|
|
self->MapIdToTextProperty(reinterpret_cast<unsigned long>(face_id), tprop);
|
|
|
|
// Fonts, organized by [Family][Bold][Italic]
|
|
|
|
static EmbeddedFontStruct EmbeddedFonts[3][2][2] =
|
|
{
|
|
{
|
|
{
|
|
{ // VTK_ARIAL: Bold [ ] Italic [ ]
|
|
|
|
face_arial_buffer_length, face_arial_buffer
|
|
},
|
|
{ // VTK_ARIAL: Bold [ ] Italic [x]
|
|
face_arial_italic_buffer_length, face_arial_italic_buffer
|
|
}
|
|
},
|
|
{
|
|
{ // VTK_ARIAL: Bold [x] Italic [ ]
|
|
face_arial_bold_buffer_length, face_arial_bold_buffer
|
|
},
|
|
{ // VTK_ARIAL: Bold [x] Italic [x]
|
|
face_arial_bold_italic_buffer_length, face_arial_bold_italic_buffer
|
|
}
|
|
}
|
|
},
|
|
{
|
|
{
|
|
{ // VTK_COURIER: Bold [ ] Italic [ ]
|
|
face_courier_buffer_length, face_courier_buffer
|
|
},
|
|
{ // VTK_COURIER: Bold [ ] Italic [x]
|
|
face_courier_italic_buffer_length, face_courier_italic_buffer
|
|
}
|
|
},
|
|
{
|
|
{ // VTK_COURIER: Bold [x] Italic [ ]
|
|
face_courier_bold_buffer_length, face_courier_bold_buffer
|
|
},
|
|
{ // VTK_COURIER: Bold [x] Italic [x]
|
|
face_courier_bold_italic_buffer_length,
|
|
face_courier_bold_italic_buffer
|
|
}
|
|
}
|
|
},
|
|
{
|
|
{
|
|
{ // VTK_TIMES: Bold [ ] Italic [ ]
|
|
face_times_buffer_length, face_times_buffer
|
|
},
|
|
{ // VTK_TIMES: Bold [ ] Italic [x]
|
|
face_times_italic_buffer_length, face_times_italic_buffer
|
|
}
|
|
},
|
|
{
|
|
{ // VTK_TIMES: Bold [x] Italic [ ]
|
|
face_times_bold_buffer_length, face_times_bold_buffer
|
|
},
|
|
{ // VTK_TIMES: Bold [x] Italic [x]
|
|
face_times_bold_italic_buffer_length, face_times_bold_italic_buffer
|
|
}
|
|
}
|
|
}
|
|
};
|
|
|
|
FT_Long length = EmbeddedFonts
|
|
[tprop->GetFontFamily()][tprop->GetBold()][tprop->GetItalic()].length;
|
|
FT_Byte *ptr = EmbeddedFonts
|
|
[tprop->GetFontFamily()][tprop->GetBold()][tprop->GetItalic()].ptr;
|
|
|
|
// Create a new face
|
|
|
|
FT_Error error = FT_New_Memory_Face(lib, ptr, length, 0, face);
|
|
if (error)
|
|
{
|
|
vtkErrorWithObjectMacro(
|
|
tprop,
|
|
<< "Unable to create font !" << " (family: " << tprop->GetFontFamily()
|
|
<< ", bold: " << tprop->GetBold() << ", italic: " << tprop->GetItalic()
|
|
<< ", length: " << length << ")");
|
|
}
|
|
else
|
|
{
|
|
#if VTK_FTFC_DEBUG
|
|
cout << "Requested: " << *face
|
|
<< " (F: " << tprop->GetFontFamily()
|
|
<< ", B: " << tprop->GetBold()
|
|
<< ", I: " << tprop->GetItalic()
|
|
<< ", O: " << tprop->GetOrientation() << ")" << endl;
|
|
#endif
|
|
if (tprop->GetOrientation() != 0.0)
|
|
{
|
|
// FreeType documentation says that the transform should not be set
|
|
// but we cache faces also by transform, so that there is a unique
|
|
// (face, orientation) cache entry
|
|
|
|
FT_Matrix matrix;
|
|
float angle = vtkMath::DegreesToRadians() * tprop->GetOrientation();
|
|
matrix.xx = (FT_Fixed)( cos(angle) * 0x10000L);
|
|
matrix.xy = (FT_Fixed)(-sin(angle) * 0x10000L);
|
|
matrix.yx = (FT_Fixed)( sin(angle) * 0x10000L);
|
|
matrix.yy = (FT_Fixed)( cos(angle) * 0x10000L);
|
|
FT_Set_Transform(*face, &matrix, NULL);
|
|
}
|
|
}
|
|
|
|
tprop->Delete();
|
|
|
|
return error;
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
void vtkFreeTypeUtilities::InitializeCacheManager()
|
|
{
|
|
#if VTK_FTFC_DEBUG_CD
|
|
printf("vtkFreeTypeUtilities::InitializeCacheManager()\n");
|
|
#endif
|
|
|
|
this->ReleaseCacheManager();
|
|
|
|
FT_Error error;
|
|
|
|
// Create the cache manager itself
|
|
|
|
this->CacheManager = new FTC_Manager;
|
|
|
|
error = FTC_Manager_New(*this->GetLibrary(),
|
|
this->MaximumNumberOfFaces,
|
|
this->MaximumNumberOfSizes,
|
|
this->MaximumNumberOfBytes,
|
|
vtkFreeTypeUtilitiesFaceRequester,
|
|
(FT_Pointer)this,
|
|
this->CacheManager);
|
|
|
|
if (error)
|
|
{
|
|
vtkErrorMacro(<< "Failed allocating a new FreeType Cache Manager");
|
|
}
|
|
|
|
// The image cache
|
|
|
|
this->ImageCache = new FTC_ImageCache;
|
|
|
|
error = FTC_ImageCache_New(*this->CacheManager, this->ImageCache);
|
|
|
|
if (error)
|
|
{
|
|
vtkErrorMacro(<< "Failed allocating a new FreeType Image Cache");
|
|
}
|
|
|
|
// The charmap cache
|
|
|
|
this->CMapCache = new FTC_CMapCache;
|
|
|
|
error = FTC_CMapCache_New(*this->CacheManager, this->CMapCache);
|
|
|
|
if (error)
|
|
{
|
|
vtkErrorMacro(<< "Failed allocating a new FreeType CMap Cache");
|
|
}
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
void vtkFreeTypeUtilities::ReleaseCacheManager()
|
|
{
|
|
#if VTK_FTFC_DEBUG_CD
|
|
printf("vtkFreeTypeUtilities::ReleaseCacheManager()\n");
|
|
#endif
|
|
|
|
if (this->CacheManager)
|
|
{
|
|
FTC_Manager_Done(*this->CacheManager);
|
|
|
|
delete this->CacheManager;
|
|
this->CacheManager = NULL;
|
|
}
|
|
|
|
if (this->ImageCache)
|
|
{
|
|
delete this->ImageCache;
|
|
this->ImageCache = NULL;
|
|
}
|
|
|
|
if (this->CMapCache)
|
|
{
|
|
delete this->CMapCache;
|
|
this->CMapCache = NULL;
|
|
}
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
int vtkFreeTypeUtilities::GetSize(unsigned long tprop_cache_id,
|
|
int font_size,
|
|
FT_Size *size)
|
|
{
|
|
#if VTK_FTFC_DEBUG_CD
|
|
printf("vtkFreeTypeUtilities::GetSize()\n");
|
|
#endif
|
|
|
|
if (!size || font_size <= 0)
|
|
{
|
|
vtkErrorMacro(<< "Wrong parameters, size is NULL or invalid font size");
|
|
return 0;
|
|
}
|
|
|
|
FTC_Manager *manager = this->GetCacheManager();
|
|
if (!manager)
|
|
{
|
|
vtkErrorMacro(<< "Failed querying the cache manager !");
|
|
return 0;
|
|
}
|
|
|
|
// Map the id of a text property in the cache to a FTC_FaceID
|
|
|
|
#if (FREETYPE_MAJOR >=2 && FREETYPE_MINOR >= 1 && FREETYPE_PATCH >= 9)
|
|
FTC_FaceID face_id = reinterpret_cast<FTC_FaceID>(tprop_cache_id);
|
|
|
|
FTC_ScalerRec scaler_rec;
|
|
scaler_rec.face_id = face_id;
|
|
scaler_rec.width = font_size;
|
|
scaler_rec.height = font_size;
|
|
scaler_rec.pixel = 1;
|
|
|
|
FT_Error error = FTC_Manager_LookupSize(*manager, &scaler_rec, size);
|
|
if (error)
|
|
{
|
|
vtkErrorMacro(<< "Failed looking up a FreeType Size");
|
|
}
|
|
|
|
return error ? 0 : 1;
|
|
#else
|
|
return 0;
|
|
#endif
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
int vtkFreeTypeUtilities::GetSize(vtkTextProperty *tprop,
|
|
FT_Size *size)
|
|
{
|
|
if (!tprop)
|
|
{
|
|
vtkErrorMacro(<< "Wrong parameters, text property is NULL");
|
|
return 0;
|
|
}
|
|
|
|
// Map the text property to a unique id that will be used as face id
|
|
|
|
unsigned long tprop_cache_id;
|
|
this->MapTextPropertyToId(tprop, &tprop_cache_id);
|
|
|
|
return this->GetSize(tprop_cache_id, tprop->GetFontSize(), size);
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
int vtkFreeTypeUtilities::GetFace(unsigned long tprop_cache_id,
|
|
FT_Face *face)
|
|
{
|
|
#if VTK_FTFC_DEBUG_CD
|
|
printf("vtkFreeTypeUtilities::GetFace()\n");
|
|
#endif
|
|
|
|
if (!face)
|
|
{
|
|
vtkErrorMacro(<< "Wrong parameters, face is NULL");
|
|
return 0;
|
|
}
|
|
|
|
FTC_Manager *manager = this->GetCacheManager();
|
|
if (!manager)
|
|
{
|
|
vtkErrorMacro(<< "Failed querying the cache manager !");
|
|
return 0;
|
|
}
|
|
|
|
// Map the id of a text property in the cache to a FTC_FaceID
|
|
|
|
FTC_FaceID face_id = reinterpret_cast<FTC_FaceID>(tprop_cache_id);
|
|
|
|
FT_Error error = FTC_Manager_LookupFace(*manager, face_id, face);
|
|
if (error)
|
|
{
|
|
vtkErrorMacro(<< "Failed looking up a FreeType Face");
|
|
}
|
|
|
|
return error ? 0 : 1;
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
int vtkFreeTypeUtilities::GetFace(vtkTextProperty *tprop,
|
|
FT_Face *face)
|
|
{
|
|
if (!tprop)
|
|
{
|
|
vtkErrorMacro(<< "Wrong parameters, face is NULL");
|
|
return 0;
|
|
}
|
|
|
|
// Map the text property to a unique id that will be used as face id
|
|
|
|
unsigned long tprop_cache_id;
|
|
this->MapTextPropertyToId(tprop, &tprop_cache_id);
|
|
|
|
return this->GetFace(tprop_cache_id, face);
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
int vtkFreeTypeUtilities::GetGlyphIndex(unsigned long tprop_cache_id,
|
|
char c,
|
|
FT_UInt *gindex)
|
|
{
|
|
#if VTK_FTFC_DEBUG_CD
|
|
printf("vtkFreeTypeUtilities::GetGlyphIndex()\n");
|
|
#endif
|
|
|
|
if (!gindex)
|
|
{
|
|
vtkErrorMacro(<< "Wrong parameters, gindex is NULL");
|
|
return 0;
|
|
}
|
|
|
|
FTC_CMapCache *cmap_cache = this->GetCMapCache();
|
|
if (!cmap_cache)
|
|
{
|
|
vtkErrorMacro(<< "Failed querying the charmap cache manager !");
|
|
return 0;
|
|
}
|
|
|
|
// Map the id of a text property in the cache to a FTC_FaceID
|
|
|
|
FTC_FaceID face_id = reinterpret_cast<FTC_FaceID>(tprop_cache_id);
|
|
|
|
// Lookup the glyph index
|
|
|
|
#if (FREETYPE_MAJOR >=2 && FREETYPE_MINOR >= 1 && FREETYPE_PATCH >= 9)
|
|
*gindex = FTC_CMapCache_Lookup(*cmap_cache, face_id, 0, c);
|
|
|
|
return *gindex ? 1 : 0;
|
|
#else
|
|
return 0;
|
|
#endif
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
int vtkFreeTypeUtilities::GetGlyphIndex(vtkTextProperty *tprop,
|
|
char c,
|
|
FT_UInt *gindex)
|
|
{
|
|
if (!tprop)
|
|
{
|
|
vtkErrorMacro(<< "Wrong parameters, text property is NULL");
|
|
return 0;
|
|
}
|
|
|
|
// Map the text property to a unique id that will be used as face id
|
|
|
|
unsigned long tprop_cache_id;
|
|
this->MapTextPropertyToId(tprop, &tprop_cache_id);
|
|
|
|
return this->GetGlyphIndex(tprop_cache_id, c, gindex);
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
int vtkFreeTypeUtilities::GetGlyph(unsigned long tprop_cache_id,
|
|
int font_size,
|
|
FT_UInt gindex,
|
|
FT_Glyph *glyph,
|
|
int request)
|
|
{
|
|
#if VTK_FTFC_DEBUG_CD
|
|
printf("vtkFreeTypeUtilities::GetGlyph()\n");
|
|
#endif
|
|
|
|
if (!glyph)
|
|
{
|
|
vtkErrorMacro(<< "Wrong parameters, one of them is NULL");
|
|
return 0;
|
|
}
|
|
|
|
FTC_ImageCache *image_cache = this->GetImageCache();
|
|
if (!image_cache)
|
|
{
|
|
vtkErrorMacro(<< "Failed querying the image cache manager !");
|
|
return 0;
|
|
}
|
|
|
|
// Map the id of a text property in the cache to a FTC_FaceID
|
|
|
|
FTC_FaceID face_id = reinterpret_cast<FTC_FaceID>(tprop_cache_id);
|
|
|
|
// Which font are we looking for
|
|
|
|
#if (FREETYPE_MAJOR >=2 && FREETYPE_MINOR >= 1 && FREETYPE_PATCH >= 9)
|
|
FTC_ImageTypeRec image_type_rec;
|
|
image_type_rec.face_id = face_id;
|
|
image_type_rec.width = font_size;
|
|
image_type_rec.height = font_size;
|
|
image_type_rec.flags = FT_LOAD_DEFAULT;
|
|
if (request == GLYPH_REQUEST_BITMAP)
|
|
{
|
|
image_type_rec.flags |= FT_LOAD_RENDER;
|
|
}
|
|
else if (request == GLYPH_REQUEST_OUTLINE)
|
|
{
|
|
image_type_rec.flags |= FT_LOAD_NO_BITMAP;
|
|
}
|
|
|
|
// Lookup the glyph
|
|
|
|
FT_Error error = FTC_ImageCache_Lookup(
|
|
*image_cache, &image_type_rec, gindex, glyph, NULL);
|
|
|
|
return error ? 0 : 1;
|
|
#else
|
|
return 0;
|
|
#endif
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
int vtkFreeTypeUtilities::GetGlyph(vtkTextProperty *tprop,
|
|
char c,
|
|
FT_Glyph *glyph,
|
|
int request)
|
|
{
|
|
if (!tprop)
|
|
{
|
|
vtkErrorMacro(<< "Wrong parameters, text property is NULL");
|
|
return 0;
|
|
}
|
|
|
|
// Map the text property to a unique id that will be used as face id
|
|
|
|
unsigned long tprop_cache_id;
|
|
this->MapTextPropertyToId(tprop, &tprop_cache_id);
|
|
|
|
// Get the character/glyph index
|
|
|
|
FT_UInt gindex;
|
|
if (!this->GetGlyphIndex(tprop_cache_id, c, &gindex))
|
|
{
|
|
vtkErrorMacro(<< "Failed querying a glyph index");
|
|
return 0;
|
|
}
|
|
|
|
// Get the glyph
|
|
|
|
return this->GetGlyph(
|
|
tprop_cache_id, tprop->GetFontSize(), gindex, glyph, request);
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
int vtkFreeTypeUtilities::IsBoundingBoxValid(int bbox[4])
|
|
{
|
|
return (!bbox ||
|
|
bbox[0] == VTK_INT_MAX || bbox[1] == VTK_INT_MIN ||
|
|
bbox[2] == VTK_INT_MAX || bbox[3] == VTK_INT_MIN) ? 0 : 1;
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
int vtkFreeTypeUtilities::GetBoundingBox(vtkTextProperty *tprop,
|
|
const char *str,
|
|
int bbox[4])
|
|
{
|
|
// We need the tprop and bbox
|
|
|
|
if (!tprop || !bbox)
|
|
{
|
|
vtkErrorMacro(<< "Wrong parameters, one of them is NULL or zero");
|
|
return 0;
|
|
}
|
|
|
|
// Initialize bbox to some large values
|
|
|
|
bbox[0] = bbox[2] = VTK_INT_MAX;
|
|
bbox[1] = bbox[3] = VTK_INT_MIN;
|
|
|
|
// No string to render, bail out now
|
|
|
|
if (!str)
|
|
{
|
|
return 1;
|
|
}
|
|
|
|
// Map the text property to a unique id that will be used as face id
|
|
|
|
unsigned long tprop_cache_id;
|
|
this->MapTextPropertyToId(tprop, &tprop_cache_id);
|
|
|
|
// Get the face
|
|
|
|
FT_Face face;
|
|
if (!this->GetFace(tprop_cache_id, &face))
|
|
{
|
|
vtkErrorMacro(<< "Failed retrieving the face");
|
|
return 0;
|
|
}
|
|
|
|
int face_has_kerning = FT_HAS_KERNING(face);
|
|
|
|
// Iterate char by char
|
|
|
|
FT_Glyph glyph;
|
|
FT_BitmapGlyph bitmap_glyph;
|
|
FT_Bitmap *bitmap;
|
|
FT_UInt gindex, previous_gindex = 0;
|
|
FT_Vector kerning_delta;
|
|
|
|
int x = 0, y = 0;
|
|
|
|
for (; *str; str++)
|
|
{
|
|
// Get the glyph index
|
|
|
|
if (!this->GetGlyphIndex(tprop_cache_id, *str, &gindex))
|
|
{
|
|
continue;
|
|
}
|
|
|
|
// Get the glyph as a bitmap
|
|
|
|
if (!this->GetGlyph(tprop_cache_id,
|
|
tprop->GetFontSize(),
|
|
gindex,
|
|
&glyph,
|
|
vtkFreeTypeUtilities::GLYPH_REQUEST_BITMAP) ||
|
|
glyph->format != FT_GLYPH_FORMAT_BITMAP)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
bitmap_glyph = reinterpret_cast<FT_BitmapGlyph>(glyph);
|
|
bitmap = &bitmap_glyph->bitmap;
|
|
|
|
if (bitmap->width && bitmap->rows)
|
|
{
|
|
// Starting position given the bearings
|
|
|
|
// Substract 1 to the bearing Y, because this is the vertical distance
|
|
// from the glyph origin (0,0) to the topmost pixel of the glyph bitmap
|
|
// (more precisely, to the pixel just above the bitmap). This distance is
|
|
// expressed in integer pixels, and is positive for upwards y.
|
|
|
|
int pen_x = x + bitmap_glyph->left;
|
|
int pen_y = y + bitmap_glyph->top - 1;
|
|
|
|
// Add the kerning
|
|
|
|
if (face_has_kerning && previous_gindex && gindex)
|
|
{
|
|
FT_Get_Kerning(
|
|
face, previous_gindex, gindex, FT_KERNING_DEFAULT, &kerning_delta);
|
|
pen_x += kerning_delta.x >> 6;
|
|
pen_y += kerning_delta.y >> 6;
|
|
}
|
|
|
|
previous_gindex = gindex;
|
|
|
|
// Update bounding box
|
|
|
|
if (pen_x < bbox[0])
|
|
{
|
|
bbox[0] = pen_x;
|
|
}
|
|
if (pen_x > bbox[1])
|
|
{
|
|
bbox[1] = pen_x;
|
|
}
|
|
if (pen_y < bbox[2])
|
|
{
|
|
bbox[2] = pen_y;
|
|
}
|
|
if (pen_y > bbox[3])
|
|
{
|
|
bbox[3] = pen_y;
|
|
}
|
|
|
|
pen_x += (bitmap->width - 1);
|
|
pen_y -= (bitmap->rows - 1);
|
|
|
|
if (pen_x < bbox[0])
|
|
{
|
|
bbox[0] = pen_x;
|
|
}
|
|
if (pen_x > bbox[1])
|
|
{
|
|
bbox[1] = pen_x;
|
|
}
|
|
if (pen_y < bbox[2])
|
|
{
|
|
bbox[2] = pen_y;
|
|
}
|
|
if (pen_y > bbox[3])
|
|
{
|
|
bbox[3] = pen_y;
|
|
}
|
|
}
|
|
|
|
// Advance to next char
|
|
|
|
x += (bitmap_glyph->root.advance.x + 0x8000) >> 16;
|
|
y += (bitmap_glyph->root.advance.y + 0x8000) >> 16;
|
|
}
|
|
|
|
// Margin for shadow (x + 1, y - 1)
|
|
|
|
if (tprop->GetShadow() && this->IsBoundingBoxValid(bbox))
|
|
{
|
|
bbox[1]++;
|
|
bbox[2]--;
|
|
}
|
|
|
|
return 1;
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
template <class T>
|
|
int vtkFreeTypeUtilitiesRenderString(
|
|
vtkFreeTypeUtilities *self,
|
|
vtkTextProperty *tprop,
|
|
const char *str,
|
|
int x, int y,
|
|
vtkImageData *data,
|
|
T *vtkNotUsed(ptr),
|
|
int use_shadow_color)
|
|
{
|
|
// Map the text property to a unique id that will be used as face id
|
|
|
|
unsigned long tprop_cache_id;
|
|
self->MapTextPropertyToId(tprop, &tprop_cache_id);
|
|
|
|
// Get the face
|
|
|
|
FT_Face face;
|
|
if (!self->GetFace(tprop_cache_id, &face))
|
|
{
|
|
vtkErrorWithObjectMacro(self, << "Failed retrieving the face");
|
|
return 0;
|
|
}
|
|
|
|
int face_has_kerning = FT_HAS_KERNING(face);
|
|
|
|
// Text property size and opacity
|
|
|
|
int tprop_font_size = tprop->GetFontSize();
|
|
|
|
float tprop_opacity = tprop->GetOpacity();
|
|
|
|
// Text color (get the shadow color if we are actually drawing the shadow)
|
|
// Also compute the luminance, if we are drawing to a grayscale image
|
|
|
|
double color[3];
|
|
if (use_shadow_color)
|
|
{
|
|
tprop->GetShadowColor(color);
|
|
}
|
|
else
|
|
{
|
|
tprop->GetColor(color);
|
|
}
|
|
float tprop_r = color[0];
|
|
float tprop_g = color[1];
|
|
float tprop_b = color[2];
|
|
|
|
float tprop_l = 0.3 * tprop_r + 0.59 * tprop_g + 0.11 * tprop_b;
|
|
|
|
// Image params (comps, increments, range)
|
|
|
|
int data_nb_comp = data->GetNumberOfScalarComponents();
|
|
|
|
vtkIdType data_inc_x, data_inc_y, data_inc_z;
|
|
data->GetIncrements(data_inc_x, data_inc_y, data_inc_z);
|
|
|
|
double data_min, data_max;
|
|
if (data->GetScalarType() == VTK_DOUBLE ||
|
|
data->GetScalarType() == VTK_FLOAT)
|
|
{
|
|
data_min = 0.0;
|
|
data_max = 1.0;
|
|
}
|
|
else
|
|
{
|
|
data_min = data->GetScalarTypeMin();
|
|
data_max = data->GetScalarTypeMax();
|
|
}
|
|
double data_range = (data_max - data_min);
|
|
|
|
// Render char by char
|
|
|
|
FT_Glyph glyph;
|
|
FT_BitmapGlyph bitmap_glyph;
|
|
FT_Bitmap *bitmap;
|
|
FT_UInt gindex, previous_gindex = 0;
|
|
FT_Vector kerning_delta;
|
|
|
|
for (; *str; str++)
|
|
{
|
|
// Get the glyph index
|
|
|
|
if (!self->GetGlyphIndex(tprop_cache_id, *str, &gindex))
|
|
{
|
|
continue;
|
|
}
|
|
|
|
// Get the glyph as a bitmap
|
|
|
|
if (!self->GetGlyph(tprop_cache_id,
|
|
tprop_font_size,
|
|
gindex,
|
|
&glyph,
|
|
vtkFreeTypeUtilities::GLYPH_REQUEST_BITMAP) ||
|
|
glyph->format != FT_GLYPH_FORMAT_BITMAP)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
bitmap_glyph = reinterpret_cast<FT_BitmapGlyph>(glyph);
|
|
bitmap = &bitmap_glyph->bitmap;
|
|
|
|
if (bitmap->pixel_mode != FT_PIXEL_MODE_GRAY)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
if (bitmap->width && bitmap->rows)
|
|
{
|
|
// Starting position given the bearings
|
|
|
|
// Substract 1 to the bearing Y, because this is the vertical distance
|
|
// from the glyph origin (0,0) to the topmost pixel of the glyph bitmap
|
|
// (more precisely, to the pixel just above the bitmap). This distance is
|
|
// expressed in integer pixels, and is positive for upwards y.
|
|
|
|
#if VTK_FTFC_DEBUG
|
|
cout << *str << ", orient: " << tprop->GetOrientation() << ", x: " << x << ", y: " << y << ", left: " << bitmap_glyph->left << ", top: " << bitmap_glyph->top << ", width: " << bitmap->width << ", rows: " << bitmap->rows << endl;
|
|
#endif
|
|
|
|
int pen_x = x + bitmap_glyph->left;
|
|
int pen_y = y + bitmap_glyph->top - 1;
|
|
|
|
// Add the kerning
|
|
|
|
if (face_has_kerning && previous_gindex && gindex)
|
|
{
|
|
FT_Get_Kerning(
|
|
face, previous_gindex, gindex, FT_KERNING_DEFAULT, &kerning_delta);
|
|
pen_x += kerning_delta.x >> 6;
|
|
pen_y += kerning_delta.y >> 6;
|
|
}
|
|
|
|
previous_gindex = gindex;
|
|
|
|
// Render
|
|
|
|
T *data_ptr = (T*)data->GetScalarPointer(pen_x, pen_y, 0);
|
|
|
|
int data_pitch = (-data->GetDimensions()[0] - bitmap->width) * data_inc_x;
|
|
|
|
unsigned char *glyph_ptr_row = bitmap->buffer;
|
|
unsigned char *glyph_ptr;
|
|
|
|
float t_alpha, t_1_m_alpha, data_alpha;
|
|
|
|
int i, j;
|
|
for (j = 0; j < bitmap->rows; j++)
|
|
{
|
|
glyph_ptr = glyph_ptr_row;
|
|
|
|
// That loop should probably be located inside the switch for efficency
|
|
|
|
for (i = 0; i < bitmap->width; i++)
|
|
{
|
|
t_alpha = tprop_opacity * (*glyph_ptr / 255.0);
|
|
t_1_m_alpha = 1.0 - t_alpha;
|
|
|
|
switch (data_nb_comp)
|
|
{
|
|
// L
|
|
|
|
case 1:
|
|
*data_ptr = (T)(
|
|
(data_min + data_range * tprop_l * t_alpha) +
|
|
*data_ptr * t_1_m_alpha);
|
|
glyph_ptr++;
|
|
data_ptr++;
|
|
break;
|
|
|
|
// L,A
|
|
// TOFIX: that code is so wrong (alpha)
|
|
|
|
case 2:
|
|
data_alpha = (data_ptr[1] - data_min) / data_range;
|
|
*data_ptr = (T)(
|
|
(data_min + data_range * tprop_l * t_alpha) +
|
|
(*data_ptr * data_alpha) * t_1_m_alpha);
|
|
data_ptr++;
|
|
*data_ptr = (T)(
|
|
data_min + data_range * (t_alpha + data_alpha * t_1_m_alpha));
|
|
data_ptr++;
|
|
glyph_ptr++;
|
|
break;
|
|
|
|
// RGB
|
|
|
|
case 3:
|
|
*data_ptr = (T)(
|
|
(data_min + data_range * tprop_r * t_alpha) +
|
|
*data_ptr * t_1_m_alpha);
|
|
data_ptr++;
|
|
*data_ptr = (T)(
|
|
(data_min + data_range * tprop_g * t_alpha) +
|
|
*data_ptr * t_1_m_alpha);
|
|
data_ptr++;
|
|
*data_ptr = (T)(
|
|
(data_min + data_range * tprop_b * t_alpha) +
|
|
*data_ptr * t_1_m_alpha);
|
|
data_ptr++;
|
|
glyph_ptr++;
|
|
break;
|
|
|
|
// RGB,A
|
|
// TOFIX: that code is so wrong (alpha)
|
|
|
|
case 4:
|
|
data_alpha = (data_ptr[1] - data_min) / data_range;
|
|
*data_ptr = (T)(
|
|
(data_min + data_range * tprop_r * t_alpha) +
|
|
(*data_ptr * data_alpha) * t_1_m_alpha);
|
|
data_ptr++;
|
|
*data_ptr = (T)(
|
|
(data_min + data_range * tprop_g * t_alpha) +
|
|
(*data_ptr * data_alpha) * t_1_m_alpha);
|
|
data_ptr++;
|
|
*data_ptr = (T)(
|
|
(data_min + data_range * tprop_b * t_alpha) +
|
|
(*data_ptr * data_alpha) * t_1_m_alpha);
|
|
data_ptr++;
|
|
*data_ptr = (T)(
|
|
data_min + data_range * (t_alpha + data_alpha * t_1_m_alpha));
|
|
data_ptr++;
|
|
glyph_ptr++;
|
|
break;
|
|
}
|
|
}
|
|
glyph_ptr_row += bitmap->pitch;
|
|
data_ptr += data_pitch;
|
|
}
|
|
}
|
|
|
|
// Advance to next char
|
|
|
|
x += (bitmap_glyph->root.advance.x + 0x8000) >> 16;
|
|
y += (bitmap_glyph->root.advance.y + 0x8000) >> 16;
|
|
}
|
|
|
|
return 1;
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
int vtkFreeTypeUtilities::RenderString(vtkTextProperty *tprop,
|
|
const char *str,
|
|
int x, int y,
|
|
vtkImageData *data)
|
|
{
|
|
// Check
|
|
|
|
if (!tprop || !str || !data)
|
|
{
|
|
vtkErrorMacro(<< "Wrong parameters, one of them is NULL or zero");
|
|
return 0;
|
|
}
|
|
|
|
if (data->GetNumberOfScalarComponents() > 4)
|
|
{
|
|
vtkErrorMacro("The image data must have a maximum of four components");
|
|
return 0;
|
|
}
|
|
|
|
// Execute shadow
|
|
|
|
int res = 1;
|
|
|
|
if (tprop->GetShadow())
|
|
{
|
|
switch (data->GetScalarType())
|
|
{
|
|
vtkTemplateMacro(res &= vtkFreeTypeUtilitiesRenderString(
|
|
this,
|
|
tprop,
|
|
str,
|
|
x + 1, y - 1,
|
|
data,
|
|
(VTK_TT *)(NULL),
|
|
1));
|
|
default:
|
|
vtkErrorMacro(<< "Execute: Unknown ScalarType");
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
// Execute text
|
|
|
|
switch (data->GetScalarType())
|
|
{
|
|
vtkTemplateMacro(res &= vtkFreeTypeUtilitiesRenderString(
|
|
this,
|
|
tprop,
|
|
str,
|
|
x, y,
|
|
data,
|
|
(VTK_TT *)(NULL),
|
|
0));
|
|
default:
|
|
vtkErrorMacro(<< "Execute: Unknown ScalarType");
|
|
return 0;
|
|
}
|
|
|
|
return res;
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
void vtkFreeTypeUtilities::PrintSelf(ostream& os, vtkIndent indent)
|
|
{
|
|
this->Superclass::PrintSelf(os,indent);
|
|
|
|
os << indent << "MaximumNumberOfFaces: "
|
|
<< this->MaximumNumberOfFaces << endl;
|
|
|
|
os << indent << "MaximumNumberOfSizes: "
|
|
<< this->MaximumNumberOfSizes << endl;
|
|
|
|
os << indent << "MaximumNumberOfBytes: "
|
|
<< this->MaximumNumberOfBytes << endl;
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
// Print entry
|
|
|
|
void vtkFreeTypeUtilities::PrintEntry(int i, char *msg)
|
|
{
|
|
if (!this->Entries[i])
|
|
{
|
|
return;
|
|
}
|
|
|
|
printf("%s: [%2d] =", msg, i);
|
|
|
|
vtkTextProperty *tprop = this->Entries[i]->TextProperty;
|
|
if (tprop)
|
|
{
|
|
printf(" [S: %2d]", tprop->GetFontSize());
|
|
|
|
double *color = tprop->GetColor();
|
|
if (color)
|
|
{
|
|
printf(" [RGBA: %.2f/%.2f/%.2f (%.2f)]",
|
|
color[0], color[1], color[2], tprop->GetOpacity());
|
|
}
|
|
|
|
/*
|
|
if (tprop->GetFaceFileName())
|
|
{
|
|
printf(" [F: %s]", tprop->GetFaceFileName());
|
|
}
|
|
else
|
|
*/
|
|
{
|
|
printf(" [F: %d] [I: %d] [B: %d]",
|
|
tprop->GetFontFamily(), tprop->GetItalic(), tprop->GetBold());
|
|
}
|
|
}
|
|
|
|
if (this->Entries[i]->Font)
|
|
{
|
|
printf(" [F: %p]", (void*)this->Entries[i]->Font);
|
|
printf("\n [f: %p]",
|
|
(void*)*(this->Entries[i]->Font->Face()->Face()));
|
|
}
|
|
|
|
printf("\n");
|
|
fflush(stdout);
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
// Release entry
|
|
|
|
void vtkFreeTypeUtilities::ReleaseEntry(int i)
|
|
{
|
|
if (!this->Entries[i])
|
|
{
|
|
return;
|
|
}
|
|
|
|
#if VTK_FTFC_DEBUG
|
|
this->PrintEntry(this->NumberOfEntries, "Rl");
|
|
#endif
|
|
|
|
if (this->Entries[i]->TextProperty)
|
|
{
|
|
this->Entries[i]->TextProperty->Delete();
|
|
this->Entries[i]->TextProperty = NULL;
|
|
}
|
|
|
|
if (this->Entries[i]->Font)
|
|
{
|
|
delete this->Entries[i]->Font;
|
|
this->Entries[i]->Font = NULL;
|
|
}
|
|
|
|
delete this->Entries[i];
|
|
this->Entries[i] = NULL;
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
// Initialize cache
|
|
|
|
void vtkFreeTypeUtilities::InitializeCache()
|
|
{
|
|
#if VTK_FTFC_DEBUG_CD
|
|
printf("vtkFreeTypeUtilities::InitializeCache()\n");
|
|
#endif
|
|
this->ReleaseCache();
|
|
|
|
int i;
|
|
for (i = 0; i < VTK_FTFC_CACHE_CAPACITY; i++)
|
|
{
|
|
this->Entries[i] = NULL;
|
|
}
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
// Release cache
|
|
|
|
void vtkFreeTypeUtilities::ReleaseCache()
|
|
{
|
|
#if VTK_FTFC_DEBUG_CD
|
|
printf("vtkFreeTypeUtilities::ReleaseCache()\n");
|
|
#endif
|
|
|
|
int i;
|
|
for (i = 0; i < this->NumberOfEntries; i++)
|
|
{
|
|
#if VTK_FTFC_DEBUG
|
|
this->PrintEntry(i, "Rl");
|
|
#endif
|
|
this->ReleaseEntry(i);
|
|
}
|
|
|
|
this->NumberOfEntries = 0;
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
// Get a font from the cache given the text property. If no font is
|
|
// found in the cache, one is created and stored with the given color
|
|
// parameters. If override_color is true, then red, green, blue are used as
|
|
// text color instead of the colors found in the vtkTextProperty.
|
|
|
|
vtkFreeTypeUtilities::Entry*
|
|
vtkFreeTypeUtilities::GetFont(vtkTextProperty *tprop,
|
|
double override_color[3])
|
|
{
|
|
int i, j;
|
|
|
|
// Get the requested color and opacity
|
|
|
|
double tprop_color[3];
|
|
for (i = 0; i < 3; i++)
|
|
{
|
|
tprop_color[i] = override_color ? override_color[i] : tprop->GetColor()[i];
|
|
if (tprop_color[i] < 0.0)
|
|
{
|
|
tprop_color[i] = 0.0;
|
|
}
|
|
}
|
|
|
|
float tprop_opacity =
|
|
(tprop->GetOpacity() < 0.0) ? 1.0 : tprop->GetOpacity();
|
|
|
|
// Has the font been cached ?
|
|
|
|
for (i = 0; i < this->NumberOfEntries; i++)
|
|
{
|
|
vtkTextProperty *entry_tprop = this->Entries[i]->TextProperty;
|
|
double *entry_tprop_color = entry_tprop->GetColor();
|
|
|
|
if (
|
|
// If a face file name has been specified, it overrides the
|
|
// font family as well as italic and bold attributes
|
|
|
|
((
|
|
/*
|
|
!entry_tprop->GetFaceFileName() && !tprop->GetFaceFileName() &&
|
|
*/
|
|
entry_tprop->GetFontFamily() == tprop->GetFontFamily() &&
|
|
entry_tprop->GetItalic() == tprop->GetItalic() &&
|
|
entry_tprop->GetBold() == tprop->GetBold())
|
|
/*
|
|
|| (entry_tprop->GetFaceFileName() && tprop->GetFaceFileName() &&
|
|
!strcmp(entry_tprop->GetFaceFileName(), tprop->GetFaceFileName()))
|
|
*/
|
|
)
|
|
|
|
&& (entry_tprop_color[0] == tprop_color[0] &&
|
|
entry_tprop_color[1] == tprop_color[1] &&
|
|
entry_tprop_color[2] == tprop_color[2] &&
|
|
entry_tprop->GetOpacity() == tprop_opacity)
|
|
|
|
&& entry_tprop->GetFontSize() == tprop->GetFontSize())
|
|
{
|
|
// Make this the most recently used
|
|
if (i != 0)
|
|
{
|
|
vtkFreeTypeUtilities::Entry *tmp = this->Entries[i];
|
|
for (j = i - 1; j >= 0; j--)
|
|
{
|
|
this->Entries[j+1] = this->Entries[j];
|
|
}
|
|
this->Entries[0] = tmp;
|
|
}
|
|
return this->Entries[0];
|
|
}
|
|
}
|
|
|
|
// OK the font is not cached, try to create one
|
|
|
|
FTFont *font = new FTGLPixmapFont;
|
|
|
|
// A face file name has been provided, try to load it, otherwise
|
|
// just use the embedded fonts (i.e. font family, bold and italic attribs)
|
|
|
|
/*
|
|
if (tprop->GetFaceFileName())
|
|
{
|
|
if (!font->Open(tprop->GetFaceFileName(), false))
|
|
{
|
|
vtkErrorWithObjectMacro(
|
|
tprop,
|
|
<< "Unable to load font " << tprop->GetFaceFileName());
|
|
delete font;
|
|
return 0;
|
|
}
|
|
|
|
// Try to load an AFM metrics file for the PFB/PFA Postscript fonts
|
|
|
|
int length = strlen(tprop->GetFaceFileName());
|
|
if (length > 4 &&
|
|
(!strcmp(tprop->GetFaceFileName() + length - 4, ".pfb") ||
|
|
!strcmp(tprop->GetFaceFileName() + length - 4, ".pfa")))
|
|
{
|
|
char *metrics = new char[length + 1];
|
|
strncpy(metrics, tprop->GetFaceFileName(), length - 3);
|
|
strcpy(metrics + length - 3, "afm");
|
|
struct stat fs;
|
|
if (stat(metrics, &fs) == 0)
|
|
{
|
|
font->Attach(metrics);
|
|
}
|
|
delete [] metrics;
|
|
}
|
|
}
|
|
else
|
|
*/
|
|
{
|
|
// Fonts, organized by [Family][Bold][Italic]
|
|
|
|
static EmbeddedFontStruct EmbeddedFonts[3][2][2] =
|
|
{
|
|
{
|
|
{
|
|
{ // VTK_ARIAL: Bold [ ] Italic [ ]
|
|
|
|
face_arial_buffer_length, face_arial_buffer
|
|
},
|
|
{ // VTK_ARIAL: Bold [ ] Italic [x]
|
|
face_arial_italic_buffer_length, face_arial_italic_buffer
|
|
}
|
|
},
|
|
{
|
|
{ // VTK_ARIAL: Bold [x] Italic [ ]
|
|
face_arial_bold_buffer_length, face_arial_bold_buffer
|
|
},
|
|
{ // VTK_ARIAL: Bold [x] Italic [x]
|
|
face_arial_bold_italic_buffer_length, face_arial_bold_italic_buffer
|
|
}
|
|
}
|
|
},
|
|
{
|
|
{
|
|
{ // VTK_COURIER: Bold [ ] Italic [ ]
|
|
face_courier_buffer_length, face_courier_buffer
|
|
},
|
|
{ // VTK_COURIER: Bold [ ] Italic [x]
|
|
face_courier_italic_buffer_length, face_courier_italic_buffer
|
|
}
|
|
},
|
|
{
|
|
{ // VTK_COURIER: Bold [x] Italic [ ]
|
|
face_courier_bold_buffer_length, face_courier_bold_buffer
|
|
},
|
|
{ // VTK_COURIER: Bold [x] Italic [x]
|
|
face_courier_bold_italic_buffer_length,
|
|
face_courier_bold_italic_buffer
|
|
}
|
|
}
|
|
},
|
|
{
|
|
{
|
|
{ // VTK_TIMES: Bold [ ] Italic [ ]
|
|
face_times_buffer_length, face_times_buffer
|
|
},
|
|
{ // VTK_TIMES: Bold [ ] Italic [x]
|
|
face_times_italic_buffer_length, face_times_italic_buffer
|
|
}
|
|
},
|
|
{
|
|
{ // VTK_TIMES: Bold [x] Italic [ ]
|
|
face_times_bold_buffer_length, face_times_bold_buffer
|
|
},
|
|
{ // VTK_TIMES: Bold [x] Italic [x]
|
|
face_times_bold_italic_buffer_length, face_times_bold_italic_buffer
|
|
}
|
|
}
|
|
}
|
|
};
|
|
|
|
size_t length = EmbeddedFonts
|
|
[tprop->GetFontFamily()][tprop->GetBold()][tprop->GetItalic()].length;
|
|
unsigned char *ptr = EmbeddedFonts
|
|
[tprop->GetFontFamily()][tprop->GetBold()][tprop->GetItalic()].ptr;
|
|
|
|
if (!font->Open(ptr, length, false))
|
|
{
|
|
vtkErrorWithObjectMacro(
|
|
tprop,
|
|
<< "Unable to create font !" << " (family: " << tprop->GetFontFamily()
|
|
<< ", bold: " << tprop->GetBold() << ", italic: " << tprop->GetItalic()
|
|
<< ", length: " << length << ")");
|
|
delete font;
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
// Set face size
|
|
|
|
font->FaceSize(tprop->GetFontSize());
|
|
|
|
// We need to make room for a new font
|
|
|
|
if (this->NumberOfEntries == VTK_FTFC_CACHE_CAPACITY)
|
|
{
|
|
#if VTK_FTFC_DEBUG
|
|
printf("Cache is full, deleting last!\n");
|
|
#endif
|
|
this->NumberOfEntries--;
|
|
}
|
|
|
|
// Add the new font
|
|
|
|
if (this->Entries[this->NumberOfEntries])
|
|
{
|
|
this->ReleaseEntry(this->NumberOfEntries);
|
|
}
|
|
this->Entries[this->NumberOfEntries] = new vtkFreeTypeUtilities::Entry;
|
|
|
|
// Set the other info
|
|
|
|
this->Entries[this->NumberOfEntries]->TextProperty = vtkTextProperty::New();
|
|
|
|
vtkTextProperty *entry_tprop =
|
|
this->Entries[this->NumberOfEntries]->TextProperty;
|
|
|
|
entry_tprop->ShallowCopy(tprop);
|
|
entry_tprop->SetOpacity(tprop_opacity);
|
|
entry_tprop->SetColor(tprop_color);
|
|
|
|
this->Entries[this->NumberOfEntries]->Font = font;
|
|
|
|
this->Entries[this->NumberOfEntries]->LargestAscender =
|
|
this->Entries[this->NumberOfEntries]->LargestDescender = -1;
|
|
|
|
#if VTK_FTFC_DEBUG
|
|
this->PrintEntry(this->NumberOfEntries, "Cr");
|
|
#endif
|
|
|
|
vtkFreeTypeUtilities::Entry *tmp = this->Entries[this->NumberOfEntries];
|
|
|
|
this->NumberOfEntries++;
|
|
return tmp;
|
|
}
|
|
|