84b9265eed
X-SVN-Rev: 26091
667 lines
16 KiB
C++
667 lines
16 KiB
C++
/*
|
|
**********************************************************************
|
|
* Copyright (C) 1998-2009, International Business Machines
|
|
* Corporation and others. All Rights Reserved.
|
|
**********************************************************************
|
|
*/
|
|
|
|
#include "LETypes.h"
|
|
#include "LEInsertionList.h"
|
|
#include "LEGlyphStorage.h"
|
|
|
|
U_NAMESPACE_BEGIN
|
|
|
|
UOBJECT_DEFINE_RTTI_IMPLEMENTATION(LEGlyphStorage)
|
|
|
|
LEInsertionCallback::~LEInsertionCallback()
|
|
{
|
|
// nothing to do...
|
|
}
|
|
|
|
LEGlyphStorage::LEGlyphStorage()
|
|
: fGlyphCount(0), fGlyphs(NULL), fCharIndices(NULL), fPositions(NULL),
|
|
fAuxData(NULL), fInsertionList(NULL), fSrcIndex(0), fDestIndex(0)
|
|
{
|
|
// nothing else to do!
|
|
}
|
|
|
|
LEGlyphStorage::~LEGlyphStorage()
|
|
{
|
|
reset();
|
|
}
|
|
|
|
void LEGlyphStorage::reset()
|
|
{
|
|
fGlyphCount = 0;
|
|
|
|
if (fPositions != NULL) {
|
|
LE_DELETE_ARRAY(fPositions);
|
|
fPositions = NULL;
|
|
}
|
|
|
|
if (fAuxData != NULL) {
|
|
LE_DELETE_ARRAY(fAuxData);
|
|
fAuxData = NULL;
|
|
}
|
|
|
|
if (fInsertionList != NULL) {
|
|
delete fInsertionList;
|
|
fInsertionList = NULL;
|
|
}
|
|
|
|
if (fCharIndices != NULL) {
|
|
LE_DELETE_ARRAY(fCharIndices);
|
|
fCharIndices = NULL;
|
|
}
|
|
|
|
if (fGlyphs != NULL) {
|
|
LE_DELETE_ARRAY(fGlyphs);
|
|
fGlyphs = NULL;
|
|
}
|
|
}
|
|
|
|
// FIXME: This might get called more than once, for various reasons. Is
|
|
// testing for pre-existing glyph and charIndices arrays good enough?
|
|
void LEGlyphStorage::allocateGlyphArray(le_int32 initialGlyphCount, le_bool rightToLeft, LEErrorCode &success)
|
|
{
|
|
if (LE_FAILURE(success)) {
|
|
return;
|
|
}
|
|
|
|
if (initialGlyphCount <= 0) {
|
|
success = LE_ILLEGAL_ARGUMENT_ERROR;
|
|
return;
|
|
}
|
|
|
|
if (fGlyphs == NULL) {
|
|
fGlyphCount = initialGlyphCount;
|
|
fGlyphs = LE_NEW_ARRAY(LEGlyphID, fGlyphCount);
|
|
|
|
if (fGlyphs == NULL) {
|
|
success = LE_MEMORY_ALLOCATION_ERROR;
|
|
return;
|
|
}
|
|
}
|
|
|
|
if (fCharIndices == NULL) {
|
|
fCharIndices = LE_NEW_ARRAY(le_int32, fGlyphCount);
|
|
|
|
if (fCharIndices == NULL) {
|
|
LE_DELETE_ARRAY(fGlyphs);
|
|
fGlyphs = NULL;
|
|
success = LE_MEMORY_ALLOCATION_ERROR;
|
|
return;
|
|
}
|
|
|
|
// Initialize the charIndices array
|
|
le_int32 i, count = fGlyphCount, dir = 1, out = 0;
|
|
|
|
if (rightToLeft) {
|
|
out = fGlyphCount - 1;
|
|
dir = -1;
|
|
}
|
|
|
|
for (i = 0; i < count; i += 1, out += dir) {
|
|
fCharIndices[out] = i;
|
|
}
|
|
}
|
|
|
|
if (fInsertionList == NULL) {
|
|
// FIXME: check this for failure?
|
|
fInsertionList = new LEInsertionList(rightToLeft);
|
|
if (fInsertionList == NULL) {
|
|
LE_DELETE_ARRAY(fCharIndices);
|
|
fCharIndices = NULL;
|
|
|
|
LE_DELETE_ARRAY(fGlyphs);
|
|
fGlyphs = NULL;
|
|
|
|
success = LE_MEMORY_ALLOCATION_ERROR;
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
|
|
// FIXME: do we want to initialize the positions to [0, 0]?
|
|
le_int32 LEGlyphStorage::allocatePositions(LEErrorCode &success)
|
|
{
|
|
if (LE_FAILURE(success)) {
|
|
return -1;
|
|
}
|
|
|
|
if (fPositions != NULL) {
|
|
success = LE_INTERNAL_ERROR;
|
|
return -1;
|
|
}
|
|
|
|
fPositions = LE_NEW_ARRAY(float, 2 * (fGlyphCount + 1));
|
|
|
|
if (fPositions == NULL) {
|
|
success = LE_MEMORY_ALLOCATION_ERROR;
|
|
return -1;
|
|
}
|
|
|
|
return fGlyphCount;
|
|
}
|
|
|
|
// FIXME: do we want to initialize the aux data to NULL?
|
|
le_int32 LEGlyphStorage::allocateAuxData(LEErrorCode &success)
|
|
{
|
|
if (LE_FAILURE(success)) {
|
|
return -1;
|
|
}
|
|
|
|
if (fAuxData != NULL) {
|
|
success = LE_INTERNAL_ERROR;
|
|
return -1;
|
|
}
|
|
|
|
fAuxData = LE_NEW_ARRAY(le_uint32, fGlyphCount);
|
|
|
|
if (fAuxData == NULL) {
|
|
success = LE_MEMORY_ALLOCATION_ERROR;
|
|
return -1;
|
|
}
|
|
|
|
return fGlyphCount;
|
|
}
|
|
|
|
void LEGlyphStorage::getCharIndices(le_int32 charIndices[], le_int32 indexBase, LEErrorCode &success) const
|
|
{
|
|
le_int32 i;
|
|
|
|
if (LE_FAILURE(success)) {
|
|
return;
|
|
}
|
|
|
|
if (charIndices == NULL) {
|
|
success = LE_ILLEGAL_ARGUMENT_ERROR;
|
|
return;
|
|
}
|
|
|
|
if (fCharIndices == NULL) {
|
|
success = LE_NO_LAYOUT_ERROR;
|
|
return;
|
|
}
|
|
|
|
for (i = 0; i < fGlyphCount; i += 1) {
|
|
charIndices[i] = fCharIndices[i] + indexBase;
|
|
}
|
|
}
|
|
|
|
void LEGlyphStorage::getCharIndices(le_int32 charIndices[], LEErrorCode &success) const
|
|
{
|
|
if (LE_FAILURE(success)) {
|
|
return;
|
|
}
|
|
|
|
if (charIndices == NULL) {
|
|
success = LE_ILLEGAL_ARGUMENT_ERROR;
|
|
return;
|
|
}
|
|
|
|
if (fCharIndices == NULL) {
|
|
success = LE_NO_LAYOUT_ERROR;
|
|
return;
|
|
}
|
|
|
|
LE_ARRAY_COPY(charIndices, fCharIndices, fGlyphCount);
|
|
}
|
|
|
|
// Copy the glyphs into caller's (32-bit) glyph array, OR in extraBits
|
|
void LEGlyphStorage::getGlyphs(le_uint32 glyphs[], le_uint32 extraBits, LEErrorCode &success) const
|
|
{
|
|
le_int32 i;
|
|
|
|
if (LE_FAILURE(success)) {
|
|
return;
|
|
}
|
|
|
|
if (glyphs == NULL) {
|
|
success = LE_ILLEGAL_ARGUMENT_ERROR;
|
|
return;
|
|
}
|
|
|
|
if (fGlyphs == NULL) {
|
|
success = LE_NO_LAYOUT_ERROR;
|
|
return;
|
|
}
|
|
|
|
for (i = 0; i < fGlyphCount; i += 1) {
|
|
glyphs[i] = fGlyphs[i] | extraBits;
|
|
}
|
|
}
|
|
|
|
void LEGlyphStorage::getGlyphs(LEGlyphID glyphs[], LEErrorCode &success) const
|
|
{
|
|
if (LE_FAILURE(success)) {
|
|
return;
|
|
}
|
|
|
|
if (glyphs == NULL) {
|
|
success = LE_ILLEGAL_ARGUMENT_ERROR;
|
|
return;
|
|
}
|
|
|
|
if (fGlyphs == NULL) {
|
|
success = LE_NO_LAYOUT_ERROR;
|
|
return;
|
|
}
|
|
|
|
LE_ARRAY_COPY(glyphs, fGlyphs, fGlyphCount);
|
|
}
|
|
|
|
LEGlyphID LEGlyphStorage::getGlyphID(le_int32 glyphIndex, LEErrorCode &success) const
|
|
{
|
|
if (LE_FAILURE(success)) {
|
|
return 0xFFFF;
|
|
}
|
|
|
|
if (fGlyphs == NULL) {
|
|
success = LE_NO_LAYOUT_ERROR;
|
|
return 0xFFFF;
|
|
}
|
|
|
|
if (glyphIndex < 0 || glyphIndex >= fGlyphCount) {
|
|
success = LE_INDEX_OUT_OF_BOUNDS_ERROR;
|
|
return 0xFFFF;
|
|
}
|
|
|
|
return fGlyphs[glyphIndex];
|
|
}
|
|
|
|
void LEGlyphStorage::setGlyphID(le_int32 glyphIndex, LEGlyphID glyphID, LEErrorCode &success)
|
|
{
|
|
if (LE_FAILURE(success)) {
|
|
return;
|
|
}
|
|
|
|
if (fGlyphs == NULL) {
|
|
success = LE_NO_LAYOUT_ERROR;
|
|
return;
|
|
}
|
|
|
|
if (glyphIndex < 0 || glyphIndex >= fGlyphCount) {
|
|
success = LE_INDEX_OUT_OF_BOUNDS_ERROR;
|
|
return;
|
|
}
|
|
|
|
fGlyphs[glyphIndex] = glyphID;
|
|
}
|
|
|
|
le_int32 LEGlyphStorage::getCharIndex(le_int32 glyphIndex, LEErrorCode &success) const
|
|
{
|
|
if (LE_FAILURE(success)) {
|
|
return -1;
|
|
}
|
|
|
|
if (fCharIndices == NULL) {
|
|
success = LE_NO_LAYOUT_ERROR;
|
|
return -1;
|
|
}
|
|
|
|
if (glyphIndex < 0 || glyphIndex >= fGlyphCount) {
|
|
success = LE_INDEX_OUT_OF_BOUNDS_ERROR;
|
|
return -1;
|
|
}
|
|
|
|
return fCharIndices[glyphIndex];
|
|
}
|
|
|
|
void LEGlyphStorage::setCharIndex(le_int32 glyphIndex, le_int32 charIndex, LEErrorCode &success)
|
|
{
|
|
if (LE_FAILURE(success)) {
|
|
return;
|
|
}
|
|
|
|
if (fCharIndices == NULL) {
|
|
success = LE_NO_LAYOUT_ERROR;
|
|
return;
|
|
}
|
|
|
|
if (glyphIndex < 0 || glyphIndex >= fGlyphCount) {
|
|
success = LE_INDEX_OUT_OF_BOUNDS_ERROR;
|
|
return;
|
|
}
|
|
|
|
fCharIndices[glyphIndex] = charIndex;
|
|
}
|
|
|
|
void LEGlyphStorage::getAuxData(le_uint32 auxData[], LEErrorCode &success) const
|
|
{
|
|
if (LE_FAILURE(success)) {
|
|
return;
|
|
}
|
|
|
|
if (auxData == NULL) {
|
|
success = LE_ILLEGAL_ARGUMENT_ERROR;
|
|
return;
|
|
}
|
|
|
|
if (fAuxData == NULL) {
|
|
success = LE_NO_LAYOUT_ERROR;
|
|
return;
|
|
}
|
|
|
|
LE_ARRAY_COPY(auxData, fAuxData, fGlyphCount);
|
|
}
|
|
|
|
le_uint32 LEGlyphStorage::getAuxData(le_int32 glyphIndex, LEErrorCode &success) const
|
|
{
|
|
if (LE_FAILURE(success)) {
|
|
return 0;
|
|
}
|
|
|
|
if (fAuxData == NULL) {
|
|
success = LE_NO_LAYOUT_ERROR;
|
|
return 0;
|
|
}
|
|
|
|
if (glyphIndex < 0 || glyphIndex >= fGlyphCount) {
|
|
success = LE_INDEX_OUT_OF_BOUNDS_ERROR;
|
|
return 0;
|
|
}
|
|
|
|
return fAuxData[glyphIndex];
|
|
}
|
|
|
|
void LEGlyphStorage::setAuxData(le_int32 glyphIndex, le_uint32 auxData, LEErrorCode &success)
|
|
{
|
|
if (LE_FAILURE(success)) {
|
|
return;
|
|
}
|
|
|
|
if (fAuxData == NULL) {
|
|
success = LE_NO_LAYOUT_ERROR;
|
|
return;
|
|
}
|
|
|
|
if (glyphIndex < 0 || glyphIndex >= fGlyphCount) {
|
|
success = LE_INDEX_OUT_OF_BOUNDS_ERROR;
|
|
return;
|
|
}
|
|
|
|
fAuxData[glyphIndex] = auxData;
|
|
}
|
|
|
|
void LEGlyphStorage::getGlyphPositions(float positions[], LEErrorCode &success) const
|
|
{
|
|
if (LE_FAILURE(success)) {
|
|
return;
|
|
}
|
|
|
|
if (positions == NULL) {
|
|
success = LE_ILLEGAL_ARGUMENT_ERROR;
|
|
return;
|
|
}
|
|
|
|
if (fPositions == NULL) {
|
|
success = LE_NO_LAYOUT_ERROR;
|
|
return;
|
|
}
|
|
|
|
LE_ARRAY_COPY(positions, fPositions, fGlyphCount * 2 + 2);
|
|
}
|
|
|
|
void LEGlyphStorage::getGlyphPosition(le_int32 glyphIndex, float &x, float &y, LEErrorCode &success) const
|
|
{
|
|
if (LE_FAILURE(success)) {
|
|
return;
|
|
}
|
|
|
|
if (glyphIndex < 0 || glyphIndex > fGlyphCount) {
|
|
success = LE_INDEX_OUT_OF_BOUNDS_ERROR;
|
|
return;
|
|
}
|
|
|
|
if (fPositions == NULL) {
|
|
success = LE_NO_LAYOUT_ERROR;
|
|
return;
|
|
}
|
|
|
|
x = fPositions[glyphIndex * 2];
|
|
y = fPositions[glyphIndex * 2 + 1];
|
|
}
|
|
|
|
void LEGlyphStorage::setPosition(le_int32 glyphIndex, float x, float y, LEErrorCode &success)
|
|
{
|
|
if (LE_FAILURE(success)) {
|
|
return;
|
|
}
|
|
|
|
if (glyphIndex < 0 || glyphIndex > fGlyphCount) {
|
|
success = LE_INDEX_OUT_OF_BOUNDS_ERROR;
|
|
return;
|
|
}
|
|
|
|
fPositions[glyphIndex * 2] = x;
|
|
fPositions[glyphIndex * 2 + 1] = y;
|
|
}
|
|
|
|
void LEGlyphStorage::adjustPosition(le_int32 glyphIndex, float xAdjust, float yAdjust, LEErrorCode &success)
|
|
{
|
|
if (LE_FAILURE(success)) {
|
|
return;
|
|
}
|
|
|
|
if (glyphIndex < 0 || glyphIndex > fGlyphCount) {
|
|
success = LE_INDEX_OUT_OF_BOUNDS_ERROR;
|
|
return;
|
|
}
|
|
|
|
fPositions[glyphIndex * 2] += xAdjust;
|
|
fPositions[glyphIndex * 2 + 1] += yAdjust;
|
|
}
|
|
|
|
void LEGlyphStorage::adoptGlyphArray(LEGlyphStorage &from)
|
|
{
|
|
if (fGlyphs != NULL) {
|
|
LE_DELETE_ARRAY(fGlyphs);
|
|
}
|
|
|
|
fGlyphs = from.fGlyphs;
|
|
from.fGlyphs = NULL;
|
|
|
|
if (fInsertionList != NULL) {
|
|
delete fInsertionList;
|
|
}
|
|
|
|
fInsertionList = from.fInsertionList;
|
|
from.fInsertionList = NULL;
|
|
}
|
|
|
|
void LEGlyphStorage::adoptCharIndicesArray(LEGlyphStorage &from)
|
|
{
|
|
if (fCharIndices != NULL) {
|
|
LE_DELETE_ARRAY(fCharIndices);
|
|
}
|
|
|
|
fCharIndices = from.fCharIndices;
|
|
from.fCharIndices = NULL;
|
|
}
|
|
|
|
void LEGlyphStorage::adoptPositionArray(LEGlyphStorage &from)
|
|
{
|
|
if (fPositions != NULL) {
|
|
LE_DELETE_ARRAY(fPositions);
|
|
}
|
|
|
|
fPositions = from.fPositions;
|
|
from.fPositions = NULL;
|
|
}
|
|
|
|
void LEGlyphStorage::adoptAuxDataArray(LEGlyphStorage &from)
|
|
{
|
|
if (fAuxData != NULL) {
|
|
LE_DELETE_ARRAY(fAuxData);
|
|
}
|
|
|
|
fAuxData = from.fAuxData;
|
|
from.fAuxData = NULL;
|
|
}
|
|
|
|
void LEGlyphStorage::adoptGlyphCount(LEGlyphStorage &from)
|
|
{
|
|
fGlyphCount = from.fGlyphCount;
|
|
}
|
|
|
|
void LEGlyphStorage::adoptGlyphCount(le_int32 newGlyphCount)
|
|
{
|
|
fGlyphCount = newGlyphCount;
|
|
}
|
|
|
|
// Move a glyph to a different position in the LEGlyphStorage ( used for Indic v2 processing )
|
|
|
|
void LEGlyphStorage::moveGlyph(le_int32 fromPosition, le_int32 toPosition, le_uint32 marker )
|
|
{
|
|
|
|
LEErrorCode success = LE_NO_ERROR;
|
|
|
|
LEGlyphID holdGlyph = getGlyphID(fromPosition,success);
|
|
le_int32 holdCharIndex = getCharIndex(fromPosition,success);
|
|
le_uint32 holdAuxData = getAuxData(fromPosition,success);
|
|
|
|
if ( fromPosition < toPosition ) {
|
|
for ( le_int32 i = fromPosition ; i < toPosition ; i++ ) {
|
|
setGlyphID(i,getGlyphID(i+1,success),success);
|
|
setCharIndex(i,getCharIndex(i+1,success),success);
|
|
setAuxData(i,getAuxData(i+1,success),success);
|
|
}
|
|
} else {
|
|
for ( le_int32 i = toPosition ; i > fromPosition ; i-- ) {
|
|
setGlyphID(i,getGlyphID(i-1,success),success);
|
|
setCharIndex(i,getCharIndex(i-1,success),success);
|
|
setAuxData(i,getAuxData(i-1,success),success);
|
|
|
|
}
|
|
}
|
|
|
|
setGlyphID(toPosition,holdGlyph,success);
|
|
setCharIndex(toPosition,holdCharIndex,success);
|
|
setAuxData(toPosition,holdAuxData | marker,success);
|
|
|
|
}
|
|
|
|
// Glue code for existing stable API
|
|
LEGlyphID *LEGlyphStorage::insertGlyphs(le_int32 atIndex, le_int32 insertCount)
|
|
{
|
|
LEErrorCode ignored = LE_NO_LAYOUT_ERROR;
|
|
return insertGlyphs(atIndex, insertCount, ignored);
|
|
}
|
|
|
|
// FIXME: add error checking?
|
|
LEGlyphID *LEGlyphStorage::insertGlyphs(le_int32 atIndex, le_int32 insertCount, LEErrorCode& success)
|
|
{
|
|
return fInsertionList->insert(atIndex, insertCount, success);
|
|
}
|
|
|
|
le_int32 LEGlyphStorage::applyInsertions()
|
|
{
|
|
le_int32 growAmount = fInsertionList->getGrowAmount();
|
|
|
|
if (growAmount == 0) {
|
|
return fGlyphCount;
|
|
}
|
|
|
|
le_int32 newGlyphCount = fGlyphCount + growAmount;
|
|
|
|
LEGlyphID *newGlyphs = (LEGlyphID *) LE_GROW_ARRAY(fGlyphs, newGlyphCount);
|
|
if (newGlyphs == NULL) {
|
|
// Could not grow the glyph array
|
|
return fGlyphCount;
|
|
}
|
|
fGlyphs = newGlyphs;
|
|
|
|
le_int32 *newCharIndices = (le_int32 *) LE_GROW_ARRAY(fCharIndices, newGlyphCount);
|
|
if (newCharIndices == NULL) {
|
|
// Could not grow the glyph array
|
|
return fGlyphCount;
|
|
}
|
|
fCharIndices = newCharIndices;
|
|
|
|
if (fAuxData != NULL) {
|
|
le_uint32 *newAuxData = (le_uint32 *) LE_GROW_ARRAY(fAuxData, newGlyphCount);
|
|
if (newAuxData == NULL) {
|
|
// could not grow the aux data array
|
|
return fGlyphCount;
|
|
}
|
|
fAuxData = (le_uint32 *)newAuxData;
|
|
}
|
|
|
|
fSrcIndex = fGlyphCount - 1;
|
|
fDestIndex = newGlyphCount - 1;
|
|
|
|
#if 0
|
|
// If the current position is at the end of the array
|
|
// update it to point to the end of the new array. The
|
|
// insertion callback will handle all other cases.
|
|
// FIXME: this is left over from GlyphIterator, but there's no easy
|
|
// way to implement this here... it seems that GlyphIterator doesn't
|
|
// really need it 'cause the insertions don't get applied until after a
|
|
// complete pass over the glyphs, after which the iterator gets reset anyhow...
|
|
// probably better to just document that for LEGlyphStorage and GlyphIterator...
|
|
if (position == glyphCount) {
|
|
position = newGlyphCount;
|
|
}
|
|
#endif
|
|
|
|
fInsertionList->applyInsertions(this);
|
|
|
|
fInsertionList->reset();
|
|
|
|
return fGlyphCount = newGlyphCount;
|
|
}
|
|
|
|
le_bool LEGlyphStorage::applyInsertion(le_int32 atPosition, le_int32 count, LEGlyphID newGlyphs[])
|
|
{
|
|
#if 0
|
|
// if the current position is within the block we're shifting
|
|
// it needs to be updated to the current glyph's
|
|
// new location.
|
|
// FIXME: this is left over from GlyphIterator, but there's no easy
|
|
// way to implement this here... it seems that GlyphIterator doesn't
|
|
// really need it 'cause the insertions don't get applied until after a
|
|
// complete pass over the glyphs, after which the iterator gets reset anyhow...
|
|
// probably better to just document that for LEGlyphStorage and GlyphIterator...
|
|
if (position >= atPosition && position <= fSrcIndex) {
|
|
position += fDestIndex - fSrcIndex;
|
|
}
|
|
#endif
|
|
|
|
if (fAuxData != NULL) {
|
|
le_int32 src = fSrcIndex, dest = fDestIndex;
|
|
|
|
while (src > atPosition) {
|
|
fAuxData[dest--] = fAuxData[src--];
|
|
}
|
|
|
|
for (le_int32 i = count - 1; i >= 0; i -= 1) {
|
|
fAuxData[dest--] = fAuxData[atPosition];
|
|
}
|
|
}
|
|
|
|
while (fSrcIndex > atPosition) {
|
|
fGlyphs[fDestIndex] = fGlyphs[fSrcIndex];
|
|
fCharIndices[fDestIndex] = fCharIndices[fSrcIndex];
|
|
|
|
fDestIndex -= 1;
|
|
fSrcIndex -= 1;
|
|
}
|
|
|
|
for (le_int32 i = count - 1; i >= 0; i -= 1) {
|
|
fGlyphs[fDestIndex] = newGlyphs[i];
|
|
fCharIndices[fDestIndex] = fCharIndices[atPosition];
|
|
|
|
fDestIndex -= 1;
|
|
}
|
|
|
|
// the source glyph we're pointing at
|
|
// just got replaced by the insertion
|
|
fSrcIndex -= 1;
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
U_NAMESPACE_END
|
|
|