diff options
Diffstat (limited to 'gfx/graphite2/src/Segment.cpp')
-rw-r--r-- | gfx/graphite2/src/Segment.cpp | 545 |
1 files changed, 545 insertions, 0 deletions
diff --git a/gfx/graphite2/src/Segment.cpp b/gfx/graphite2/src/Segment.cpp new file mode 100644 index 000000000..3020bfd36 --- /dev/null +++ b/gfx/graphite2/src/Segment.cpp @@ -0,0 +1,545 @@ +/* GRAPHITE2 LICENSING + + Copyright 2010, SIL International + All rights reserved. + + This library is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published + by the Free Software Foundation; either version 2.1 of License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should also have received a copy of the GNU Lesser General Public + License along with this library in the file named "LICENSE". + If not, write to the Free Software Foundation, 51 Franklin Street, + Suite 500, Boston, MA 02110-1335, USA or visit their web page on the + internet at http://www.fsf.org/licenses/lgpl.html. + +Alternatively, the contents of this file may be used under the terms of the +Mozilla Public License (http://mozilla.org/MPL) or the GNU General Public +License, as published by the Free Software Foundation, either version 2 +of the License or (at your option) any later version. +*/ +#include "inc/UtfCodec.h" +#include <cstring> +#include <cstdlib> + +#include "inc/bits.h" +#include "inc/Segment.h" +#include "graphite2/Font.h" +#include "inc/CharInfo.h" +#include "inc/debug.h" +#include "inc/Slot.h" +#include "inc/Main.h" +#include "inc/CmapCache.h" +#include "inc/Collider.h" +#include "graphite2/Segment.h" + + +using namespace graphite2; + +Segment::Segment(unsigned int numchars, const Face* face, uint32 script, int textDir) +: m_freeSlots(NULL), + m_freeJustifies(NULL), + m_charinfo(new CharInfo[numchars]), + m_collisions(NULL), + m_face(face), + m_silf(face->chooseSilf(script)), + m_first(NULL), + m_last(NULL), + m_bufSize(numchars + 10), + m_numGlyphs(numchars), + m_numCharinfo(numchars), + m_passBits(m_silf->aPassBits() ? -1 : 0), + m_defaultOriginal(0), + m_dir(textDir), + m_flags(((m_silf->flags() & 0x20) != 0) << 1) +{ + freeSlot(newSlot()); + m_bufSize = log_binary(numchars)+1; +} + +Segment::~Segment() +{ + for (SlotRope::iterator i = m_slots.begin(); i != m_slots.end(); ++i) + free(*i); + for (AttributeRope::iterator i = m_userAttrs.begin(); i != m_userAttrs.end(); ++i) + free(*i); + for (JustifyRope::iterator i = m_justifies.begin(); i != m_justifies.end(); ++i) + free(*i); + delete[] m_charinfo; + free(m_collisions); +} + +#ifndef GRAPHITE2_NSEGCACHE +SegmentScopeState Segment::setScope(Slot * firstSlot, Slot * lastSlot, size_t subLength) +{ + SegmentScopeState state; + state.numGlyphsOutsideScope = m_numGlyphs - subLength; + state.realFirstSlot = m_first; + state.slotBeforeScope = firstSlot->prev(); + state.slotAfterScope = lastSlot->next(); + state.realLastSlot = m_last; + firstSlot->prev(NULL); + lastSlot->next(NULL); + assert(m_defaultOriginal == 0); + m_defaultOriginal = firstSlot->original(); + m_numGlyphs = subLength; + m_first = firstSlot; + m_last = lastSlot; + return state; +} + +void Segment::removeScope(SegmentScopeState & state) +{ + m_numGlyphs = state.numGlyphsOutsideScope + m_numGlyphs; + if (state.slotBeforeScope) + { + state.slotBeforeScope->next(m_first); + m_first->prev(state.slotBeforeScope); + m_first = state.realFirstSlot; + } + if (state.slotAfterScope) + { + state.slotAfterScope->prev(m_last); + m_last->next(state.slotAfterScope); + m_last = state.realLastSlot; + } + m_defaultOriginal = 0; +} + +#if 0 +void Segment::append(const Segment &other) +{ + Rect bbox = other.m_bbox + m_advance; + + m_slots.insert(m_slots.end(), other.m_slots.begin(), other.m_slots.end()); + CharInfo* pNewCharInfo = new CharInfo[m_numCharinfo+other.m_numCharinfo]; //since CharInfo has no constructor, this doesn't do much + for (unsigned int i=0 ; i<m_numCharinfo ; ++i) + pNewCharInfo[i] = m_charinfo[i]; + m_last->next(other.m_first); + other.m_last->prev(m_last); + m_userAttrs.insert(m_userAttrs.end(), other.m_userAttrs.begin(), other.m_userAttrs.end()); + + delete[] m_charinfo; + m_charinfo = pNewCharInfo; + pNewCharInfo += m_numCharinfo ; + for (unsigned int i=0 ; i<m_numCharinfo ; ++i) + pNewCharInfo[i] = other.m_charinfo[i]; + + m_numCharinfo += other.m_numCharinfo; + m_numGlyphs += other.m_numGlyphs; + m_advance = m_advance + other.m_advance; + m_bbox = m_bbox.widen(bbox); + m_passBits &= other.passBits(); +} +#endif +#endif // GRAPHITE2_NSEGCACHE + +void Segment::appendSlot(int id, int cid, int gid, int iFeats, size_t coffset) +{ + Slot *aSlot = newSlot(); + + if (!aSlot) return; + m_charinfo[id].init(cid); + m_charinfo[id].feats(iFeats); + m_charinfo[id].base(coffset); + const GlyphFace * theGlyph = m_face->glyphs().glyphSafe(gid); + m_charinfo[id].breakWeight(theGlyph ? theGlyph->attrs()[m_silf->aBreak()] : 0); + + aSlot->child(NULL); + aSlot->setGlyph(this, gid, theGlyph); + aSlot->originate(id); + aSlot->before(id); + aSlot->after(id); + if (m_last) m_last->next(aSlot); + aSlot->prev(m_last); + m_last = aSlot; + if (!m_first) m_first = aSlot; + if (theGlyph && m_silf->aPassBits()) + m_passBits &= theGlyph->attrs()[m_silf->aPassBits()] + | (m_silf->numPasses() > 16 ? (theGlyph->attrs()[m_silf->aPassBits() + 1] << 16) : 0); +} + +Slot *Segment::newSlot() +{ + if (!m_freeSlots) + { + // check that the segment doesn't grow indefinintely + if (m_numGlyphs > m_numCharinfo * MAX_SEG_GROWTH_FACTOR) + return NULL; + int numUser = m_silf->numUser(); +#if !defined GRAPHITE2_NTRACING + if (m_face->logger()) ++numUser; +#endif + Slot *newSlots = grzeroalloc<Slot>(m_bufSize); + int16 *newAttrs = grzeroalloc<int16>(m_bufSize * numUser); + if (!newSlots || !newAttrs) + { + free(newSlots); + free(newAttrs); + return NULL; + } + for (size_t i = 0; i < m_bufSize; i++) + { + ::new (newSlots + i) Slot(newAttrs + i * numUser); + newSlots[i].next(newSlots + i + 1); + } + newSlots[m_bufSize - 1].next(NULL); + newSlots[0].next(NULL); + m_slots.push_back(newSlots); + m_userAttrs.push_back(newAttrs); + m_freeSlots = (m_bufSize > 1)? newSlots + 1 : NULL; + return newSlots; + } + Slot *res = m_freeSlots; + m_freeSlots = m_freeSlots->next(); + res->next(NULL); + return res; +} + +void Segment::freeSlot(Slot *aSlot) +{ + if (m_last == aSlot) m_last = aSlot->prev(); + if (m_first == aSlot) m_first = aSlot->next(); + if (aSlot->attachedTo()) + aSlot->attachedTo()->removeChild(aSlot); + while (aSlot->firstChild()) + { + if (aSlot->firstChild()->attachedTo() == aSlot) + { + aSlot->firstChild()->attachTo(NULL); + aSlot->removeChild(aSlot->firstChild()); + } + else + aSlot->firstChild(NULL); + } + // reset the slot incase it is reused + ::new (aSlot) Slot(aSlot->userAttrs()); + memset(aSlot->userAttrs(), 0, m_silf->numUser() * sizeof(int16)); + // Update generation counter for debug +#if !defined GRAPHITE2_NTRACING + if (m_face->logger()) + ++aSlot->userAttrs()[m_silf->numUser()]; +#endif + // update next pointer + if (!m_freeSlots) + aSlot->next(NULL); + else + aSlot->next(m_freeSlots); + m_freeSlots = aSlot; +} + +SlotJustify *Segment::newJustify() +{ + if (!m_freeJustifies) + { + const size_t justSize = SlotJustify::size_of(m_silf->numJustLevels()); + byte *justs = grzeroalloc<byte>(justSize * m_bufSize); + if (!justs) return NULL; + for (int i = m_bufSize - 2; i >= 0; --i) + { + SlotJustify *p = reinterpret_cast<SlotJustify *>(justs + justSize * i); + SlotJustify *next = reinterpret_cast<SlotJustify *>(justs + justSize * (i + 1)); + p->next = next; + } + m_freeJustifies = (SlotJustify *)justs; + m_justifies.push_back(m_freeJustifies); + } + SlotJustify *res = m_freeJustifies; + m_freeJustifies = m_freeJustifies->next; + res->next = NULL; + return res; +} + +void Segment::freeJustify(SlotJustify *aJustify) +{ + int numJust = m_silf->numJustLevels(); + if (m_silf->numJustLevels() <= 0) numJust = 1; + aJustify->next = m_freeJustifies; + memset(aJustify->values, 0, numJust*SlotJustify::NUMJUSTPARAMS*sizeof(int16)); + m_freeJustifies = aJustify; +} + +#ifndef GRAPHITE2_NSEGCACHE +void Segment::splice(size_t offset, size_t length, Slot * const startSlot, + Slot * endSlot, const Slot * srcSlot, + const size_t numGlyphs) +{ + size_t numChars = length; + extendLength(numGlyphs - length); + // remove any extra + if (numGlyphs < length) + { + Slot * end = endSlot->next(); + do + { + endSlot = endSlot->prev(); + freeSlot(endSlot->next()); + } while (numGlyphs < --length); + endSlot->next(end); + if (end) + end->prev(endSlot); + } + else + { + // insert extra slots if needed + while (numGlyphs > length) + { + Slot * extra = newSlot(); + if (!extra) return; + extra->prev(endSlot); + extra->next(endSlot->next()); + endSlot->next(extra); + if (extra->next()) + extra->next()->prev(extra); + if (m_last == endSlot) + m_last = extra; + endSlot = extra; + ++length; + } + } + + endSlot = endSlot->next(); + assert(numGlyphs == length); + assert(offset + numChars <= m_numCharinfo); + Slot * indexmap[eMaxSpliceSize*3]; + assert(numGlyphs < sizeof indexmap/sizeof *indexmap); + Slot * slot = startSlot; + for (uint16 i=0; i < numGlyphs; slot = slot->next(), ++i) + indexmap[i] = slot; + + for (slot = startSlot; slot != endSlot; slot = slot->next(), srcSlot = srcSlot->next()) + { + slot->set(*srcSlot, offset, m_silf->numUser(), m_silf->numJustLevels(), numChars); + if (srcSlot->attachedTo()) slot->attachTo(indexmap[srcSlot->attachedTo()->index()]); + if (srcSlot->nextSibling()) slot->m_sibling = indexmap[srcSlot->nextSibling()->index()]; + if (srcSlot->firstChild()) slot->m_child = indexmap[srcSlot->firstChild()->index()]; + } +} +#endif // GRAPHITE2_NSEGCACHE + +// reverse the slots but keep diacritics in their same position after their bases +void Segment::reverseSlots() +{ + m_dir = m_dir ^ 64; // invert the reverse flag + if (m_first == m_last) return; // skip 0 or 1 glyph runs + + Slot *t = 0; + Slot *curr = m_first; + Slot *tlast; + Slot *tfirst; + Slot *out = 0; + + while (curr && getSlotBidiClass(curr) == 16) + curr = curr->next(); + if (!curr) return; + tfirst = curr->prev(); + tlast = curr; + + while (curr) + { + if (getSlotBidiClass(curr) == 16) + { + Slot *d = curr->next(); + while (d && getSlotBidiClass(d) == 16) + d = d->next(); + + d = d ? d->prev() : m_last; + Slot *p = out->next(); // one after the diacritics. out can't be null + if (p) + p->prev(d); + else + tlast = d; + t = d->next(); + d->next(p); + curr->prev(out); + out->next(curr); + } + else // will always fire first time round the loop + { + if (out) + out->prev(curr); + t = curr->next(); + curr->next(out); + out = curr; + } + curr = t; + } + out->prev(tfirst); + if (tfirst) + tfirst->next(out); + else + m_first = out; + m_last = tlast; +} + +void Segment::linkClusters(Slot *s, Slot * end) +{ + end = end->next(); + + for (; s != end && !s->isBase(); s = s->next()); + Slot * ls = s; + + if (m_dir & 1) + { + for (; s != end; s = s->next()) + { + if (!s->isBase()) continue; + + s->sibling(ls); + ls = s; + } + } + else + { + for (; s != end; s = s->next()) + { + if (!s->isBase()) continue; + + ls->sibling(s); + ls = s; + } + } +} + +Position Segment::positionSlots(const Font *font, Slot * iStart, Slot * iEnd, bool isRtl, bool isFinal) +{ + Position currpos(0., 0.); + float clusterMin = 0.; + Rect bbox; + bool reorder = (currdir() != isRtl); + + if (reorder) + { + Slot *temp; + reverseSlots(); + temp = iStart; + iStart = iEnd; + iEnd = temp; + } + if (!iStart) iStart = m_first; + if (!iEnd) iEnd = m_last; + + if (!iStart || !iEnd) // only true for empty segments + return currpos; + + if (isRtl) + { + for (Slot * s = iEnd, * const end = iStart->prev(); s && s != end; s = s->prev()) + { + if (s->isBase()) + currpos = s->finalise(this, font, currpos, bbox, 0, clusterMin = currpos.x, isRtl, isFinal); + } + } + else + { + for (Slot * s = iStart, * const end = iEnd->next(); s && s != end; s = s->next()) + { + if (s->isBase()) + currpos = s->finalise(this, font, currpos, bbox, 0, clusterMin = currpos.x, isRtl, isFinal); + } + } + if (reorder) + reverseSlots(); + return currpos; +} + + +void Segment::associateChars(int offset, int numChars) +{ + int i = 0, j = 0; + CharInfo *c, *cend; + for (c = m_charinfo + offset, cend = m_charinfo + offset + numChars; c != cend; ++c) + { + c->before(-1); + c->after(-1); + } + for (Slot * s = m_first; s; s->index(i++), s = s->next()) + { + j = s->before(); + if (j < 0) continue; + + for (const int after = s->after(); j <= after; ++j) + { + c = charinfo(j); + if (c->before() == -1 || i < c->before()) c->before(i); + if (c->after() < i) c->after(i); + } + } + for (Slot *s = m_first; s; s = s->next()) + { + int a; + for (a = s->after() + 1; a < offset + numChars && charinfo(a)->after() < 0; ++a) + { charinfo(a)->after(s->index()); } + --a; + s->after(a); + + for (a = s->before() - 1; a >= offset && charinfo(a)->before() < 0; --a) + { charinfo(a)->before(s->index()); } + ++a; + s->before(a); + } +} + + +template <typename utf_iter> +inline void process_utf_data(Segment & seg, const Face & face, const int fid, utf_iter c, size_t n_chars) +{ + const Cmap & cmap = face.cmap(); + int slotid = 0; + + const typename utf_iter::codeunit_type * const base = c; + for (; n_chars; --n_chars, ++c, ++slotid) + { + const uint32 usv = *c; + uint16 gid = cmap[usv]; + if (!gid) gid = face.findPseudo(usv); + seg.appendSlot(slotid, usv, gid, fid, c - base); + } +} + + +bool Segment::read_text(const Face *face, const Features* pFeats/*must not be NULL*/, gr_encform enc, const void* pStart, size_t nChars) +{ + assert(face); + assert(pFeats); + if (!m_charinfo) return false; + + // utf iterator is self recovering so we don't care about the error state of the iterator. + switch (enc) + { + case gr_utf8: process_utf_data(*this, *face, addFeatures(*pFeats), utf8::const_iterator(pStart), nChars); break; + case gr_utf16: process_utf_data(*this, *face, addFeatures(*pFeats), utf16::const_iterator(pStart), nChars); break; + case gr_utf32: process_utf_data(*this, *face, addFeatures(*pFeats), utf32::const_iterator(pStart), nChars); break; + } + return true; +} + +void Segment::doMirror(uint16 aMirror) +{ + Slot * s; + for (s = m_first; s; s = s->next()) + { + unsigned short g = glyphAttr(s->gid(), aMirror); + if (g && (!(dir() & 4) || !glyphAttr(s->gid(), aMirror + 1))) + s->setGlyph(this, g); + } +} + +bool Segment::initCollisions() +{ + m_collisions = grzeroalloc<SlotCollision>(slotCount()); + if (!m_collisions) return false; + + for (Slot *p = m_first; p; p = p->next()) + if (p->index() < slotCount()) + ::new (collisionInfo(p)) SlotCollision(this, p); + else + return false; + return true; +} |