virtualx-engine/thirdparty/graphite/src/Segment.cpp
Rémi Verschelde 0ee6ffb257
graphite: Update to latest Git, switch to MIT license
Graphite is now available under:
MIT OR MPL-2.0 OR LGPL-2.1-or-later OR GPL-2.0-or-later

We pick MIT which is the same as Godot's main license for simplicity.

Remove define to skip deprecation warnings, upstream fixed those.
2022-12-13 10:06:00 +01:00

400 lines
11 KiB
C++

// SPDX-License-Identifier: MIT OR MPL-2.0 OR LGPL-2.1-or-later OR GPL-2.0-or-later
// Copyright 2010, SIL International, All rights reserved.
#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(size_t 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_defaultOriginal(0),
m_dir(textDir),
m_flags(((m_silf->flags() & 0x20) != 0) << 1),
m_passBits(m_silf->aPassBits() ? -1 : 0)
{
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);
}
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 (aSlot == nullptr) return;
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(nullptr);
aSlot->removeChild(aSlot->firstChild());
}
else
aSlot->firstChild(nullptr);
}
// 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(nullptr);
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 (ptrdiff_t 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;
}
// 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, size_t 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 + int(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;
}