Commit 5ff4f80b authored by David C. Lonie's avatar David C. Lonie
Browse files

Add unicode text support to charts and context2D.

This patch assumes all 8-bit strings rendering in the OpenGL context2D
device are UTF-8 encoded, and uses vtkUnicodeString::from_utf8 to allow
extended character sets to be used in Context2D 8-bit strings.

This also fixes an issue with UTF16 rendering alignment, and updates the
image cache to use 16-bit strings for freetype renderings and 8-bit strings
for mathtext rendering.

Change-Id: Ic5f905f0a2ba70919e5df59120ce7a8ee536945a
parent f112ee18
......@@ -75,6 +75,7 @@
#define VTK_COURIER 1
#define VTK_TIMES 2
#define VTK_UNKNOWN_FONT 3
#define VTK_FONT_FILE 4
#define VTK_TEXT_LEFT 0
#define VTK_TEXT_CENTERED 1
......
......@@ -797,7 +797,33 @@ void vtkOpenGLContextDevice2D::AlignText(double orientation, float width,
//-----------------------------------------------------------------------------
void vtkOpenGLContextDevice2D::DrawString(float *point,
const vtkStdString &string)
const vtkStdString &str)
{
// The &str[0] below will segfault if the string is empty:
if (size_t size = str.size())
{
const char *begin(&str[0]);
const char *end(begin + size);
this->DrawString(point, vtkUnicodeString::from_utf8(begin, end));
}
}
//-----------------------------------------------------------------------------
void vtkOpenGLContextDevice2D::ComputeStringBounds(const vtkStdString &str,
float bounds[4])
{
// The &str[0] below will segfault if the string is empty:
if (size_t size = str.size())
{
const char *begin(&str[0]);
const char *end(begin + size);
this->ComputeStringBounds(vtkUnicodeString::from_utf8(begin, end), bounds);
}
}
//-----------------------------------------------------------------------------
void vtkOpenGLContextDevice2D::DrawString(float *point,
const vtkUnicodeString &string)
{
vtkOpenGLClearErrorMacro();
......@@ -810,9 +836,9 @@ void vtkOpenGLContextDevice2D::DrawString(float *point,
std::floor(point[1] * yScale) / yScale };
// Cache rendered text strings
vtkTextureImageCache<TextPropertyKey>::CacheData &cache =
this->Storage->TextTextureCache.GetCacheData(
TextPropertyKey(this->TextProp, string));
vtkTextureImageCache<UTF16TextPropertyKey>::CacheData &cache =
this->Storage->TextTextureCache.GetCacheData(
UTF16TextPropertyKey(this->TextProp, string));
vtkImageData* image = cache.ImageData;
if (image->GetNumberOfPoints() == 0 && image->GetNumberOfCells() == 0)
{
......@@ -864,47 +890,6 @@ void vtkOpenGLContextDevice2D::DrawString(float *point,
vtkOpenGLCheckErrorMacro("failed after DrawString");
}
//-----------------------------------------------------------------------------
void vtkOpenGLContextDevice2D::ComputeStringBounds(const vtkStdString &string,
float bounds[4])
{
vtkVector2i box = this->TextRenderer->GetBounds(this->TextProp, string);
// Check for invalid bounding box
if (box[0] == VTK_INT_MIN || box[0] == VTK_INT_MAX ||
box[1] == VTK_INT_MIN || box[1] == VTK_INT_MAX)
{
bounds[0] = static_cast<float>(0);
bounds[1] = static_cast<float>(0);
bounds[2] = static_cast<float>(0);
bounds[3] = static_cast<float>(0);
return;
}
GLfloat mv[16];
glGetFloatv(GL_MODELVIEW_MATRIX, mv);
float xScale = mv[0];
float yScale = mv[5];
bounds[0] = static_cast<float>(0);
bounds[1] = static_cast<float>(0);
bounds[2] = static_cast<float>(box.GetX() / xScale);
bounds[3] = static_cast<float>(box.GetY() / yScale);
}
//-----------------------------------------------------------------------------
void vtkOpenGLContextDevice2D::DrawString(float *point,
const vtkUnicodeString &string)
{
int p[] = { static_cast<int>(point[0]),
static_cast<int>(point[1]) };
//TextRenderer draws in window, not viewport coords
p[0]+=this->Storage->Offset.GetX();
p[1]+=this->Storage->Offset.GetY();
vtkImageData *data = vtkImageData::New();
this->TextRenderer->RenderString(this->TextProp, string, data);
this->DrawImage(point, 1.0, data);
data->Delete();
}
//-----------------------------------------------------------------------------
void vtkOpenGLContextDevice2D::ComputeStringBounds(const vtkUnicodeString &string,
float bounds[4])
......@@ -948,9 +933,9 @@ void vtkOpenGLContextDevice2D::DrawMathTextString(float point[2],
float p[] = { std::floor(point[0]), std::floor(point[1]) };
// Cache rendered text strings
vtkTextureImageCache<TextPropertyKey>::CacheData &cache =
vtkTextureImageCache<UTF8TextPropertyKey>::CacheData &cache =
this->Storage->MathTextTextureCache.GetCacheData(
TextPropertyKey(this->TextProp, string));
UTF8TextPropertyKey(this->TextProp, string));
vtkImageData* image = cache.ImageData;
if (image->GetNumberOfPoints() == 0 && image->GetNumberOfCells() == 0)
{
......
......@@ -35,6 +35,9 @@
#include "vtkColor.h"
#include "vtkTextProperty.h"
#include "vtkFreeTypeTools.h"
#include "vtkStdString.h"
#include "vtkUnicodeString.h"
#include <algorithm>
#include <list>
#include <utility>
......@@ -162,6 +165,7 @@ typename vtkTextureImageCache<Key>::CacheData& vtkTextureImageCache<Key>
// .NAME TextPropertyKey - unique key for a vtkTextProperty and text
// .SECTION Description
// Uniquely describe a pair of vtkTextProperty and text.
template <class StringType>
struct TextPropertyKey
{
// Description:
......@@ -175,7 +179,7 @@ struct TextPropertyKey
// Description:
// Creates a TextPropertyKey.
TextPropertyKey(vtkTextProperty* textProperty, const vtkStdString& text)
TextPropertyKey(vtkTextProperty* textProperty, const StringType& text)
{
this->TextPropertyId = GetIdFromTextProperty(textProperty);
this->FontSize = textProperty->GetFontSize();
......@@ -206,9 +210,12 @@ struct TextPropertyKey
vtkColor4ub Color;
// States in the function not to use more than 32 bits - int works fine here.
unsigned int TextPropertyId;
vtkStdString Text;
StringType Text;
};
typedef TextPropertyKey<vtkStdString> UTF8TextPropertyKey;
typedef TextPropertyKey<vtkUnicodeString> UTF16TextPropertyKey;
class vtkOpenGLContextDevice2D::Private
{
public:
......@@ -502,8 +509,8 @@ public:
// Description:
// Cache for text images. Generating texture for strings is expensive,
// we cache the textures here for a faster reuse.
mutable vtkTextureImageCache<TextPropertyKey> TextTextureCache;
mutable vtkTextureImageCache<TextPropertyKey> MathTextTextureCache;
mutable vtkTextureImageCache<UTF16TextPropertyKey> TextTextureCache;
mutable vtkTextureImageCache<UTF8TextPropertyKey> MathTextTextureCache;
};
#endif // VTKOPENGLCONTEXTDEVICE2DPRIVATE_H
......
......@@ -27,6 +27,7 @@ vtkTextProperty::vtkTextProperty()
this->Opacity = 1.0;
this->FontFamilyAsString = 0;
this->FontFile = NULL;
this->SetFontFamilyAsString( "Arial" );
this->FontSize = 12;
......@@ -48,6 +49,7 @@ vtkTextProperty::vtkTextProperty()
vtkTextProperty::~vtkTextProperty()
{
this->SetFontFamilyAsString(NULL);
this->SetFontFile(NULL);
}
//----------------------------------------------------------------------------
......@@ -62,6 +64,7 @@ void vtkTextProperty::ShallowCopy(vtkTextProperty *tprop)
this->SetOpacity(tprop->GetOpacity());
this->SetFontFamilyAsString(tprop->GetFontFamilyAsString());
this->SetFontFile(tprop->GetFontFile());
this->SetFontSize(tprop->GetFontSize());
this->SetBold(tprop->GetBold());
......@@ -105,6 +108,8 @@ void vtkTextProperty::PrintSelf(ostream& os, vtkIndent indent)
os << indent << "FontFamilyAsString: "
<< (this->FontFamilyAsString ? this->FontFamilyAsString : "(null)") << endl;
os << indent << "FontFile: "
<< (this->FontFile ? this->FontFile : "(null)") << endl;
os << indent << "FontSize: " << this->FontSize << "\n";
os << indent << "Bold: " << (this->Bold ? "On\n" : "Off\n");
......
......@@ -51,6 +51,9 @@ public:
// Description:
// Set/Get the font family. Supports legacy three font family system.
// If the symbolic constant VTK_FONT_FILE is returned by GetFontFamily(), the
// string returned by GetFontFile() must be an absolute filepath
// to a local FreeType compatible font.
vtkGetStringMacro(FontFamilyAsString);
vtkSetStringMacro(FontFamilyAsString);
void SetFontFamily(int t);
......@@ -62,6 +65,13 @@ public:
static int GetFontFamilyFromString( const char *f );
static const char *GetFontFamilyAsString( int f );
// Description:
// The absolute filepath to a local file containing a freetype-readable font
// if GetFontFamily() return VTK_FONT_FILE. The result is undefined for other
// values of GetFontFamily().
vtkGetStringMacro(FontFile)
vtkSetStringMacro(FontFile)
// Description:
// Set/Get the font size (in points).
vtkSetClampMacro(FontSize,int,0,VTK_INT_MAX);
......@@ -148,6 +158,7 @@ protected:
double Color[3];
double Opacity;
char* FontFamilyAsString;
char* FontFile;
int FontSize;
int Bold;
int Italic;
......@@ -178,6 +189,10 @@ inline const char *vtkTextProperty::GetFontFamilyAsString( int f )
{
return "Times";
}
else if ( f == VTK_FONT_FILE )
{
return "File";
}
return "Unknown";
}
......@@ -215,6 +230,10 @@ inline int vtkTextProperty::GetFontFamilyFromString( const char *f )
{
return VTK_TIMES;
}
else if ( strcmp( f, GetFontFamilyAsString( VTK_FONT_FILE) ) == 0 )
{
return VTK_FONT_FILE;
}
return VTK_UNKNOWN_FONT;
}
......
......@@ -536,7 +536,11 @@ void vtkFreeTypeTools::MapTextPropertyToId(vtkTextProperty *tprop,
int bits = 1;
// The font family is hashed into 16 bits (= 17 bits so far)
*id |= vtkFreeTypeTools::HashString(tprop->GetFontFamilyAsString()) << bits;
vtkTypeUInt16 familyHash =
vtkFreeTypeTools::HashString(tprop->GetFontFamily() != VTK_FONT_FILE
? tprop->GetFontFamilyAsString()
: tprop->GetFontFile());
*id |= familyHash << bits;
bits += 16;
// Bold is in 1 bit (= 18 bits so far)
......@@ -862,6 +866,23 @@ bool vtkFreeTypeTools::LookupFace(vtkTextProperty *tprop, FT_Library lib,
" unavailable. Substituting Arial.");
family = VTK_ARIAL;
}
else if (family == VTK_FONT_FILE)
{
vtkDebugWithObjectMacro(tprop,
<< "Attempting to load font from file: "
<< tprop->GetFontFile());
if (FT_New_Face(lib, tprop->GetFontFile(), 0, face) == 0)
{
return true;
}
vtkDebugWithObjectMacro(
tprop,
<< "Error loading font from file '" << tprop->GetFontFile()
<< "'. Falling back to arial.");
family = VTK_ARIAL;
}
FT_Long length = EmbeddedFonts
[family][tprop->GetBold()][tprop->GetItalic()].length;
......@@ -1938,6 +1959,7 @@ void vtkFreeTypeTools::GetLineMetrics(T begin, T end, MetaData &metaData,
{
// FIXME: do something more elegant here.
// We should render an empty rectangle to adhere to the specs...
vtkDebugMacro(<<"Unrecognized character: " << *begin);
continue;
}
......
......@@ -46,8 +46,9 @@ vtkFontConfigFreeTypeToolsFaceRequester(FTC_FaceID face_id,
vtkSmartPointer<vtkTextProperty>::New();
self->MapIdToTextProperty(reinterpret_cast<intptr_t>(face_id), tprop);
bool faceIsSet = self->GetForceCompiledFonts() ?
false : self->LookupFaceFontConfig(tprop, lib, face);
bool faceIsSet =
self->GetForceCompiledFonts() || tprop->GetFontFamily() == VTK_FONT_FILE
? false : self->LookupFaceFontConfig(tprop, lib, face);
// Fall back to compiled fonts if lookup fails/compiled fonts are forced:
if (!faceIsSet)
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment