Commit 1d91f9ba authored by Cory Quammen's avatar Cory Quammen

BUG 15797: Fix text property ID computation

Only a few settings defined by the vtkTextProperty were used to
compute the text property ID in MapTextPropertyToId(). This can cause
incorrect rendering when settings that weren't considered, such as the
shadow setting, were changed.

Added a more general hashing function, HashBuffer(), to accommodate
all the vtkTextProperty settings, and use this to compute the
vtkTextProperty hash. HashString() is no longer used, but remains for
backwards compatibility.

Added a test to ensure that changing a setting results in the
computation of a different ID.
parent df44c5c9
......@@ -6,6 +6,7 @@ set(TestMathTextFreeTypeTextRendererNoMath_ARGS
vtk_add_test_cxx(${vtk-module}CxxTests tests
TestFTStringToPath.cxx
TestFreeTypeTextMapperNoMath.cxx
TestFreeTypeTools.cxx,NO_VALID
TestMathTextFreeTypeTextRendererNoMath.cxx
TestTextActor.cxx
TestTextActor3D.cxx
......
/*=========================================================================
Program: Visualization Toolkit
Module: TestFreeTypeTools.cxx
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 "vtkFreeTypeTools.h"
#include "vtkSmartPointer.h"
#include "vtkTextProperty.h"
#include <set>
namespace {
//----------------------------------------------------------------------------
int CheckIfIDExists(vtkTextProperty* property, std::set<size_t> & ids)
{
size_t id;
vtkFreeTypeTools::GetInstance()->MapTextPropertyToId(property, &id);
if (ids.size() > 0 && ids.find(id) != ids.end())
{
std::cout << "ID " << id
<< " already exists for other vtkTextProperty settings\n";
return EXIT_FAILURE;
}
ids.insert(id);
return EXIT_SUCCESS;
}
}
//----------------------------------------------------------------------------
int TestFreeTypeTools(int, char *[])
{
size_t result = EXIT_SUCCESS;
std::set<size_t> ids;
vtkSmartPointer<vtkTextProperty> property =
vtkSmartPointer<vtkTextProperty>::New();
// Initial settings
property->BoldOff();
property->ItalicOff();
property->ShadowOff();
property->SetFontSize(12);
property->SetColor(1.0, 1.0, 1.0);
property->SetOpacity(1.0);
property->SetBackgroundColor(0.0, 0.0, 0.0);
property->SetBackgroundOpacity(1.0);
property->SetFontFamilyToArial();
property->SetShadowOffset(2, 2);
property->SetOrientation(0.0);
property->SetLineSpacing(1.0);
property->SetLineOffset(1.0);
std::cout << "Bold\n";
property->BoldOn();
result += CheckIfIDExists(property, ids);
property->BoldOff();
std::cout << "Italic\n";
property->ItalicOn();
result += CheckIfIDExists(property, ids);
property->ItalicOff();
std::cout << "Shadow\n";
property->ShadowOn();
result += CheckIfIDExists(property, ids);
property->ShadowOff();
std::cout << "Font size\n";
property->SetFontSize(14);
result += CheckIfIDExists(property, ids);
property->SetFontSize(12);
std::cout << "Color\n";
property->SetColor(0.0, 1.0, 1.0);
result += CheckIfIDExists(property, ids);
property->SetColor(1.0, 1.0, 1.0);
std::cout << "Opacity\n";
property->SetOpacity(0.9);
result += CheckIfIDExists(property, ids);
property->SetOpacity(1.0);
std::cout << "BackgroundColor\n";
property->SetBackgroundColor(1.0, 0.0, 0.0);
result += CheckIfIDExists(property, ids);
property->SetBackgroundColor(0.0, 0.0, 0.0);
std::cout << "BackgroundOpacity\n";
property->SetBackgroundOpacity(0.8);
result += CheckIfIDExists(property, ids);
property->SetBackgroundOpacity(1.0);
std::cout << "FontFamily\n";
property->SetFontFamilyToCourier();
result += CheckIfIDExists(property, ids);
property->SetFontFamilyToArial();
std::cout << "ShadowOffset\n";
property->SetShadowOffset(-2, -3);
result += CheckIfIDExists(property, ids);
property->SetShadowOffset(2, 2);
std::cout << "Orientation\n";
property->SetOrientation(90.0);
result += CheckIfIDExists(property, ids);
property->SetOrientation(0.0);
std::cout << "LineSpacing\n";
property->SetLineSpacing(2.0);
result += CheckIfIDExists(property, ids);
property->SetLineSpacing(1.0);
std::cout << "LineOffset\n";
property->SetLineOffset(2.0);
result += CheckIfIDExists(property, ids);
property->SetLineOffset(1.0);
return result;
}
......@@ -581,6 +581,27 @@ vtkTypeUInt16 vtkFreeTypeTools::HashString(const char *str)
return hash;
}
//----------------------------------------------------------------------------
vtkTypeUInt32 vtkFreeTypeTools::HashBuffer(const void *buffer, size_t n, vtkTypeUInt32 hash)
{
if (buffer == NULL)
{
return 0;
}
const char* key = reinterpret_cast<const char*>(buffer);
// Jenkins hash function
for (size_t i = 0; i < n; ++i)
{
hash += key[i];
hash += (hash << 10);
hash += (hash << 15);
}
return hash;
}
//----------------------------------------------------------------------------
void vtkFreeTypeTools::MapTextPropertyToId(vtkTextProperty *tprop,
size_t *id)
......@@ -591,43 +612,52 @@ void vtkFreeTypeTools::MapTextPropertyToId(vtkTextProperty *tprop,
return;
}
// The font family is hashed into 16 bits (= 17 bits so far)
const char* fontFamily = tprop->GetFontFamily() != VTK_FONT_FILE
? tprop->GetFontFamilyAsString()
: tprop->GetFontFile();
size_t fontFamilyLength = 0;
if (fontFamily)
{
fontFamilyLength = strlen(fontFamily);
}
vtkTypeUInt32 hash =
vtkFreeTypeTools::HashBuffer(fontFamily, fontFamilyLength);
// Create a "string" of text properties
unsigned char ucValue = tprop->GetBold();
hash = vtkFreeTypeTools::HashBuffer(&ucValue, sizeof(unsigned char), hash);
ucValue = tprop->GetItalic();
hash = vtkFreeTypeTools::HashBuffer(&ucValue, sizeof(unsigned char), hash);
ucValue = tprop->GetShadow();
hash = vtkFreeTypeTools::HashBuffer(&ucValue, sizeof(unsigned char), hash);
hash = vtkFreeTypeTools::HashBuffer(
tprop->GetColor(), 3*sizeof(double), hash);
double dValue = tprop->GetOpacity();
hash = vtkFreeTypeTools::HashBuffer(&dValue, sizeof(double), hash);
hash = vtkFreeTypeTools::HashBuffer(
tprop->GetBackgroundColor(), 3*sizeof(double), hash);
dValue = tprop->GetBackgroundOpacity();
hash = vtkFreeTypeTools::HashBuffer(&dValue, sizeof(double), hash);
int iValue = tprop->GetFontSize();
hash = vtkFreeTypeTools::HashBuffer(&iValue, sizeof(int), hash);
hash = vtkFreeTypeTools::HashBuffer(
tprop->GetShadowOffset(), 2*sizeof(int), hash);
dValue = tprop->GetOrientation();
hash = vtkFreeTypeTools::HashBuffer(&dValue, sizeof(double), hash);
hash = vtkFreeTypeTools::HashBuffer(&dValue, sizeof(double), hash);
dValue = tprop->GetLineSpacing();
hash = vtkFreeTypeTools::HashBuffer(&dValue, sizeof(double), hash);
dValue = tprop->GetLineOffset();
hash = vtkFreeTypeTools::HashBuffer(&dValue, sizeof(double), hash);
// 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;
unsigned int bits = 1;
// The font family is hashed into 16 bits (= 17 bits so far)
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)
vtkIdType bold = (tprop->GetBold() ? 1 : 0) << bits;
++bits;
// Italic is in 1 bit (= 19 bits so far)
vtkIdType 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) (31 bits)
long angle = vtkMath::Round(tprop->GetOrientation() * 10.0) % 3600;
if (angle < 0)
{
angle += 3600;
}
// We really should not use more than 12 bits
assert(angle >= 0 && (angle & 0xfffff000) == 0);
angle <<= bits;
vtkIdType merged = (bold | italic | angle);
// Now final id
*id |= merged;
// Add in the hash.
// We're dropping a bit here, but that should be okay.
*id |= hash << 1;
// Insert the TextProperty into the lookup table
if (!this->TextPropertyLookup->contains(*id))
......
......@@ -154,6 +154,11 @@ public:
// function, and is only used to generate identifiers for cached fonts.
static vtkTypeUInt16 HashString(const char *str);
// Description:
// Hash a string of a given length. This function hashes n chars and does
// not depend on a terminating null character.
static vtkTypeUInt32 HashBuffer(const void* str, size_t n, vtkTypeUInt32 hash = 0);
// Description:
// Given a text property 'tprop', get its unique ID in our cache framework.
// In the same way, given a unique ID in our cache, retrieve the
......
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