harfbuzz/src/graphite2/src/Slot.cpp

570 lines
20 KiB
C++

// SPDX-License-Identifier: MIT
// Copyright 2010, SIL International, All rights reserved.
#include "inc/Segment.h"
#include "inc/CharInfo.h"
#include "inc/Collider.h"
#include "inc/Font.h"
#include "inc/ShapingContext.hpp"
#include "inc/Slot.h"
#include "inc/Silf.h"
#include "inc/Rule.h"
using namespace graphite2;
auto Slot::attributes::operator = (attributes const & rhs) -> attributes & {
if (this != &rhs) {
reserve(rhs.num_attrs(), rhs.num_justs());
if (!rhs.is_inline() && external) {
auto const sz = external->n_attrs
+ external->n_justs*NUMJUSTPARAMS + 1;
memcpy(external, rhs.external, sz*sizeof(uint16_t));
} else local = rhs.local;
}
return *this;
}
auto Slot::attributes::operator = (attributes && rhs) noexcept -> attributes & {
if (!is_inline()) free(external);
external = rhs.external;
rhs.external = nullptr;
return *this;
}
void Slot::attributes::reserve(size_t target_num_attrs, size_t target_num_justs) {
assert(target_num_attrs < 256);
assert(target_num_justs < 256);
if (num_attrs() >= target_num_attrs
&& num_justs() >= target_num_justs)
return;
if (target_num_justs > 0
|| target_num_attrs > sizeof local.data/sizeof *local.data)
{
auto const sz = target_num_attrs + target_num_justs*NUMJUSTPARAMS + 1;
if (is_inline()) { // Convert to non-inline form.
auto box = reinterpret_cast<decltype(external)>(grzeroalloc<int16_t>(sz));
if (box) {
if (local.n_attrs) memcpy(box->data, local.data, local.n_attrs*sizeof *local.data);
external = box;
external->n_attrs = target_num_attrs;
external->n_justs = target_num_justs;
}
} else { // Grow the existing buffer.
external = static_cast<decltype(external)>(realloc(external, sz*sizeof *local.data));
external->n_attrs = target_num_attrs;
external->n_justs = target_num_justs;
}
}
else local.n_attrs = target_num_attrs;
}
void Slot::init_just_infos(Segment const & seg)
{
auto const target_num_justs = seg.silf()->numJustLevels();
for (int i = target_num_justs - 1; i >= 0; --i)
{
Justinfo *justs = seg.silf()->justAttrs() + i;
int16_t *v = m_attrs.just_info() + i * NUMJUSTPARAMS;
v[0] = seg.glyphAttr(gid(), justs->attrStretch());
v[1] = seg.glyphAttr(gid(), justs->attrShrink());
v[2] = seg.glyphAttr(gid(), justs->attrStep());
v[3] = seg.glyphAttr(gid(), justs->attrWeight());
}
}
Slot & Slot::operator = (Slot && rhs) noexcept
{
if (this != &rhs)
{
Slot_data::operator=(std::move(rhs));
m_parent_offset = 0;
m_attrs = std::move(rhs.m_attrs);
#if !defined GRAPHITE2_NTRACING
m_gen = rhs.m_gen;
#endif
}
return *this;
}
Slot & Slot::operator = (Slot const & rhs)
{
if (this != &rhs)
{
Slot_data::operator=(rhs);
m_parent_offset = 0;
m_attrs = rhs.m_attrs;
#if !defined GRAPHITE2_NTRACING
m_gen = rhs.m_gen;
#endif
}
return *this;
}
void Slot::update(int /*numGrSlots*/, int numCharInfo, Position &relpos)
{
m_before += numCharInfo;
m_after += numCharInfo;
m_position = m_position + relpos;
}
Position Slot::collision_shift(Segment const & seg) const
{
auto const cinfo = seg.collisionInfo(*this);
return cinfo ? cinfo->offset() : Position{};
}
Position Slot::update_cluster_metric(Segment const & seg, bool const rtl, bool const is_final, float & clsb, float & crsb, unsigned depth)
{
// Bail out early if the attachment chain is too deep.
if (depth == 0) return Position();
// Incorporate shift into positioning, and cluster rsb calculations.
Position shift = {m_shift.x + m_just, m_shift.y};
auto const collision_info = seg.collisionInfo(*this);
if (is_final && collision_info) {
// if (!(collision_info->flags() & SlotCollision::COLL_KERN) || rtl)
shift += collision_info->offset();
}
// Only consider if design space advance is a non-zero whole unit.
auto const slot_adv = m_advance.x + m_just;
auto const parent = attachedTo();
auto pos = shift;
if (!parent) {
clsb = min(0.0f, clsb);
pos = {0,0};
shift = {0,0};
} else {
auto base = parent->update_cluster_metric(seg, rtl, is_final, clsb, crsb, depth-1);
m_position = (pos += base + m_attach - m_with);
if (m_advance.x >= 0.5f)
clsb = min(pos.x, clsb);
}
if (m_advance.x >= 0.5f)
// We only consider shift for attached glyphs not ourselves.
crsb = max(pos.x - shift.x + slot_adv, crsb);
return pos;
}
Position Slot::finalise(const Segment & seg, const Font *font, Position & base, Rect & bbox, uint8_t attrLevel, float & clusterMin, bool rtl, bool isFinal, int depth)
{
// assert(false);
SlotCollision *coll = NULL;
if (depth > 100 || (attrLevel && m_attLevel > attrLevel)) return Position(0, 0);
float scale = font ? font->scale() : 1.0f;
Position shift(m_shift.x * (rtl * -2 + 1) + m_just, m_shift.y);
float tAdvance = m_advance.x + m_just;
if (isFinal && (coll = seg.collisionInfo(*this)))
{
const Position &collshift = coll->offset();
if (!(coll->flags() & SlotCollision::COLL_KERN) || rtl)
shift = shift + collshift;
}
const GlyphFace * glyphFace = seg.getFace()->glyphs().glyphSafe(glyph());
if (font)
{
scale = font->scale();
shift *= scale;
if (font->isHinted() && glyphFace)
tAdvance = (m_advance.x - glyphFace->theAdvance().x + m_just) * scale + font->advance(glyph());
else
tAdvance *= scale;
}
Position res;
m_position = base + shift;
if (isBase())
{
res = base + Position(tAdvance, m_advance.y * scale);
clusterMin = m_position.x;
}
else
{
float tAdv;
m_position += (m_attach - m_with) * scale;
tAdv = m_advance.x >= 0.5f ? m_position.x + tAdvance - shift.x : 0.f;
res = Position(tAdv, 0);
if ((m_advance.x >= 0.5f || m_position.x < 0) && m_position.x < clusterMin) clusterMin = m_position.x;
}
if (glyphFace)
{
Rect ourBbox = glyphFace->theBBox() * scale + m_position;
bbox = bbox.widen(ourBbox);
}
for (auto c = children(); c != end(); ++c)
{
auto tRes = c->finalise(seg, font, m_position, bbox, attrLevel, clusterMin, rtl, isFinal, depth + 1);
if ((isBase() || m_advance.x >= 0.5f) && tRes.x > res.x) res = tRes;
}
if (isParent())
{
Position tRes = children()->finalise(seg, font, m_position, bbox, attrLevel, clusterMin, rtl, isFinal, depth + 1);
if ((isBase() || m_advance.x >= 0.5f) && tRes.x > res.x) res = tRes;
}
auto sibling = child_iterator(this);
if (!isBase() && ++sibling != end())
{
Position tRes = sibling->finalise(seg, font, base, bbox, attrLevel, clusterMin, rtl, isFinal, depth + 1);
if (tRes.x > res.x) res = tRes;
}
if (isBase() && clusterMin < base.x)
{
Position adj = Position(m_position.x - clusterMin, 0.);
res += adj;
m_position += adj;
if (isParent()) children()->floodShift(adj);
}
return res;
}
int32_t Slot::clusterMetric(const Segment & seg, metrics metric, uint8_t attrLevel, bool rtl) const
{
if (glyph() >= seg.getFace()->glyphs().numGlyphs())
return 0;
Rect bbox = seg.theGlyphBBoxTemporary(glyph());
auto & slot = const_cast<Slot &>(*this);
float range[2] = {0, 0};
for (auto s = cluster(); s != end(); ++s)
{
auto base = slot.update_cluster_metric(seg, rtl, false, range[0], range[1]);
bbox.widen(seg.getFace()->glyphs().glyphSafe(glyph())->theBBox() + base);
}
Position res = {range[1], m_advance.y};
// Position res = const_cast<Slot *>(this)->finalise(seg, NULL, base, bbox, attrLevel, clusterMin, rtl, false);
switch (metric)
{
case kgmetLsb :
return int32_t(bbox.bl.x);
case kgmetRsb :
return int32_t(res.x - bbox.tr.x);
case kgmetBbTop :
return int32_t(bbox.tr.y);
case kgmetBbBottom :
return int32_t(bbox.bl.y);
case kgmetBbLeft :
return int32_t(bbox.bl.x);
case kgmetBbRight :
return int32_t(bbox.tr.x);
case kgmetBbWidth :
return int32_t(bbox.tr.x - bbox.bl.x);
case kgmetBbHeight :
return int32_t(bbox.tr.y - bbox.bl.y);
case kgmetAdvWidth :
return int32_t(res.x);
case kgmetAdvHeight :
return int32_t(res.y);
default :
return 0;
}
}
#define SLOTGETCOLATTR(x) { SlotCollision *c = seg.collisionInfo(*this); return c ? int(c-> x) : 0; }
int Slot::getAttr(const Segment & seg, attrCode ind, uint8_t subindex) const
{
if (ind >= gr_slatJStretch && ind < gr_slatJStretch + 20 && ind != gr_slatJWidth)
{
int indx = ind - gr_slatJStretch;
return getJustify(seg, indx / 5, indx % 5);
}
switch (ind)
{
case gr_slatAdvX : return int(m_advance.x);
case gr_slatAdvY : return int(m_advance.y);
case gr_slatAttTo : return m_parent_offset ? 1 : 0;
case gr_slatAttX : return int(m_attach.x);
case gr_slatAttY : return int(m_attach.y);
case gr_slatAttXOff :
case gr_slatAttYOff : return 0;
case gr_slatAttWithX : return int(m_with.x);
case gr_slatAttWithY : return int(m_with.y);
case gr_slatAttWithXOff:
case gr_slatAttWithYOff:return 0;
case gr_slatAttLevel : return m_attLevel;
case gr_slatBreak : return seg.charinfo(m_original)->breakWeight();
case gr_slatCompRef : return 0;
case gr_slatDir : return seg.dir() & 1;
case gr_slatInsert : return insertBefore();
case gr_slatPosX : return int(m_position.x); // but need to calculate it
case gr_slatPosY : return int(m_position.y);
case gr_slatShiftX : return int(m_shift.x);
case gr_slatShiftY : return int(m_shift.y);
case gr_slatMeasureSol: return -1; // err what's this?
case gr_slatMeasureEol: return -1;
case gr_slatJWidth: return int(m_just);
case gr_slatUserDefnV1: subindex = 0; GR_FALLTHROUGH;
// no break
case gr_slatUserDefn : return subindex < m_attrs.num_attrs() ? m_attrs.user_attributes()[subindex] : 0;
case gr_slatSegSplit : return seg.charinfo(m_original)->flags() & 3;
case gr_slatBidiLevel: return m_bidiLevel;
case gr_slatColFlags : { SlotCollision *c = seg.collisionInfo(*this); return c ? c->flags() : 0; }
case gr_slatColLimitblx:SLOTGETCOLATTR(limit().bl.x)
case gr_slatColLimitbly:SLOTGETCOLATTR(limit().bl.y)
case gr_slatColLimittrx:SLOTGETCOLATTR(limit().tr.x)
case gr_slatColLimittry:SLOTGETCOLATTR(limit().tr.y)
case gr_slatColShiftx : SLOTGETCOLATTR(offset().x)
case gr_slatColShifty : SLOTGETCOLATTR(offset().y)
case gr_slatColMargin : SLOTGETCOLATTR(margin())
case gr_slatColMarginWt:SLOTGETCOLATTR(marginWt())
case gr_slatColExclGlyph:SLOTGETCOLATTR(exclGlyph())
case gr_slatColExclOffx:SLOTGETCOLATTR(exclOffset().x)
case gr_slatColExclOffy:SLOTGETCOLATTR(exclOffset().y)
case gr_slatSeqClass : SLOTGETCOLATTR(seqClass())
case gr_slatSeqProxClass:SLOTGETCOLATTR(seqProxClass())
case gr_slatSeqOrder : SLOTGETCOLATTR(seqOrder())
case gr_slatSeqAboveXoff:SLOTGETCOLATTR(seqAboveXoff())
case gr_slatSeqAboveWt: SLOTGETCOLATTR(seqAboveWt())
case gr_slatSeqBelowXlim:SLOTGETCOLATTR(seqBelowXlim())
case gr_slatSeqBelowWt: SLOTGETCOLATTR(seqBelowWt())
case gr_slatSeqValignHt:SLOTGETCOLATTR(seqValignHt())
case gr_slatSeqValignWt:SLOTGETCOLATTR(seqValignWt())
default : return 0;
}
}
#define SLOTCOLSETATTR(x) { \
SlotCollision *c = seg.collisionInfo(*this); \
if (c) { c-> x ; c->setFlags(c->flags() & ~SlotCollision::COLL_KNOWN); } \
break; }
#define SLOTCOLSETCOMPLEXATTR(t, y, x) { \
SlotCollision *c = seg.collisionInfo(*this); \
if (c) { \
const t &s = c-> y; \
c-> x ; c->setFlags(c->flags() & ~SlotCollision::COLL_KNOWN); } \
break; }
void Slot::setAttr(Segment & seg, attrCode ind, uint8_t subindex, int16_t value, const ShapingContext & ctxt)
{
if (ind == gr_slatUserDefnV1)
{
ind = gr_slatUserDefn;
subindex = 0;
if (seg.numAttrs() == 0)
return;
}
else if (ind >= gr_slatJStretch && ind < gr_slatJStretch + 20 && ind != gr_slatJWidth)
{
int indx = ind - gr_slatJStretch;
return setJustify(seg, indx / 5, indx % 5, value);
}
switch (ind)
{
case gr_slatAdvX : m_advance.x = value; break;
case gr_slatAdvY : m_advance.y = value; break;
case gr_slatAttTo :
{
const uint16_t idx = uint16_t(value);
if (idx < ctxt.map.size() && ctxt.map[idx].is_valid())
{
auto other = &*ctxt.map[idx];
if (other == this || other == attachedTo() || other->copied()) break;
if (!isBase()) { attachedTo()->remove_child(this); }
auto pOther = other;
int count = 0;
bool foundOther = false;
while (pOther)
{
++count;
if (pOther == this) foundOther = true;
pOther = pOther->attachedTo();
}
if (count < 100 && !foundOther && other->add_child(this))
{
attachTo(other);
if ((ctxt.dir != 0) ^ (idx > subindex))
m_with = Position(advance(), 0);
else // normal match to previous root
m_attach = Position(other->advance(), 0);
}
}
break;
}
case gr_slatAttX : m_attach.x = value; break;
case gr_slatAttY : m_attach.y = value; break;
case gr_slatAttXOff :
case gr_slatAttYOff : break;
case gr_slatAttWithX : m_with.x = value; break;
case gr_slatAttWithY : m_with.y = value; break;
case gr_slatAttWithXOff :
case gr_slatAttWithYOff : break;
case gr_slatAttLevel :
m_attLevel = byte(value);
break;
case gr_slatBreak :
seg.charinfo(m_original)->breakWeight(value);
break;
case gr_slatCompRef : break; // not sure what to do here
case gr_slatDir : break;
case gr_slatInsert :
insertBefore(value? true : false);
break;
case gr_slatPosX : break; // can't set these here
case gr_slatPosY : break;
case gr_slatShiftX : m_shift.x = value; break;
case gr_slatShiftY : m_shift.y = value; break;
case gr_slatMeasureSol : break;
case gr_slatMeasureEol : break;
case gr_slatJWidth : just(value); break;
case gr_slatSegSplit : seg.charinfo(m_original)->addflags(value & 3); break;
case gr_slatUserDefn : assert(subindex < m_attrs.num_attrs()); m_attrs.user_attributes()[subindex] = value; break;
case gr_slatColFlags : {
SlotCollision *c = seg.collisionInfo(*this);
if (c)
c->setFlags(value);
break; }
case gr_slatColLimitblx : SLOTCOLSETCOMPLEXATTR(Rect, limit(), setLimit(Rect(Position(value, s.bl.y), s.tr)))
case gr_slatColLimitbly : SLOTCOLSETCOMPLEXATTR(Rect, limit(), setLimit(Rect(Position(s.bl.x, value), s.tr)))
case gr_slatColLimittrx : SLOTCOLSETCOMPLEXATTR(Rect, limit(), setLimit(Rect(s.bl, Position(value, s.tr.y))))
case gr_slatColLimittry : SLOTCOLSETCOMPLEXATTR(Rect, limit(), setLimit(Rect(s.bl, Position(s.tr.x, value))))
case gr_slatColMargin : SLOTCOLSETATTR(setMargin(value))
case gr_slatColMarginWt : SLOTCOLSETATTR(setMarginWt(value))
case gr_slatColExclGlyph : SLOTCOLSETATTR(setExclGlyph(value))
case gr_slatColExclOffx : SLOTCOLSETCOMPLEXATTR(Position, exclOffset(), setExclOffset(Position(value, s.y)))
case gr_slatColExclOffy : SLOTCOLSETCOMPLEXATTR(Position, exclOffset(), setExclOffset(Position(s.x, value)))
case gr_slatSeqClass : SLOTCOLSETATTR(setSeqClass(value))
case gr_slatSeqProxClass : SLOTCOLSETATTR(setSeqProxClass(value))
case gr_slatSeqOrder : SLOTCOLSETATTR(setSeqOrder(value))
case gr_slatSeqAboveXoff : SLOTCOLSETATTR(setSeqAboveXoff(value))
case gr_slatSeqAboveWt : SLOTCOLSETATTR(setSeqAboveWt(value))
case gr_slatSeqBelowXlim : SLOTCOLSETATTR(setSeqBelowXlim(value))
case gr_slatSeqBelowWt : SLOTCOLSETATTR(setSeqBelowWt(value))
case gr_slatSeqValignHt : SLOTCOLSETATTR(setSeqValignHt(value))
case gr_slatSeqValignWt : SLOTCOLSETATTR(setSeqValignWt(value))
default :
break;
}
}
int Slot::getJustify(const Segment & seg, uint8_t level, uint8_t subindex) const
{
if (level && level >= seg.silf()->numJustLevels()) return 0;
if (has_justify())
return m_attrs.just_info()[level * Slot::NUMJUSTPARAMS + subindex];
if (level >= seg.silf()->numJustLevels()) return 0;
Justinfo *jAttrs = seg.silf()->justAttrs() + level;
switch (subindex) {
case 0 : return seg.glyphAttr(gid(), jAttrs->attrStretch());
case 1 : return seg.glyphAttr(gid(), jAttrs->attrShrink());
case 2 : return seg.glyphAttr(gid(), jAttrs->attrStep());
case 3 : return seg.glyphAttr(gid(), jAttrs->attrWeight());
case 4 : return 0; // not been set yet, so clearly 0
default: return 0;
}
}
void Slot::setJustify(Segment & seg, uint8_t level, uint8_t subindex, int16_t value)
{
if (level && level >= seg.silf()->numJustLevels()) return;
if (!has_justify()) {
m_attrs.reserve(m_attrs.num_attrs(), std::max(1ul,seg.silf()->numJustLevels()));
init_just_infos(seg);
}
m_attrs.just_info()[level * Slot::NUMJUSTPARAMS + subindex] = value;
}
bool Slot::add_child(Slot *ap)
{
if (this == ap || ap->attachedTo() == this)
return false;
ap->attachTo(this);
m_flags.children = true;
for(auto first = min(ap, this)+1, last = max(ap, this)+1; first != last; ++first)
first->clusterhead(false);
return true;
}
bool Slot::remove_child(Slot *ap)
{
if (this == ap || !isParent() || !ap) return false;
if (ap->m_parent_offset > 0 && ap->m_flags.clusterhead)
{
for (auto first = min(ap, this)+1, last = max(ap, this)+1; first != last; ++first)
{
if (first->base() != ap)
{
first->clusterhead(true);
break;
}
}
}
ap->m_parent_offset = 0;
ap->m_flags.clusterhead = true;
m_flags.children = !m_flags.clusterhead || (children() != end());
return true;
}
void Slot::glyph(Segment & seg, uint16_t glyphid, const GlyphFace * theGlyph)
{
m_glyphid = glyphid;
m_bidiCls = -1;
if (!theGlyph)
{
theGlyph = seg.getFace()->glyphs().glyphSafe(glyphid);
if (!theGlyph)
{
m_realglyphid = 0;
m_advance = Position(0.,0.);
return;
}
}
m_realglyphid = theGlyph->attrs()[seg.silf()->aPseudo()];
if (m_realglyphid > seg.getFace()->glyphs().numGlyphs())
m_realglyphid = 0;
const GlyphFace *aGlyph = theGlyph;
if (m_realglyphid)
{
aGlyph = seg.getFace()->glyphs().glyphSafe(m_realglyphid);
if (!aGlyph) aGlyph = theGlyph;
}
m_advance = Position(aGlyph->theAdvance().x, 0.);
if (seg.silf()->aPassBits())
{
seg.mergePassBits(uint8_t(theGlyph->attrs()[seg.silf()->aPassBits()]));
if (seg.silf()->numPasses() > 16)
seg.mergePassBits(theGlyph->attrs()[seg.silf()->aPassBits()+1] << 16);
}
}
void Slot::floodShift(Position adj, int depth)
{
if (depth > 100)
return;
m_position += adj;
for (auto c = children(); c != end(); ++c)
c->floodShift(adj, depth + 1);
}
bool Slot::has_base(const Slot *base) const
{
for (auto p = attachedTo(); p; p = p->attachedTo())
if (p == base)
return true;
return false;
}