blob: bac403a494f5ecaa9ad6360c9fa337ee3f9e18d5 [file] [log] [blame]
/*
**********************************************************************
* 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