Commit 6dd7b574 authored by David C. Lonie's avatar David C. Lonie
Browse files

Add multiline, justification, and shadows to vtkFreeTypeTools.

Also, explicitly return text dimensions from text rasterization
functions. Older hardware requires texture image dimensions to be
powers of 2. Since the text will only be rendered into a portion of
the image in these cases, the text dimensions are needed to calculate
the texture coordinates during rendering.

Some classes were hiding the text dimensions in the image metadata, e.g.
texture coordinates and text dimensions were stored in Spacing and Origin.
This undocumented behavior was confusing and unintuitive.

Now functions that rendering scaled images explicitly return the text
dimensions.

Change-Id: Ic74b0dd9573d336427038985c671a8b724c92429
parent 5c14d896
......@@ -760,25 +760,32 @@ void vtkOpenGLContextDevice2D::DrawString(float *point,
std::floor(point[1] * yScale) / yScale };
// Cache rendered text strings
vtkTextureImageCache<TextPropertyKey>::CacheData cache =
vtkTextureImageCache<TextPropertyKey>::CacheData &cache =
this->Storage->TextTextureCache.GetCacheData(
TextPropertyKey(this->TextProp, string));
vtkImageData* image = cache.ImageData;
if (image->GetNumberOfPoints() == 0 && image->GetNumberOfCells() == 0)
{
if (!this->TextRenderer->RenderString(this->TextProp, string, image))
int textDims[2];
if (!this->TextRenderer->RenderString(this->TextProp, string, image,
textDims))
{
return;
}
cache.TextWidth = textDims[0];
cache.TextHeight = textDims[1];
}
vtkTexture* texture = cache.Texture;
texture->Render(this->Renderer);
float width = static_cast<float>(image->GetOrigin()[0]) / xScale;
float height = static_cast<float>(image->GetOrigin()[1]) / yScale;
int imgDims[3];
image->GetDimensions(imgDims);
float xw = static_cast<float>(image->GetSpacing()[0]);
float xh = static_cast<float>(image->GetSpacing()[1]);
float width = cache.TextWidth / xScale;
float height = cache.TextHeight / yScale;
float xw = cache.TextWidth / static_cast<float>(imgDims[0]);
float xh = cache.TextHeight / static_cast<float>(imgDims[1]);
this->AlignText(this->TextProp->GetOrientation(), width, height, p);
......@@ -887,25 +894,38 @@ 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<TextPropertyKey>::CacheData &cache =
this->Storage->MathTextTextureCache.GetCacheData(
TextPropertyKey(this->TextProp, string));
vtkImageData* image = cache.ImageData;
if (image->GetNumberOfPoints() == 0 && image->GetNumberOfCells() == 0)
{
int textDims[2];
if (!mathText->RenderString(string.c_str(), image, this->TextProp,
this->RenderWindow->GetDPI()))
this->RenderWindow->GetDPI(), textDims))
{
return;
}
cache.TextWidth = textDims[0];
cache.TextHeight = textDims[1];
}
vtkTexture* texture = cache.Texture;
texture->Render(this->Renderer);
int *dims = image->GetDimensions();
float width = static_cast<float>(dims[0]);
float height = static_cast<float>(dims[1]);
GLfloat mv[16];
glGetFloatv(GL_MODELVIEW_MATRIX, mv);
float xScale = mv[0];
float yScale = mv[5];
int imgDims[3];
image->GetDimensions(imgDims);
float width = cache.TextWidth / xScale;
float height = cache.TextHeight / yScale;
float xw = cache.TextWidth / static_cast<float>(imgDims[0]);
float xh = cache.TextHeight / static_cast<float>(imgDims[1]);
this->AlignText(this->TextProp->GetOrientation(), width, height, p);
......@@ -915,9 +935,9 @@ void vtkOpenGLContextDevice2D::DrawMathTextString(float point[2],
p[0] , p[1] + height };
float texCoord[] = { 0.0f, 0.0f,
1.0f, 0.0f,
1.0f, 1.0f,
0.0f, 1.0f };
xw, 0.0f,
xw, xh,
0.0f, xh };
glColor4ub(255, 255, 255, 255);
glEnableClientState(GL_VERTEX_ARRAY);
......
......@@ -52,6 +52,10 @@ public:
{
vtkSmartPointer<vtkImageData> ImageData;
vtkSmartPointer<vtkTexture> Texture;
// Dimensions of the text. Used for generating texture coords when the image
// dimensions are scaled to a power of two.
int TextWidth;
int TextHeight;
};
// Description:
......@@ -150,6 +154,8 @@ typename vtkTextureImageCache<Key>::CacheData& vtkTextureImageCache<Key>
cacheData.ImageData = vtkSmartPointer<vtkImageData>::New();
cacheData.Texture = vtkSmartPointer<vtkTexture>::New();
cacheData.Texture->SetInputData(cacheData.ImageData);
cacheData.TextWidth = 0;
cacheData.TextHeight = 0;
return this->AddCacheData(key, cacheData);
}
......
......@@ -55,13 +55,18 @@ public:
// Description:
// Given a text property and a string, this function initializes the
// vtkImageData *data and renders it in a vtkImageData.
// vtkImageData *data and renders it in a vtkImageData. textDims, if provided,
// will be overwritten by the pixel width and height of the rendered string.
// This is useful when ScaleToPowerOfTwo is true, and the image dimensions may
// not match the dimensions of the rendered text.
virtual int RenderString(vtkTextProperty *property,
const vtkUnicodeString& string,
vtkImageData *data) = 0;
vtkImageData *data,
int textDims[2] = NULL) = 0;
virtual int RenderString(vtkTextProperty *property,
const vtkStdString& string,
vtkImageData *data) = 0;
vtkImageData *data,
int text_dims[2] = NULL) = 0;
// Description:
// Should we produce images at powers of 2, makes rendering on old OpenGL
......
......@@ -90,7 +90,7 @@ vtkVector2i vtkFreeTypeStringToImage::GetBounds(vtkTextProperty *property,
int vtkFreeTypeStringToImage::RenderString(vtkTextProperty *property,
const vtkUnicodeString& string,
vtkImageData *data)
vtkImageData *data, int textDims[2])
{
// Get the required size, and initialize a new QImage to draw on.
vtkVector2i box = this->GetBounds(property, string);
......@@ -99,14 +99,15 @@ int vtkFreeTypeStringToImage::RenderString(vtkTextProperty *property,
return 0;
}
return this->Implementation->FreeType->RenderString(property,
string,
data);
data, textDims);
}
int vtkFreeTypeStringToImage::RenderString(vtkTextProperty *property,
const vtkStdString& string,
vtkImageData *data)
vtkImageData *data, int textDims[2])
{
// Get the required size, and initialize a new QImage to draw on.
vtkVector2i box = this->GetBounds(property, string);
......@@ -115,7 +116,8 @@ int vtkFreeTypeStringToImage::RenderString(vtkTextProperty *property,
return 0;
}
return this->Implementation->FreeType->RenderString(property, string, data);
return this->Implementation->FreeType->RenderString(property, string, data,
textDims);
}
//-----------------------------------------------------------------------------
......
......@@ -52,13 +52,18 @@ public:
// Description:
// Given a text property and a string, this function initializes the
// vtkImageData *data and renders it in a vtkImageData.
// vtkImageData *data and renders it in a vtkImageData. textDims, if provided,
// will be overwritten by the pixel width and height of the rendered string.
// This is useful when ScaleToPowerOfTwo is true, and the image dimensions may
// not match the dimensions of the rendered text.
virtual int RenderString(vtkTextProperty *property,
const vtkUnicodeString& string,
vtkImageData *data);
vtkImageData *data,
int textDims[2] = NULL);
virtual int RenderString(vtkTextProperty *property,
const vtkStdString& string,
vtkImageData *data);
vtkImageData *data,
int textDims[2] = NULL);
// Description:
// Should we produce images at powers of 2, makes rendering on old OpenGL
......
This diff is collapsed.
......@@ -104,11 +104,14 @@ public:
// Description:
// Given a text property and a string, this function initializes the
// vtkImageData *data and renders it in a vtkImageData.
// vtkImageData *data and renders it in a vtkImageData. textDims, if provided,
// will be overwritten by the pixel width and height of the rendered string.
// This is useful when ScaleToPowerOfTwo is true, and the image dimensions may
// not match the dimensions of the rendered text.
bool RenderString(vtkTextProperty *tprop, const vtkStdString& str,
vtkImageData *data);
vtkImageData *data, int textDims[2] = NULL);
bool RenderString(vtkTextProperty *tprop, const vtkUnicodeString& str,
vtkImageData *data);
vtkImageData *data, int textDims[2] = NULL);
// Description:
// Given a text property and a string, this function populates the vtkPath
......@@ -175,37 +178,71 @@ protected:
virtual FT_Error CreateFTCManager();
// Description:
// This function initializes calculates the size of the required bounding box.
// Used to store state about a particular rendering and cache constant values
class MetaData;
class ImageMetaData;
bool PrepareMetaData(vtkTextProperty *tprop, MetaData &metaData);
bool PrepareImageMetaData(vtkTextProperty *tprop, vtkImageData *image,
ImageMetaData &metaData);
// Description:
// Internal helper called by RenderString methods
template <typename StringType>
bool RenderStringInternal(vtkTextProperty *tprop, const StringType &str,
vtkImageData *data, int textDims[2]);
// Description:
// Internal helper method called by StringToPath methods
template <typename StringType>
bool StringToPathInternal(vtkTextProperty *tprop, const StringType &str,
vtkPath *path);
// Description:
// This function initializes calculates the size of the required bounding box
// and stores it in the MetaData provided. Both the rotated and unrotated
// bounding boxes are set, along with the lineWidths.
template <typename T>
bool CalculateBoundingBox(vtkTextProperty *tprop, const T& str, int bbox[4]);
bool CalculateBoundingBox(const T& str, MetaData &metaData);
// Description:
// This function initializes the extent of the ImageData to eventually
// receive the text stored in str
template <typename T>
void PrepareImageData(vtkImageData *data,
vtkTextProperty *tprop,
const T& str,
int *x, int *y);
void PrepareImageData(vtkImageData *data, int bbox[4]);
// Description:
// Internal helper method called by RenderString
template <typename T>
bool PopulateImageData(vtkTextProperty *tprop, const T& str,
int x, int y, vtkImageData *data);
// Internal helper method called by RenderString.
// metaData is passed through the the character renderer and caches properties
// about data (e.g. range, dimensions, increments, etc).
template <typename StringType, typename DataType>
bool PopulateData(const StringType& str, DataType data, MetaData &metaData);
// Description:
// Renders a single line of text (between begin and end) to the image data.
template <typename IteratorType, typename DataType>
bool RenderLine(IteratorType begin, IteratorType end, int lineIndex,
DataType data, MetaData &metaData);
// Description:
// Implementations for rendering a single character to a specific target.
template <typename CharType>
bool RenderCharacter(CharType character, int &x, int &y,
FT_UInt &previousGlyphIndex, vtkImageData *image,
MetaData &metaData);
template <typename CharType>
bool RenderCharacter(CharType character, int &x, int &y,
FT_UInt &previousGlyphIndex, vtkPath *path,
MetaData &metaData);
// Description:
// Internal helper method called by StringToPath
template <typename T>
bool PopulatePath(vtkTextProperty *tprop, const T& str,
int x, int y, vtkPath *path);
void JustifyPath(vtkPath *path, MetaData &metaData);
// Description:
// Internal helper method called by GetConstrainedFontSize. Returns the
// fontsize (in points) that will fit the return string @a str into the @a
// targetWidth and @a targetHeight.
template <typename T>
int FitStringToBBox(const T &str, vtkTextProperty *tprop, int targetWidth,
int FitStringToBBox(const T &str, MetaData &metaData, int targetWidth,
int targetHeight);
// Description:
......@@ -284,6 +321,16 @@ protected:
int prop_font_size, FT_UInt &gindex,
FT_OutlineGlyph &outline_glyph);
// Description:
// Get the width of the rendered string between iterators
// begin and end. Width is calculated as the sum of advances and kernings
// along the baseline (i.e. rotations are ignored), while bbox is the
// is a tight fitting bbox around the rendering string, assuming (0, 0)
// is the pen origin.
template<typename T>
void GetLineMetrics(T begin, T end, MetaData &metaData, int &width,
int bbox[4]);
// Description:
// The singleton instance and the singleton cleanup instance
static vtkFreeTypeTools* Instance;
......
......@@ -70,9 +70,13 @@ public:
// Description:
// Render the given string @a str into the vtkImageData @a data with a
// resolution of @a dpi.
// resolution of @a dpi. textDims, will be overwritten by the pixel width and
// height of the rendered string. This is useful when ScaleToPowerOfTwo is
// set to true, and the image dimensions may not match the dimensions of the
// rendered text.
virtual bool RenderString(const char *str, vtkImageData *data,
vtkTextProperty *tprop, unsigned int dpi) = 0;
vtkTextProperty *tprop,
unsigned int dpi, int textDims[2] = NULL) = 0;
// Description:
// Parse the MathText expression in str and fill path with a contour of the
......@@ -90,6 +94,13 @@ public:
int targetWidth, int targetHeight,
unsigned int dpi);
// Description:
// Set to true if the graphics implmentation requires texture image dimensions
// to be a power of two. Default is true, but this member will be set
// appropriately when GL is inited.
virtual bool GetScaleToPowerOfTwo() = 0;
virtual void SetScaleToPowerOfTwo(bool scale) = 0;
protected:
vtkMathTextUtilities();
virtual ~vtkMathTextUtilities();
......
......@@ -103,7 +103,8 @@ vtkInstantiatorNewMacro(vtkMatplotlibMathTextUtilities)
//----------------------------------------------------------------------------
vtkMatplotlibMathTextUtilities::vtkMatplotlibMathTextUtilities()
: Superclass(), MaskParser(NULL), PathParser(NULL), FontPropertiesClass(NULL)
: Superclass(), MaskParser(NULL), PathParser(NULL), FontPropertiesClass(NULL),
ScaleToPowerOfTwo(true)
{
}
......@@ -320,6 +321,55 @@ void vtkMatplotlibMathTextUtilities::RotateCorners(double angleDeg,
}
}
//----------------------------------------------------------------------------
// This is more or less ported from vtkFreeTypeTools.
bool vtkMatplotlibMathTextUtilities::PrepareImageData(vtkImageData *data,
int width, int height)
{
// If the current image data is too small to render the text,
// or more than twice as big (too hungry), then resize
int imgDims[3], newImgDims[3];
data->GetDimensions(imgDims);
if (data->GetScalarType() != VTK_UNSIGNED_CHAR ||
data->GetNumberOfScalarComponents() != 4 ||
imgDims[0] < width || imgDims[1] < height ||
width * 2 < imgDims[0] || height * 2 < imgDims[1])
{
// Scale to the next highest power of 2 if required.
if (this->ScaleToPowerOfTwo)
{
newImgDims[0] =
1 << static_cast<int>(ceil(log(static_cast<double>(width+1)) /
log(2.0)));
newImgDims[1] =
1 << static_cast<int>(ceil(log(static_cast<double>(height+1)) /
log(2.0)));
}
else
{
newImgDims[0] = width;
newImgDims[1] = height;
}
newImgDims[2] = 1;
// Allocate the new image if needed
if (data->GetScalarType() != VTK_UNSIGNED_CHAR ||
data->GetNumberOfScalarComponents() != 4 ||
newImgDims[0] != imgDims[0] ||
newImgDims[1] != imgDims[1] ||
newImgDims[2] != imgDims[2])
{
data->SetDimensions(newImgDims);
data->AllocateScalars(VTK_UNSIGNED_CHAR, 4);
}
}
// Clear the image
memset(data->GetScalarPointer(), 0,
(data->GetNumberOfPoints() * data->GetNumberOfScalarComponents()));
}
//----------------------------------------------------------------------------
bool vtkMatplotlibMathTextUtilities::GetBoundingBox(
vtkTextProperty *tprop, const char *str, unsigned int dpi, int bbox[4])
......@@ -395,7 +445,8 @@ bool vtkMatplotlibMathTextUtilities::GetBoundingBox(
bool vtkMatplotlibMathTextUtilities::RenderString(const char *str,
vtkImageData *image,
vtkTextProperty *tprop,
unsigned int dpi)
unsigned int dpi,
int textDims[2])
{
if (!this->MaskParser)
{
......@@ -474,8 +525,14 @@ bool vtkMatplotlibMathTextUtilities::RenderString(const char *str,
return false;
}
image->SetDimensions(cols, rows, 1);
image->AllocateScalars(VTK_UNSIGNED_CHAR, 4);
if (textDims)
{
textDims[0] = cols;
textDims[1] = rows;
}
this->PrepareImageData(image, cols, rows);
for (long int row = rows-1; row >= 0; --row)
{
for (long int col = 0; col < cols; ++col)
......@@ -520,6 +577,23 @@ bool vtkMatplotlibMathTextUtilities::RenderString(const char *str,
// Rotate the corners of the image and determine the bounding box
this->RotateCorners(angleDeg, corners, bbox);
// Also rotate the text dimensions.
if (textDims)
{
double text_bbox[4];
corners[0][0] = static_cast<double>(0);
corners[0][1] = static_cast<double>(0);
corners[1][0] = static_cast<double>(0);
corners[1][1] = static_cast<double>(textDims[1]);
corners[2][0] = static_cast<double>(textDims[0]);
corners[2][1] = static_cast<double>(textDims[1]);
corners[3][0] = static_cast<double>(textDims[0]) ;
corners[3][1] = static_cast<double>(0);
this->RotateCorners(angleDeg, corners, text_bbox);
textDims[0] = std::ceil(text_bbox[1] - text_bbox[0]);
textDims[1] = std::ceil(text_bbox[3] - text_bbox[2]);
}
// Rotate the temporary image into the returned image:
vtkNew<vtkTransform> rotation;
rotation->RotateWXYZ(-angleDeg, 0, 0, 1);
......
......@@ -46,15 +46,25 @@ public:
// Description:
// Render the given string @a str into the vtkImageData @a data with a
// resolution of @a dpi. The image is resized automatically.
// resolution of @a dpi. The image is resized automatically. textDims
// will be overwritten by the pixel width and height of the rendered string.
// This is useful when ScaleToPowerOfTwo is true, and the image dimensions may
// not match the dimensions of the rendered text.
bool RenderString(const char *str, vtkImageData *data, vtkTextProperty *tprop,
unsigned int dpi);
unsigned int dpi, int textDims[2] = NULL);
// Description:
// Parse the MathText expression in str and fill path with a contour of the
// glyphs.
bool StringToPath(const char *str, vtkPath *path, vtkTextProperty *tprop);
// Description:
// Set to true if the graphics implmentation requires texture image dimensions
// to be a power of two. Default is true, but this member will be set
// appropriately when GL is inited.
vtkSetMacro(ScaleToPowerOfTwo, bool);
vtkGetMacro(ScaleToPowerOfTwo, bool);
protected:
vtkMatplotlibMathTextUtilities();
virtual ~vtkMatplotlibMathTextUtilities();
......@@ -89,6 +99,9 @@ protected:
};
static Availablity MPLMathTextAvailable;
bool ScaleToPowerOfTwo;
bool PrepareImageData(vtkImageData *data, int width, int height);
private:
vtkMatplotlibMathTextUtilities(const vtkMatplotlibMathTextUtilities&); // Not implemented.
void operator=(const vtkMatplotlibMathTextUtilities&); // Not implemented.
......
......@@ -148,7 +148,7 @@ vtkVector2i vtkQtStringToImage::GetBounds(vtkTextProperty *property,
int vtkQtStringToImage::RenderString(vtkTextProperty *property,
const vtkUnicodeString& string,
vtkImageData *data)
vtkImageData *data, int textDims[2])
{
if (!QApplication::instance())
{
......@@ -161,6 +161,11 @@ int vtkQtStringToImage::RenderString(vtkTextProperty *property,
{
return 0;
}
if (textDims)
{
textDims[0] = box.GetX();
textDims[1] = box.GetY();
}
QString text = QString::fromUtf8(string.utf8_str());
QFont fontSpec = this->Implementation->TextPropertyToFont(property);
......@@ -222,9 +227,10 @@ int vtkQtStringToImage::RenderString(vtkTextProperty *property,
int vtkQtStringToImage::RenderString(vtkTextProperty *property,
const vtkStdString& string,
vtkImageData *data)
vtkImageData *data, int textDims[2])
{
return this->RenderString(property, vtkUnicodeString::from_utf8(string), data);
return this->RenderString(property, vtkUnicodeString::from_utf8(string), data,
textDims);
}
//-----------------------------------------------------------------------------
......
......@@ -54,13 +54,16 @@ public:
// Description:
// Given a text property and a string, this function initializes the
// vtkImageData *data and renders it in a vtkImageData.
// vtkImageData *data and renders it in a vtkImageData. textDims, if provided,
// will be overwritten by the pixel width and height of the rendered string.
virtual int RenderString(vtkTextProperty *property,
const vtkUnicodeString& string,
vtkImageData *data);
vtkImageData *data,
int textDims[2] = NULL);
virtual int RenderString(vtkTextProperty *property,
const vtkStdString& string,
vtkImageData *data);
vtkImageData *data,
int textDims[2] = NULL);
// Description:
// Make a deep copy of the supplied utility class.
......
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