| /* |
| ********************************************************************** |
| * 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 |
| |