diff --git a/gfx/graphite2/README.mozilla b/gfx/graphite2/README.mozilla index 9f78fcd32a5..f41cde99c43 100644 --- a/gfx/graphite2/README.mozilla +++ b/gfx/graphite2/README.mozilla @@ -1,6 +1,6 @@ This directory contains the Graphite2 library from http://hg.palaso.org/graphitedev -Current version derived from upstream changeset 1efd96aeade9 +Current version derived from upstream changeset e6539b6769cf See gfx/graphite2/moz-gr-update.sh for update procedure. diff --git a/gfx/graphite2/include/graphite2/Font.h b/gfx/graphite2/include/graphite2/Font.h index 72e8461eafb..fcbfc9d682c 100644 --- a/gfx/graphite2/include/graphite2/Font.h +++ b/gfx/graphite2/include/graphite2/Font.h @@ -29,8 +29,8 @@ #include "graphite2/Types.h" #define GR2_VERSION_MAJOR 1 -#define GR2_VERSION_MINOR 2 -#define GR2_VERSION_BUGFIX 4 +#define GR2_VERSION_MINOR 3 +#define GR2_VERSION_BUGFIX 0 #ifdef __cplusplus extern "C" diff --git a/gfx/graphite2/include/graphite2/Segment.h b/gfx/graphite2/include/graphite2/Segment.h index c89df5e8811..afcff7492a8 100644 --- a/gfx/graphite2/include/graphite2/Segment.h +++ b/gfx/graphite2/include/graphite2/Segment.h @@ -120,13 +120,45 @@ enum gr_attrCode { /// Justification weight for this glyph (not implemented) gr_slatJWeight, /// Amount this slot mush shrink or stretch in design units - gr_slatJWidth, + gr_slatJWidth = 29, /// SubSegment split point gr_slatSegSplit = gr_slatJStretch + 29, /// User defined attribute, see subattr for user attr number gr_slatUserDefn, /// Bidi level - gr_slatBidiLevel, + gr_slatBidiLevel = 56, + /// Collision flags + gr_slatColFlags, + /// Collision constraint rectangle left (bl.x) + gr_slatColLimitblx, + /// Collision constraint rectangle lower (bl.y) + gr_slatColLimitbly, + /// Collision constraint rectangle right (tr.x) + gr_slatColLimittrx, + /// Collision constraint rectangle upper (tr.y) + gr_slatColLimittry, + /// Collision shift x + gr_slatColShiftx, + /// Collision shift y + gr_slatColShifty, + /// Collision margin + gr_slatColMargin, + /// Margin cost weight + gr_slatColMarginWt, + // Additional glyph that excludes movement near this one: + gr_slatColExclGlyph, + gr_slatColExclOffx, + gr_slatColExclOffy, + // Collision sequence enforcing attributes: + gr_slatSeqClass, + gr_slatSeqProxClass, + gr_slatSeqOrder, + gr_slatSeqAboveXoff, + gr_slatSeqAboveWt, + gr_slatSeqBelowXlim, + gr_slatSeqBelowWt, + gr_slatSeqValignHt, + gr_slatSeqValignWt, /// not implemented gr_slatMax, diff --git a/gfx/graphite2/include/graphite2/Types.h b/gfx/graphite2/include/graphite2/Types.h index 268bee7a975..e8e45c1ff8f 100644 --- a/gfx/graphite2/include/graphite2/Types.h +++ b/gfx/graphite2/include/graphite2/Types.h @@ -58,12 +58,15 @@ enum gr_encform { #endif #endif #define GR2_LOCAL -#else - #if __GNUC__ >= 4 - #define GR2_API __attribute__ ((visibility("default"))) - #define GR2_LOCAL __attribute__ ((visibility("hidden"))) +#elif __GNUC__ >= 4 + #if defined GRAPHITE2_STATIC + #define GR2_API __attribute__ ((visibility("hidden"))) #else - #define GR2_API - #define GR2_LOCAL + #define GR2_API __attribute__ ((visibility("default"))) #endif + #define GR2_LOCAL __attribute__ ((visibility("hidden"))) +#else + #define GR2_API + #define GR2_LOCAL #endif + diff --git a/gfx/graphite2/src/Bidi.cpp b/gfx/graphite2/src/Bidi.cpp index 530f51abc1d..e26db2531c3 100644 --- a/gfx/graphite2/src/Bidi.cpp +++ b/gfx/graphite2/src/Bidi.cpp @@ -62,8 +62,8 @@ enum DirCode { // Hungarian: dirc }; enum DirMask { - WSflag = (1 << 7), // keep track of WS for eos handling - WSMask = ~(1 << 7) + WSflag = int8(1 << 7), // keep track of WS for eos handling + WSMask = int8(~(1 << 7)) }; inline uint8 BaseClass(Slot *s) { return s->getBidiClass() & WSMask; } @@ -540,7 +540,7 @@ void processParens(Slot *s, Segment *seg, uint8 aMirror, int level, BracketPairS BracketPair *p; for ( ; s; s = s->prev()) // walk the sequence { - uint16 ogid = seg->glyphAttr(s->gid(), aMirror); + uint16 ogid = seg->glyphAttr(s->gid(), aMirror) || s->gid(); int cls = BaseClass(s); switch(cls) @@ -571,35 +571,38 @@ void processParens(Slot *s, Segment *seg, uint8 aMirror, int level, BracketPairS mask |= 2; } } - for (p = stack.start(); p; p =p->next()) // walk the stack + if (stack.size()) { - if (p->close() && p->mask()) + for (p = stack.start(); p; p =p->next()) // walk the stack { - int dir = (level & 1) + 1; - if (p->mask() & dir) - { } - else if (p->mask() & (1 << (~level & 1))) // if inside has strong other embedding + if (p->close() && p->mask()) { - int ldir = p->before(); - if ((p->before() == OPP || p->before() == CPP) && p->prev()) + int dir = (level & 1) + 1; + if (p->mask() & dir) + { } + else if (p->mask() & (1 << (~level & 1))) // if inside has strong other embedding { - for (BracketPair *q = p->prev(); q; q = q->prev()) + int ldir = p->before(); + if ((p->before() == OPP || p->before() == CPP) && p->prev()) { - ldir = q->open()->getBidiClass(); - if (ldir < 3) break; - ldir = q->before(); - if (ldir < 3) break; + for (BracketPair *q = p->prev(); q; q = q->prev()) + { + ldir = q->open()->getBidiClass(); + if (ldir < 3) break; + ldir = q->before(); + if (ldir < 3) break; + } + if (ldir > 2) ldir = 0; } - if (ldir > 2) ldir = 0; + if (ldir > 0 && (ldir - 1) != (level & 1)) // is dir given opp. to level dir (ldir == R or L) + dir = (~level & 1) + 1; } - if (ldir > 0 && (ldir - 1) != (level & 1)) // is dir given opp. to level dir (ldir == R or L) - dir = (~level & 1) + 1; + p->open()->setBidiClass(dir); + p->close()->setBidiClass(dir); } - p->open()->setBidiClass(dir); - p->close()->setBidiClass(dir); } + stack.clear(); } - stack.clear(); } int GetDeferredNeutrals(int action, int level) diff --git a/gfx/graphite2/src/CMakeLists.txt b/gfx/graphite2/src/CMakeLists.txt index f785070830a..336dd8cc796 100644 --- a/gfx/graphite2/src/CMakeLists.txt +++ b/gfx/graphite2/src/CMakeLists.txt @@ -78,15 +78,18 @@ add_library(graphite2 SHARED CachedFace.cpp CmapCache.cpp Code.cpp + Collider.cpp + Decompressor.cpp Face.cpp FeatureMap.cpp Font.cpp GlyphFace.cpp GlyphCache.cpp + Intervals.cpp Justifier.cpp NameTable.cpp Pass.cpp - Rule.cpp + Position.cpp Segment.cpp Silf.cpp Slot.cpp @@ -112,7 +115,7 @@ endif (${CMAKE_BUILD_TYPE} STREQUAL "ClangASN") if (${CMAKE_SYSTEM_NAME} STREQUAL "Linux") set_target_properties(graphite2 PROPERTIES - COMPILE_FLAGS "-Wall -Wextra -Wno-unknown-pragmas -Wendif-labels -Wshadow -Wctor-dtor-privacy -Wnon-virtual-dtor -fno-rtti -fno-exceptions -fvisibility=hidden -fvisibility-inlines-hidden -fno-stack-protector" + COMPILE_FLAGS "-Wall -Wextra -Wno-unknown-pragmas -Wendif-labels -Wshadow -Wctor-dtor-privacy -Wnon-virtual-dtor -fno-rtti -fno-exceptions -fvisibility=hidden -fvisibility-inlines-hidden -fno-stack-protector -Wdouble-promotion" LINK_FLAGS "-nodefaultlibs ${GRAPHITE_LINK_FLAGS}" LINKER_LANGUAGE C) if (${CMAKE_CXX_COMPILER} MATCHES ".*mingw.*") diff --git a/gfx/graphite2/src/Code.cpp b/gfx/graphite2/src/Code.cpp index 762b9f4ca1d..ed32b724c2e 100644 --- a/gfx/graphite2/src/Code.cpp +++ b/gfx/graphite2/src/Code.cpp @@ -77,6 +77,7 @@ struct context } // end namespace +byte * Machine::Code::local_memory = 0; class Machine::Code::decoder { @@ -94,7 +95,7 @@ public: }; - decoder(const limits & lims, Code &code) throw(); + decoder(limits & lims, Code &code, enum passtype pt) throw(); bool load(const byte * bc_begin, const byte * bc_end); void apply_analysis(instr * const code, instr * code_end); @@ -107,6 +108,7 @@ private: bool emit_opcode(opcode opc, const byte * & bc); bool validate_opcode(const opcode opc, const byte * const bc); bool valid_upto(const uint16 limit, const uint16 x) const throw(); + bool test_context() const throw(); void failure(const status_t s) const throw() { _code.failure(s); } Code & _code; @@ -114,14 +116,16 @@ private: uint16 _rule_length; instr * _instr; byte * _data; - const limits & _max; + limits & _max; analysis _analysis; + enum passtype _passtype; + int _stack_depth; }; struct Machine::Code::decoder::limits { - const byte * const bytecode; + const byte * bytecode; const uint8 pre_context; const uint16 rule_length, classes, @@ -130,19 +134,21 @@ struct Machine::Code::decoder::limits const byte attrid[gr_slatMax]; }; -inline Machine::Code::decoder::decoder(const limits & lims, Code &code) throw() +inline Machine::Code::decoder::decoder(limits & lims, Code &code, enum passtype pt) throw() : _code(code), _pre_context(code._constraint ? 0 : lims.pre_context), _rule_length(code._constraint ? 1 : lims.rule_length), - _instr(code._code), _data(code._data), _max(lims) + _instr(code._code), _data(code._data), _max(lims), _passtype(pt), + _stack_depth(0) { } Machine::Code::Code(bool is_constraint, const byte * bytecode_begin, const byte * const bytecode_end, - uint8 pre_context, uint16 rule_length, const Silf & silf, const Face & face) + uint8 pre_context, uint16 rule_length, const Silf & silf, const Face & face, + enum passtype pt, byte * & _out) : _code(0), _data(0), _data_size(0), _instr_count(0), _max_ref(0), _status(loaded), - _constraint(is_constraint), _modify(false), _delete(false), _own(true) + _constraint(is_constraint), _modify(false), _delete(false), _own(_out==0) { #ifdef GRAPHITE2_TELEMETRY telemetry::category _code_cat(face.tele.code); @@ -150,7 +156,7 @@ Machine::Code::Code(bool is_constraint, const byte * bytecode_begin, const byte assert(bytecode_begin != 0); if (bytecode_begin == bytecode_end) { - ::new (this) Code(); + // ::new (this) Code(); return; } assert(bytecode_end > bytecode_begin); @@ -158,17 +164,17 @@ Machine::Code::Code(bool is_constraint, const byte * bytecode_begin, const byte // Allocate code and dat target buffers, these sizes are a worst case // estimate. Once we know their real sizes the we'll shrink them. - _code = static_cast(malloc((bytecode_end - bytecode_begin) - * sizeof(instr))); - _data = static_cast(malloc((bytecode_end - bytecode_begin) - * sizeof(byte))); + if (_out) _code = reinterpret_cast(_out); + else _code = static_cast(malloc((bytecode_end - bytecode_begin) + * (sizeof(instr)+sizeof(byte)))); + _data = reinterpret_cast(_code + (bytecode_end - bytecode_begin)); if (!_code || !_data) { failure(alloc_failed); return; } - const decoder::limits lims = { + decoder::limits lims = { bytecode_end, pre_context, rule_length, @@ -184,7 +190,7 @@ Machine::Code::Code(bool is_constraint, const byte * bytecode_begin, const byte 0,0,0,0,0,0,0, silf.numUser()} }; - decoder dec(lims, *this); + decoder dec(lims, *this, pt); if(!dec.load(bytecode_begin, bytecode_end)) return; @@ -211,8 +217,13 @@ Machine::Code::Code(bool is_constraint, const byte * bytecode_begin, const byte // memory. assert((bytecode_end - bytecode_begin) >= std::ptrdiff_t(_instr_count)); assert((bytecode_end - bytecode_begin) >= std::ptrdiff_t(_data_size)); - _code = static_cast(realloc(_code, (_instr_count+1)*sizeof(instr))); - _data = static_cast(realloc(_data, _data_size*sizeof(byte))); + memmove(_code + (_instr_count+1), _data, _data_size*sizeof(byte)); + size_t const total_sz = ((_instr_count+1) + (_data_size + sizeof(instr)-1)/sizeof(instr))*sizeof(instr); + if (_out) + _out += total_sz; + else + _code = static_cast(realloc(_code, total_sz)); + _data = reinterpret_cast(_code + (_instr_count+1)); if (!_code) { @@ -237,6 +248,7 @@ Machine::Code::~Code() throw () bool Machine::Code::decoder::load(const byte * bc, const byte * bc_end) { + _max.bytecode = bc_end; while (bc < bc_end) { const opcode opc = fetch_opcode(bc++); @@ -266,55 +278,82 @@ opcode Machine::Code::decoder::fetch_opcode(const byte * bc) switch (opc) { case NOP : + break; case PUSH_BYTE : case PUSH_BYTEU : case PUSH_SHORT : case PUSH_SHORTU : case PUSH_LONG : + ++_stack_depth; + break; case ADD : case SUB : case MUL : case DIV : case MIN_ : case MAX_ : - case NEG : - case TRUNC8 : - case TRUNC16 : - case COND : case AND : case OR : - case NOT : case EQUAL : case NOT_EQ : case LESS : case GTR : case LESS_EQ : case GTR_EQ : + case BITOR : + case BITAND : + if (--_stack_depth <= 0) + failure(underfull_stack); + break; + case NEG : + case TRUNC8 : + case TRUNC16 : + case NOT : + case BITNOT : + case BITSET : + if (_stack_depth <= 0) + failure(underfull_stack); + break; + case COND : + _stack_depth -= 2; + if (_stack_depth <= 0) + failure(underfull_stack); break; case NEXT : case NEXT_N : // runtime checked case COPY_NEXT : + test_context(); ++_pre_context; break; case PUT_GLYPH_8BIT_OBS : valid_upto(_max.classes, bc[0]); + test_context(); break; case PUT_SUBS_8BIT_OBS : valid_upto(_rule_length, _pre_context + int8(bc[0])); valid_upto(_max.classes, bc[1]); valid_upto(_max.classes, bc[2]); + test_context(); break; case PUT_COPY : valid_upto(_rule_length, _pre_context + int8(bc[0])); + test_context(); break; case INSERT : - --_pre_context; + if (_passtype >= PASS_TYPE_POSITIONING) + failure(invalid_opcode); + else + --_pre_context; break; case DELETE : + if (_passtype >= PASS_TYPE_POSITIONING) + failure(invalid_opcode); + test_context(); break; case ASSOC : for (uint8 num = bc[0]; num; --num) valid_upto(_rule_length, _pre_context + int8(bc[num])); + test_context(); break; case CNTXT_ITEM : valid_upto(_max.rule_length, _max.pre_context + int8(bc[0])); @@ -325,39 +364,52 @@ opcode Machine::Code::decoder::fetch_opcode(const byte * bc) case ATTR_ADD : case ATTR_SUB : case ATTR_SET_SLOT : + if (--_stack_depth < 0) + failure(underfull_stack); valid_upto(gr_slatMax, bc[0]); + test_context(); break; case IATTR_SET_SLOT : + if (--_stack_depth < 0) + failure(underfull_stack); if (valid_upto(gr_slatMax, bc[0])) valid_upto(_max.attrid[bc[0]], bc[1]); + test_context(); break; case PUSH_SLOT_ATTR : + ++_stack_depth; valid_upto(gr_slatMax, bc[0]); valid_upto(_rule_length, _pre_context + int8(bc[1])); break; case PUSH_GLYPH_ATTR_OBS : + ++_stack_depth; valid_upto(_max.glyf_attrs, bc[0]); valid_upto(_rule_length, _pre_context + int8(bc[1])); break; case PUSH_GLYPH_METRIC : + ++_stack_depth; valid_upto(kgmetDescent, bc[0]); valid_upto(_rule_length, _pre_context + int8(bc[1])); // level: dp[2] no check necessary break; case PUSH_FEAT : + ++_stack_depth; valid_upto(_max.features, bc[0]); valid_upto(_rule_length, _pre_context + int8(bc[1])); break; case PUSH_ATT_TO_GATTR_OBS : + ++_stack_depth; valid_upto(_max.glyf_attrs, bc[0]); valid_upto(_rule_length, _pre_context + int8(bc[1])); break; case PUSH_ATT_TO_GLYPH_METRIC : + ++_stack_depth; valid_upto(kgmetDescent, bc[0]); valid_upto(_rule_length, _pre_context + int8(bc[1])); // level: dp[2] no check necessary break; case PUSH_ISLOT_ATTR : + ++_stack_depth; if (valid_upto(gr_slatMax, bc[0])) { valid_upto(_rule_length, _pre_context + int8(bc[1])); @@ -365,32 +417,42 @@ opcode Machine::Code::decoder::fetch_opcode(const byte * bc) } break; case PUSH_IGLYPH_ATTR :// not implemented + ++_stack_depth; case POP_RET : + if (--_stack_depth < 0) + failure(underfull_stack); case RET_ZERO : case RET_TRUE : break; case IATTR_SET : case IATTR_ADD : case IATTR_SUB : + if (--_stack_depth < 0) + failure(underfull_stack); if (valid_upto(gr_slatMax, bc[0])) valid_upto(_max.attrid[bc[0]], bc[1]); + test_context(); break; case PUSH_PROC_STATE : // dummy: dp[0] no check necessary case PUSH_VERSION : + ++_stack_depth; break; case PUT_SUBS : valid_upto(_rule_length, _pre_context + int8(bc[0])); valid_upto(_max.classes, uint16(bc[1]<< 8) | bc[2]); valid_upto(_max.classes, uint16(bc[3]<< 8) | bc[4]); + test_context(); break; case PUT_SUBS2 : // not implemented case PUT_SUBS3 : // not implemented break; case PUT_GLYPH : valid_upto(_max.classes, uint16(bc[0]<< 8) | bc[1]); + test_context(); break; case PUSH_GLYPH_ATTR : case PUSH_ATT_TO_GLYPH_ATTR : + ++_stack_depth; valid_upto(_max.glyf_attrs, uint16(bc[0]<< 8) | bc[1]); valid_upto(_rule_length, _pre_context + int8(bc[2])); break; @@ -506,16 +568,20 @@ bool Machine::Code::decoder::emit_opcode(opcode opc, const byte * & bc) byte & instr_skip = _data[-1]; byte & data_skip = *_data++; ++_code._data_size; + const byte *curr_end = _max.bytecode; if (load(bc, bc + instr_skip)) { bc += instr_skip; data_skip = instr_skip - (_code._instr_count - ctxt_start); instr_skip = _code._instr_count - ctxt_start; + _max.bytecode = curr_end; _rule_length = 1; _pre_context = 0; } + else + return false; } return bool(_code); @@ -554,7 +620,7 @@ bool Machine::Code::decoder::validate_opcode(const opcode opc, const byte * cons } const opcode_t & op = Machine::getOpcodeTable()[opc]; const size_t param_sz = op.param_sz == VARARGS ? bc[0] + 1 : op.param_sz; - if (bc + param_sz > _max.bytecode) + if (bc - 1 + param_sz > _max.bytecode) { failure(arguments_exhausted); return false; @@ -570,6 +636,15 @@ bool Machine::Code::decoder::valid_upto(const uint16 limit, const uint16 x) cons return t; } +bool Machine::Code::decoder::test_context() const throw() +{ + if (_pre_context >= _rule_length) + { + failure(out_of_range_data); + return false; + } + return true; +} inline void Machine::Code::failure(const status_t s) throw() { @@ -594,8 +669,8 @@ void Machine::Code::decoder::analysis::set_changed(const int index) throw() { void Machine::Code::release_buffers() throw() { - free(_code); - free(_data); + if (_own) + free(_code); _code = 0; _data = 0; _own = false; @@ -604,13 +679,12 @@ void Machine::Code::release_buffers() throw() int32 Machine::Code::run(Machine & m, slotref * & map) const { - assert(_own); +// assert(_own); assert(*this); // Check we are actually runnable if (m.slotMap().size() <= size_t(_max_ref + m.slotMap().context())) { m._status = Machine::slot_offset_out_bounds; -// return (m.slotMap().end() - map); return 1; } diff --git a/gfx/graphite2/src/Collider.cpp b/gfx/graphite2/src/Collider.cpp new file mode 100644 index 00000000000..b17b9b42f0a --- /dev/null +++ b/gfx/graphite2/src/Collider.cpp @@ -0,0 +1,1080 @@ +/* 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 +#include +#include +#include +#include +#include "inc/Collider.h" +#include "inc/Segment.h" +#include "inc/Slot.h" +#include "inc/GlyphCache.h" +#include "inc/Sparse.h" + +#define ISQRT2 0.707106781f + +// Possible rounding error for subbox boundaries: 0.016 = 1/64 = 1/256 * 4 +// (values in font range from 0..256) +#define SUBBOX_RND_ERR 0.016 + +using namespace graphite2; + +//// SHIFT-COLLIDER //// + +// Initialize the Collider to hold the basic movement limits for the +// target slot, the one we are focusing on fixing. +bool ShiftCollider::initSlot(Segment *seg, Slot *aSlot, const Rect &limit, float margin, float marginWeight, + const Position &currShift, const Position &currOffset, int dir, GR_MAYBE_UNUSED json * const dbgout) +{ + int i; + float max, min; + float a, shift; + const GlyphCache &gc = seg->getFace()->glyphs(); + unsigned short gid = aSlot->gid(); + if (!gc.check(gid)) + return false; + const BBox &bb = gc.getBoundingBBox(gid); + const SlantBox &sb = gc.getBoundingSlantBox(gid); + //float sx = aSlot->origin().x + currShift.x; + //float sy = aSlot->origin().y + currShift.y; + if (currOffset.x != 0.f || currOffset.y != 0.f) + _limit = Rect(limit.bl - currOffset, limit.tr - currOffset); + else + _limit = limit; + // For a ShiftCollider, these indices indicate which vector we are moving by: + // each _ranges represents absolute space with respect to the origin of the slot. Thus take into account true origins but subtract the vmin for the slot + for (i = 0; i < 4; ++i) + { + switch (i) { + case 0 : // x direction + min = _limit.bl.x + currOffset.x; + max = _limit.tr.x + currOffset.x; + _len[i] = bb.xa - bb.xi; + a = currOffset.y + currShift.y; + _ranges[i].initialise(min, max, margin, marginWeight, a); + break; + case 1 : // y direction + min = _limit.bl.y + currOffset.y; + max = _limit.tr.y + currOffset.y; + _len[i] = bb.ya - bb.yi; + a = currOffset.x + currShift.x; + _ranges[i].initialise(min, max, margin, marginWeight, a); + break; + case 2 : // sum (negatively sloped diagonal boundaries) + // pick closest x,y limit boundaries in s direction + shift = currOffset.x + currOffset.y + currShift.x + currShift.y; + min = -2 * std::min(currShift.x - _limit.bl.x, currShift.y - _limit.bl.y) + shift; + max = 2 * std::min(_limit.tr.x - currShift.x, _limit.tr.y - currShift.y) + shift; + _len[i] = sb.sa - sb.si; + a = currOffset.x - currOffset.y + currShift.x - currShift.y; + _ranges[i].initialise(min, max, margin / ISQRT2, marginWeight, a); + break; + case 3 : // diff (positively sloped diagonal boundaries) + // pick closest x,y limit boundaries in d direction + shift = currOffset.x - currOffset.y + currShift.x - currShift.y; + min = -2 * std::min(currShift.x - _limit.bl.x, _limit.tr.y - currShift.y) + shift; + max = 2 * std::min(_limit.tr.x - currShift.x, currShift.y - _limit.bl.y) + shift; + _len[i] = sb.da - sb.di; + a = currOffset.x + currOffset.y + currShift.x + currShift.y; + _ranges[i].initialise(min, max, margin / ISQRT2, marginWeight, a); + break; + } + } + + _target = aSlot; + if ((dir & 1) == 0) + { + // For LTR, switch and negate x limits. + _limit.bl.x = -1 * limit.tr.x; + //_limit.tr.x = -1 * limit.bl.x; + } + _currOffset = currOffset; + _currShift = currShift; + _origin = aSlot->origin() - currOffset; // the original anchor position of the glyph + + _margin = margin; + _marginWt = marginWeight; + + SlotCollision *c = seg->collisionInfo(aSlot); + _seqClass = c->seqClass(); + _seqProxClass = c->seqProxClass(); + _seqOrder = c->seqOrder(); + return true; +} + +template +float sdm(float vi, float va, float mx, float my, O op) +{ + float res = 2 * mx - vi; + if (op(res, vi + 2 * my)) + { + res = va + 2 * my; + if (op(res, 2 * mx - va)) + res = mx + my; + } + return res; +} + +// Mark an area with a cost that can vary along the x or y axis. The region is expressed in terms of the centre of the target glyph in each axis +void ShiftCollider::addBox_slope(bool isx, const Rect &box, const BBox &bb, const SlantBox &sb, const Position &org, float weight, float m, bool minright, int axis) +{ + float a, c; + switch (axis) { + case 0 : + if (box.bl.y < org.y + bb.ya && box.tr.y > org.y + bb.yi && box.width() > 0) + { + a = org.y + 0.5f * (bb.yi + bb.ya); + c = 0.5f * (bb.xi + bb.xa); + if (isx) + _ranges[axis].weighted(box.bl.x - c, box.tr.x - c, weight, a, m, + (minright ? box.tr.x : box.bl.x) - c, a, 0, false); + else + _ranges[axis].weighted(box.bl.x - c, box.tr.x - c, weight, a, 0, 0, org.y, + m * (a * a + sqr((minright ? box.tr.y : box.bl.y) - 0.5f * (bb.yi + bb.ya))), false); + } + break; + case 1 : + if (box.bl.x < org.x + bb.xa && box.tr.x > org.x + bb.xi && box.height() > 0) + { + a = org.x + 0.5f * (bb.xi + bb.xa); + c = 0.5f * (bb.yi + bb.ya); + if (isx) + _ranges[axis].weighted(box.bl.y - c, box.tr.y - c, weight, a, 0, 0, org.x, + m * (a * a + sqr((minright ? box.tr.x : box.bl.x) - 0.5f * (bb.xi + bb.xa))), false); + else + _ranges[axis].weighted(box.bl.y - c, box.tr.y - c, weight, a, m, + (minright ? box.tr.y : box.bl.y) - c, a, 0, false); + } + break; + case 2 : + if (box.bl.x - box.tr.y < org.x - org.y + sb.da && box.tr.x - box.bl.y > org.x - org.y + sb.di) + { + float d = org.x - org.y + 0.5f * (sb.di + sb.da); + c = 0.5f * (sb.si + sb.sa); + float smax = std::min(2 * box.tr.x - d, 2 * box.tr.y + d); + float smin = std::max(2 * box.bl.x - d, 2 * box.bl.y + d); + if (smin > smax) return; + float si; + a = d; + if (isx) + si = 2 * (minright ? box.tr.x : box.bl.x) - a; + else + si = 2 * (minright ? box.tr.y : box.bl.y) + a; + _ranges[axis].weighted(smin - c, smax - c, weight / 2, a, m / 2, si, 0, 0, isx); + } + break; + case 3 : + if (box.bl.x + box.bl.y < org.x + org.y + sb.sa && box.tr.x + box.tr.y > org.x + org.y + sb.si) + { + float s = org.x + org.y + 0.5f * (sb.si + sb.sa); + c = 0.5f * (sb.di + sb.da); + float dmax = std::min(2 * box.tr.x - s, s - 2 * box.bl.y); + float dmin = std::max(2 * box.bl.x - s, s - 2 * box.tr.y); + if (dmin > dmax) return; + float di; + a = s; + if (isx) + di = 2 * (minright ? box.tr.x : box.bl.x) - a; + else + di = 2 * (minright ? box.tr.y : box.bl.y) + a; + _ranges[axis].weighted(dmin - c, dmax - c, weight / 2, a, m / 2, di, 0, 0, !isx); + } + break; + default : + break; + } + return; +} + +// Mark an area with an absolute cost, making it completely inaccessible. +inline void ShiftCollider::removeBox(const Rect &box, const BBox &bb, const SlantBox &sb, const Position &org, int axis) +{ + float c; + switch (axis) { + case 0 : + if (box.bl.y < org.y + bb.ya && box.tr.y > org.y + bb.yi && box.width() > 0) + { + c = 0.5f * (bb.xi + bb.xa); + _ranges[axis].exclude(box.bl.x - c, box.tr.x - c); + } + break; + case 1 : + if (box.bl.x < org.x + bb.xa && box.tr.x > org.x + bb.xi && box.height() > 0) + { + c = 0.5f * (bb.yi + bb.ya); + _ranges[axis].exclude(box.bl.y - c, box.tr.y - c); + } + break; + case 2 : + if (box.bl.x - box.tr.y < org.x - org.y + sb.da && box.tr.x - box.bl.y > org.x - org.y + sb.di + && box.width() > 0 && box.height() > 0) + { + float di = org.x - org.y + sb.di; + float da = org.x - org.y + sb.da; + float smax = sdm(di, da, box.tr.x, box.tr.y, std::greater()); + float smin = sdm(da, di, box.bl.x, box.bl.y, std::less()); + c = 0.5f * (sb.si + sb.sa); + _ranges[axis].exclude(smin - c, smax - c); + } + break; + case 3 : + if (box.bl.x + box.bl.y < org.x + org.y + sb.sa && box.tr.x + box.tr.y > org.x + org.y + sb.si + && box.width() > 0 && box.height() > 0) + { + float si = org.x + org.y + sb.si; + float sa = org.x + org.y + sb.sa; + float dmax = sdm(si, sa, box.tr.x, -box.bl.y, std::greater()); + float dmin = sdm(sa, si, box.bl.x, -box.tr.y, std::less()); + c = 0.5f * (sb.di + sb.da); + _ranges[axis].exclude(dmin - c, dmax - c); + } + break; + default : + break; + } + return; +} + +// Adjust the movement limits for the target to avoid having it collide +// with the given neighbor slot. Also determine if there is in fact a collision +// between the target and the given slot. +bool ShiftCollider::mergeSlot(Segment *seg, Slot *slot, const Position &currShift, + bool isAfter, // slot is logically after _target + bool sameCluster, bool &hasCol, bool isExclusion, + GR_MAYBE_UNUSED json * const dbgout ) +{ + bool isCol = false; + const float tx = _currOffset.x + _currShift.x; + const float ty = _currOffset.y + _currShift.y; + const float td = tx - ty; + const float ts = tx + ty; + const float sx = slot->origin().x - _origin.x + currShift.x; + const float sy = slot->origin().y - _origin.y + currShift.y; + const float sd = sx - sy; + const float ss = sx + sy; + float vmin, vmax; + float omin, omax, otmin, otmax; + float cmin, cmax; // target limits + float torg; + const GlyphCache &gc = seg->getFace()->glyphs(); + const unsigned short gid = slot->gid(); + if (!gc.check(gid)) + return false; + const unsigned short tgid = _target->gid(); + const BBox &bb = gc.getBoundingBBox(gid); + const SlantBox &sb = gc.getBoundingSlantBox(gid); + const BBox &tbb = gc.getBoundingBBox(tgid); + const SlantBox &tsb = gc.getBoundingSlantBox(tgid); + + SlotCollision * cslot = seg->collisionInfo(slot); + int orderFlags = 0; + bool sameClass = _seqProxClass == 0 && cslot->seqClass() == _seqClass; + if (sameCluster && _seqClass + && (sameClass || (_seqProxClass != 0 && cslot->seqClass() == _seqProxClass))) + // Force the target glyph to be in the specified direction from the slot we're testing. + orderFlags = _seqOrder; + float seq_above_wt = cslot->seqAboveWt(); + float seq_below_wt = cslot->seqBelowWt(); + float seq_valign_wt = cslot->seqValignWt(); + + // if isAfter, invert orderFlags for diagonal orders. + if (isAfter) + { + // invert appropriate bits + orderFlags ^= (sameClass ? 0x3F : 0x3); + // consider 2 bits at a time, non overlapping. If both bits set, clear them + orderFlags = orderFlags ^ ((((orderFlags >> 1) & orderFlags) & 0x15) * 3); + } + +#if !defined GRAPHITE2_NTRACING + if (dbgout) + dbgout->setenv(0, slot); +#endif + + // Process main bounding octabox. + for (int i = 0; i < 4; ++i) + { + switch (i) { + case 0 : // x direction + vmin = std::max(std::max(bb.xi - tbb.xa + sx, sb.di - tsb.da + ty + sd), sb.si - tsb.sa - ty + ss); + vmax = std::min(std::min(bb.xa - tbb.xi + sx, sb.da - tsb.di + ty + sd), sb.sa - tsb.si - ty + ss); + otmin = tbb.yi + ty; + otmax = tbb.ya + ty; + omin = bb.yi + sy; + omax = bb.ya + sy; + torg = _currOffset.x; + cmin = _limit.bl.x + torg; + cmax = _limit.tr.x - tbb.xi + tbb.xa + torg; + break; + case 1 : // y direction + vmin = std::max(std::max(bb.yi - tbb.ya + sy, tsb.di - sb.da + tx - sd), sb.si - tsb.sa - tx + ss); + vmax = std::min(std::min(bb.ya - tbb.yi + sy, tsb.da - sb.di + tx - sd), sb.sa - tsb.si - tx + ss); + otmin = tbb.xi + tx; + otmax = tbb.xa + tx; + omin = bb.xi + sx; + omax = bb.xa + sx; + torg = _currOffset.y; + cmin = _limit.bl.y + torg; + cmax = _limit.tr.y - tbb.yi + tbb.ya + torg; + break; + case 2 : // sum - moving along the positively-sloped vector, so the boundaries are the + // negatively-sloped boundaries. + vmin = std::max(std::max(sb.si - tsb.sa + ss, 2 * (bb.yi - tbb.ya + sy) + td), 2 * (bb.xi - tbb.xa + sx) - td); + vmax = std::min(std::min(sb.sa - tsb.si + ss, 2 * (bb.ya - tbb.yi + sy) + td), 2 * (bb.xa - tbb.xi + sx) - td); + otmin = tsb.di + td; + otmax = tsb.da + td; + omin = sb.di + sd; + omax = sb.da + sd; + torg = _currOffset.x + _currOffset.y; + cmin = _limit.bl.x + _limit.bl.y + torg; + cmax = _limit.tr.x + _limit.tr.y - tsb.si + tsb.sa + torg; + break; + case 3 : // diff - moving along the negatively-sloped vector, so the boundaries are the + // positively-sloped boundaries. + vmin = std::max(std::max(sb.di - tsb.da + sd, 2 * (bb.xi - tbb.xa + sx) - ts), -2 * (bb.ya - tbb.yi + sy) + ts); + vmax = std::min(std::min(sb.da - tsb.di + sd, 2 * (bb.xa - tbb.xi + sx) - ts), -2 * (bb.yi - tbb.ya + sy) + ts); + otmin = tsb.si + ts; + otmax = tsb.sa + ts; + omin = sb.si + ss; + omax = sb.sa + ss; + torg = _currOffset.x - _currOffset.y; + cmin = _limit.bl.x - _limit.tr.y + torg; + cmax = _limit.tr.x - _limit.bl.y - tsb.di + tsb.da + torg; + break; + default : + continue; + } + +#if !defined GRAPHITE2_NTRACING + if (dbgout) + dbgout->setenv(1, reinterpret_cast(-1)); +#define DBGTAG(x) if (dbgout) dbgout->setenv(1, reinterpret_cast(-x)); +#else +#define DBGTAG(x) +#endif + + if (orderFlags) + { + Position org(tx, ty); + float xminf = _limit.bl.x + _currOffset.x + tbb.xi; + float xpinf = _limit.tr.x + _currOffset.x + tbb.xa; + float ypinf = _limit.tr.y + _currOffset.y + tbb.ya; + float yminf = _limit.bl.y + _currOffset.y + tbb.yi; + switch (orderFlags) { + case SlotCollision::SEQ_ORDER_RIGHTUP : + { + float r1Xedge = cslot->seqAboveXoff() + 0.5f * (bb.xi + bb.xa) + sx; + float r3Xedge = cslot->seqBelowXlim() + bb.xa + sx + 0.5f * (tbb.xa - tbb.xi); + float r2Yedge = 0.5f * (bb.yi + bb.ya) + sy; + + // DBGTAG(1x) means the regions are up and right + // region 1 + DBGTAG(11) + addBox_slope(true, Rect(Position(xminf, r2Yedge), Position(r1Xedge, ypinf)), + tbb, tsb, org, 0, seq_above_wt, true, i); + // region 2 + DBGTAG(12) + removeBox(Rect(Position(xminf, yminf), Position(r3Xedge, r2Yedge)), tbb, tsb, org, i); + // region 3, which end is zero is irrelevant since m weight is 0 + DBGTAG(13) + addBox_slope(true, Rect(Position(r3Xedge, yminf), Position(xpinf, r2Yedge - cslot->seqValignHt())), + tbb, tsb, org, seq_below_wt, 0, true, i); + // region 4 + DBGTAG(14) + addBox_slope(false, Rect(Position(sx + bb.xi, r2Yedge), Position(xpinf, r2Yedge + cslot->seqValignHt())), + tbb, tsb, org, 0, seq_valign_wt, true, i); + // region 5 + DBGTAG(15) + addBox_slope(false, Rect(Position(sx + bb.xi, r2Yedge - cslot->seqValignHt()), Position(xpinf, r2Yedge)), + tbb, tsb, org, seq_below_wt, seq_valign_wt, false, i); + break; + } + case SlotCollision::SEQ_ORDER_LEFTDOWN : + { + float r1Xedge = 0.5f * (bb.xi + bb.xa) + cslot->seqAboveXoff() + sx; + float r3Xedge = bb.xi - cslot->seqBelowXlim() + sx - 0.5f * (tbb.xa - tbb.xi); + float r2Yedge = 0.5f * (bb.yi + bb.ya) + sy; + // DBGTAG(2x) means the regions are up and right + // region 1 + DBGTAG(21) + addBox_slope(true, Rect(Position(r1Xedge, yminf), Position(xpinf, r2Yedge)), + tbb, tsb, org, 0, seq_above_wt, false, i); + // region 2 + DBGTAG(22) + removeBox(Rect(Position(r3Xedge, r2Yedge), Position(xpinf, ypinf)), tbb, tsb, org, i); + // region 3 + DBGTAG(23) + addBox_slope(true, Rect(Position(xminf, r2Yedge - cslot->seqValignHt()), Position(r3Xedge, ypinf)), + tbb, tsb, org, seq_below_wt, 0, false, i); + // region 4 + DBGTAG(24) + addBox_slope(false, Rect(Position(xminf, r2Yedge), Position(sx + bb.xa, r2Yedge + cslot->seqValignHt())), + tbb, tsb, org, 0, seq_valign_wt, true, i); + // region 5 + DBGTAG(25) + addBox_slope(false, Rect(Position(xminf, r2Yedge - cslot->seqValignHt()), + Position(sx + bb.xa, r2Yedge)), tbb, tsb, org, seq_below_wt, seq_valign_wt, false, i); + break; + } + case SlotCollision::SEQ_ORDER_NOABOVE : // enforce neighboring glyph being above + DBGTAG(31); + removeBox(Rect(Position(bb.xi - tbb.xa + sx, sy + bb.ya), + Position(bb.xa - tbb.xi + sx, ypinf)), tbb, tsb, org, i); + break; + case SlotCollision::SEQ_ORDER_NOBELOW : // enforce neighboring glyph being below + DBGTAG(32); + removeBox(Rect(Position(bb.xi - tbb.xa + sx, yminf), + Position(bb.xa - tbb.xi + sx, sy + bb.yi)), tbb, tsb, org, i); + break; + case SlotCollision::SEQ_ORDER_NOLEFT : // enforce neighboring glyph being to the left + DBGTAG(33) + removeBox(Rect(Position(xminf, bb.yi - tbb.ya + sy), + Position(bb.xi - tbb.xa + sx, bb.ya - tbb.yi + sy)), tbb, tsb, org, i); + break; + case SlotCollision::SEQ_ORDER_NORIGHT : // enforce neighboring glyph being to the right + DBGTAG(34) + removeBox(Rect(Position(bb.xa - tbb.xi + sx, bb.yi - tbb.ya + sy), + Position(xpinf, bb.ya - tbb.yi + sy)), tbb, tsb, org, i); + break; + default : + break; + } + } + + if (vmax < cmin - _margin || vmin > cmax + _margin || omax < otmin - _margin || omin > otmax + _margin) + continue; + + // Process sub-boxes that are defined for this glyph. + // We only need to do this if there was in fact a collision with the main octabox. + uint8 numsub = gc.numSubBounds(gid); + if (numsub > 0) + { + bool anyhits = false; + for (int j = 0; j < numsub; ++j) + { + const BBox &sbb = gc.getSubBoundingBBox(gid, j); + const SlantBox &ssb = gc.getSubBoundingSlantBox(gid, j); + switch (i) { + case 0 : // x + vmin = std::max(std::max(sbb.xi-tbb.xa+sx, ssb.di-tsb.da+sd+ty), ssb.si-tsb.sa+ss-ty); + vmax = std::min(std::min(sbb.xa-tbb.xi+sx, ssb.da-tsb.di+sd+ty), ssb.sa-tsb.si+ss-ty); + omin = sbb.yi + sy; + omax = sbb.ya + sy; + break; + case 1 : // y + vmin = std::max(std::max(sbb.yi-tbb.ya+sy, tsb.di-ssb.da-sd+tx), ssb.si-tsb.sa+ss-tx); + vmax = std::min(std::min(sbb.ya-tbb.yi+sy, tsb.da-ssb.di-sd+tx), ssb.sa-tsb.si+ss-tx); + omin = sbb.xi + sx; + omax = sbb.xa + sx; + break; + case 2 : // sum + vmin = std::max(std::max(ssb.si-tsb.sa+ss, 2*(sbb.yi-tbb.ya+sy)+td), 2*(sbb.xi-tbb.xa+sx)-td); + vmax = std::min(std::min(ssb.sa-tsb.si+ss, 2*(sbb.ya-tbb.yi+sy)+td), 2*(sbb.xa-tbb.xi+sx)-td); + omin = ssb.di + sd; + omax = ssb.da + sd; + break; + case 3 : // diff + vmin = std::max(std::max(ssb.di-tsb.da+sd, 2*(sbb.xi-tbb.xa+sx)-ts), -2*(sbb.ya-tbb.yi+sy)+ts); + vmax = std::min(std::min(ssb.da-tsb.di+sd, 2*(sbb.xa-tbb.xi+sx)-ts), -2*(sbb.yi-tbb.ya+sy)+ts); + omin = ssb.si + ss; + omax = ssb.sa + ss; + break; + } + if (vmax < cmin - _margin || vmin > cmax + _margin || omax < otmin - _margin || omin > otmax + _margin) + continue; + +#if !defined GRAPHITE2_NTRACING + if (dbgout) + dbgout->setenv(1, reinterpret_cast(j)); +#endif + if (omin > otmax) + _ranges[i].weightedAxis(i, vmin - _margin, vmax + _margin, 0, 0, 0, 0, 0, + sqr(_margin - omin + otmax) * _marginWt, false); + else if (omax < otmin) + _ranges[i].weightedAxis(i, vmin - _margin, vmax + _margin, 0, 0, 0, 0, 0, + sqr(_margin - otmin + omax) * _marginWt, false); + else + _ranges[i].exclude_with_margins(vmin, vmax, i); + anyhits = true; + } + if (anyhits) + isCol = true; + } + else // no sub-boxes + { +#if !defined GRAPHITE2_NTRACING + if (dbgout) + dbgout->setenv(1, reinterpret_cast(-1)); +#endif + isCol = true; + if (omin > otmax) + _ranges[i].weightedAxis(i, vmin - _margin, vmax + _margin, 0, 0, 0, 0, 0, + sqr(_margin - omin + otmax) * _marginWt, false); + else if (omax < otmin) + _ranges[i].weightedAxis(i, vmin - _margin, vmax + _margin, 0, 0, 0, 0, 0, + sqr(_margin - otmin + omax) * _marginWt, false); + else + _ranges[i].exclude_with_margins(vmin, vmax, i); + + } + } + bool res = true; + if (cslot && cslot->exclGlyph() > 0 && gc.check(cslot->exclGlyph()) && !isExclusion) + { + // Set up the bogus slot representing the exclusion glyph. + Slot *exclSlot = seg->newSlot(); + exclSlot->setGlyph(seg, cslot->exclGlyph()); + Position exclOrigin(slot->origin() + cslot->exclOffset()); + exclSlot->origin(exclOrigin); + res &= mergeSlot(seg, exclSlot, currShift, isAfter, sameCluster, isCol, true, dbgout ); + seg->freeSlot(exclSlot); + } + hasCol |= isCol; + return res; + +} // end of ShiftCollider::mergeSlot + + +// Figure out where to move the target glyph to, and return the amount to shift by. +Position ShiftCollider::resolve(GR_MAYBE_UNUSED Segment *seg, bool &isCol, GR_MAYBE_UNUSED json * const dbgout) +{ + float tbase; + float totalCost = (float)(std::numeric_limits::max() / 2); + Position resultPos = Position(0, 0); +#if !defined GRAPHITE2_NTRACING + int bestAxis = -1; + if (dbgout) + { + outputJsonDbgStartSlot(dbgout, seg); + *dbgout << "vectors" << json::array; + } +#endif + isCol = true; + for (int i = 0; i < 4; ++i) + { + float bestCost = -1; + float bestPos; + // Calculate the margin depending on whether we are moving diagonally or not: + switch (i) { + case 0 : // x direction + tbase = _currOffset.x; + break; + case 1 : // y direction + tbase = _currOffset.y; + break; + case 2 : // sum (negatively-sloped diagonals) + tbase = _currOffset.x + _currOffset.y; + break; + case 3 : // diff (positively-sloped diagonals) + tbase = _currOffset.x - _currOffset.y; + break; + } + Position testp; + bestPos = _ranges[i].closest(0, bestCost) - tbase; // Get the best relative position +#if !defined GRAPHITE2_NTRACING + if (dbgout) + outputJsonDbgOneVector(dbgout, seg, i, tbase, bestCost, bestPos) ; +#endif + if (bestCost >= 0.0f) + { + isCol = false; + switch (i) { + case 0 : testp = Position(bestPos, _currShift.y); break; + case 1 : testp = Position(_currShift.x, bestPos); break; + case 2 : testp = Position(0.5f * (_currShift.x - _currShift.y + bestPos), 0.5f * (_currShift.y - _currShift.x + bestPos)); break; + case 3 : testp = Position(0.5f * (_currShift.x + _currShift.y + bestPos), 0.5f * (_currShift.x + _currShift.y - bestPos)); break; + } + if (bestCost < totalCost - 0.01f) + { + totalCost = bestCost; + resultPos = testp; +#if !defined GRAPHITE2_NTRACING + bestAxis = i; +#endif + } + } + } // end of loop over 4 directions + +#if !defined GRAPHITE2_NTRACING + if (dbgout) + outputJsonDbgEndSlot(dbgout, resultPos, bestAxis, isCol); +#endif + + return resultPos; + +} // end of ShiftCollider::resolve + + +#if !defined GRAPHITE2_NTRACING + +void ShiftCollider::outputJsonDbg(json * const dbgout, Segment *seg, int axis) +{ + int axisMax = axis; + if (axis < 0) // output all axes + { + *dbgout << "gid" << _target->gid() + << "limit" << _limit + << "target" << json::object + << "origin" << _target->origin() + << "margin" << _margin + << "bbox" << seg->theGlyphBBoxTemporary(_target->gid()) + << "slantbox" << seg->getFace()->glyphs().slant(_target->gid()) + << json::close; // target object + *dbgout << "ranges" << json::array; + axis = 0; + axisMax = 3; + } + for (int iAxis = axis; iAxis <= axisMax; ++iAxis) + { + *dbgout << json::flat << json::array << _ranges[iAxis].position(); + for (Zones::const_iterator s = _ranges[iAxis].begin(), e = _ranges[iAxis].end(); s != e; ++s) + *dbgout << json::flat << json::array + << Position(s->x, s->xm) << s->sm << s->smx << s->c + << json::close; + *dbgout << json::close; + } + if (axis < axisMax) // looped through the _ranges array for all axes + *dbgout << json::close; // ranges array +} + +void ShiftCollider::outputJsonDbgStartSlot(json * const dbgout, Segment *seg) +{ + *dbgout << json::object // slot - not closed till the end of the caller method + << "slot" << objectid(dslot(seg, _target)) + << "gid" << _target->gid() + << "limit" << _limit + << "target" << json::object + << "origin" << _origin + << "currShift" << _currShift + << "currOffset" << seg->collisionInfo(_target)->offset() + << "bbox" << seg->theGlyphBBoxTemporary(_target->gid()) + << "slantBox" << seg->getFace()->glyphs().slant(_target->gid()) + << "fix" << "shift"; + *dbgout << json::close; // target object +} + +void ShiftCollider::outputJsonDbgEndSlot(GR_MAYBE_UNUSED json * const dbgout, + Position resultPos, int bestAxis, bool isCol) +{ + *dbgout << json::close // vectors array + << "result" << resultPos + //<< "scraping" << _scraping[bestAxis] + << "bestAxis" << bestAxis + << "stillBad" << isCol + << json::close; // slot object +} + +void ShiftCollider::outputJsonDbgOneVector(json * const dbgout, Segment *seg, int axis, + float tleft, float bestCost, float bestVal) +{ + const char * label; + switch (axis) + { + case 0: label = "x"; break; + case 1: label = "y"; break; + case 2: label = "sum (NE-SW)"; break; + case 3: label = "diff (NW-SE)"; break; + default: label = "???"; break; + } + + *dbgout << json::object // vector + << "direction" << label + << "targetMin" << tleft; + + outputJsonDbgRemovals(dbgout, axis, seg); + + *dbgout << "ranges"; + outputJsonDbg(dbgout, seg, axis); + + *dbgout << "bestCost" << bestCost + << "bestVal" << bestVal + tleft + << json::close; // vectors object +} + +void ShiftCollider::outputJsonDbgRemovals(json * const dbgout, int axis, Segment *seg) +{ + *dbgout << "removals" << json::array; + _ranges[axis].jsonDbgOut(seg); + *dbgout << json::close; // removals array +} + +#endif // !defined GRAPHITE2_NTRACING + + +//// KERN-COLLIDER //// + +inline +static float localmax (float al, float au, float bl, float bu, float x) +{ + if (al < bl) + { if (au < bu) return au < x ? au : x; } + else if (au > bu) return bl < x ? bl : x; + return x; +} + +inline +static float localmin(float al, float au, float bl, float bu, float x) +{ + if (bl > al) + { if (bu > au) return bl > x ? bl : x; } + else if (au > bu) return al > x ? al : x; + return x; +} + +// Return the given edge of the glyph at height y, taking any slant box into account. +static float get_edge(Segment *seg, const Slot *s, const Position &shift, float y, float width, bool isRight) +{ + const GlyphCache &gc = seg->getFace()->glyphs(); + unsigned short gid = s->gid(); + float sx = s->origin().x + shift.x; + float sy = s->origin().y + shift.y; + uint8 numsub = gc.numSubBounds(gid); + float res = isRight ? (float)-1e38 : (float)1e38; + + if (numsub > 0) + { + for (int i = 0; i < numsub; ++i) + { + const BBox &sbb = gc.getSubBoundingBBox(gid, i); + const SlantBox &ssb = gc.getSubBoundingSlantBox(gid, i); + if (sy + sbb.yi > y + width / 2 || sy + sbb.ya < y - width / 2) + continue; + if (isRight) + { + float x = sx + sbb.xa; + if (x > res) + { + float td = sx - sy + ssb.da + y; + float ts = sx + sy + ssb.sa - y; + x = localmax(td - width / 2, td + width / 2, ts - width / 2, ts + width / 2, x); + if (x > res) + res = x; + } + } + else + { + float x = sx + sbb.xi; + if (x < res) + { + float td = sx - sy + ssb.di + y; + float ts = sx + sy + ssb.si - y; + x = localmin(td - width / 2, td + width / 2, ts - width / 2, ts + width / 2, x); + if (x < res) + res = x; + } + } + } + } + else + { + const BBox &bb = gc.getBoundingBBox(gid); + const SlantBox &sb = gc.getBoundingSlantBox(gid); + float td = sx - sy + y; + float ts = sx + sy - y; + if (isRight) + res = localmax(td + sb.da - width / 2, td + sb.da + width / 2, ts + sb.sa - width / 2, ts + sb.sa + width / 2, sx + bb.xa); + else + res = localmin(td + sb.di - width / 2, td + sb.di + width / 2, ts + sb.si - width / 2, ts + sb.si + width / 2, sx + bb.xi); + } + return res; +} + + +bool KernCollider::initSlot(Segment *seg, Slot *aSlot, const Rect &limit, float margin, + const Position &currShift, const Position &offsetPrev, int dir, + float ymin, float ymax, GR_MAYBE_UNUSED json * const dbgout) +{ + const GlyphCache &gc = seg->getFace()->glyphs(); + const Slot *base = aSlot; + // const Slot *last = aSlot; + const Slot *s; + int numSlices; + while (base->attachedTo()) + base = base->attachedTo(); + if (margin < 10) margin = 10; + + _limit = limit; + _offsetPrev = offsetPrev; // kern from a previous pass + + // Calculate the height of the glyph and how many horizontal slices to use. + if (_maxy >= 1e37f) + { + _maxy = ymax; + _miny = ymin; + _sliceWidth = margin / 1.5f; + numSlices = int((_maxy - _miny + 2) / (_sliceWidth / 1.5f) + 1.f); // +2 helps with rounding errors + _edges.clear(); + _edges.insert(_edges.begin(), numSlices, (dir & 1) ? 1e38f : -1e38f); + _xbound = (dir & 1) ? (float)1e38f : (float)-1e38f; + } + else if (_maxy != ymax || _miny != ymin) + { + if (_miny != ymin) + { + numSlices = int((ymin - _miny) / _sliceWidth - 1); + _miny += numSlices * _sliceWidth; + if (numSlices < 0) + _edges.insert(_edges.begin(), -numSlices, (dir & 1) ? 1e38f : -1e38f); + else if ((unsigned)numSlices < _edges.size()) // this shouldn't fire since we always grow the range + { + Vector::iterator e = _edges.begin(); + while (numSlices--) + ++e; + _edges.erase(_edges.begin(), e); + } + } + if (_maxy != ymax) + { + numSlices = int((ymax - _miny) / _sliceWidth + 1); + _maxy = numSlices * _sliceWidth + _miny; + if (numSlices > (int)_edges.size()) + _edges.insert(_edges.end(), numSlices - _edges.size(), (dir & 1) ? 1e38f : -1e38f); + else if (numSlices < (int)_edges.size()) // this shouldn't fire since we always grow the range + { + while ((int)_edges.size() > numSlices) + _edges.pop_back(); + } + } + } + numSlices = _edges.size(); + +#if !defined GRAPHITE2_NTRACING + // Debugging + _seg = seg; + _slotNear.clear(); + _slotNear.insert(_slotNear.begin(), numSlices, NULL); + _nearEdges.clear(); + _nearEdges.insert(_nearEdges.begin(), numSlices, (dir & 1) ? -1e38f : +1e38f); +#endif + + // Determine the trailing edge of each slice (ie, left edge for a RTL glyph). + for (s = base; s; s = s->nextInCluster(s)) + { + SlotCollision *c = seg->collisionInfo(s); + if (!gc.check(s->gid())) + return false; + const BBox &bs = gc.getBoundingBBox(s->gid()); + float x = s->origin().x + c->shift().x + ((dir & 1) ? bs.xi : bs.xa); + // Loop over slices. + // Note smin might not be zero if glyph s is not at the bottom of the cluster; similarly for smax. + float toffset = c->shift().y - _miny + 1 + s->origin().y; + int smin = std::max(0, int((bs.yi + toffset) / _sliceWidth)); + int smax = std::min(numSlices - 1, int((bs.ya + toffset) / _sliceWidth + 1)); + for (int i = smin; i <= smax; ++i) + { + float t; + float y = _miny - 1 + (i + .5f) * _sliceWidth; // vertical center of slice + if ((dir & 1) && x < _edges[i]) + { + t = get_edge(seg, s, currShift, y, _sliceWidth, false); + if (t < _edges[i]) + { + _edges[i] = t; + if (t < _xbound) + _xbound = t; + } + } + else if (!(dir & 1) && x > _edges[i]) + { + t = get_edge(seg, s, currShift, y, _sliceWidth, true); + if (t > _edges[i]) + { + _edges[i] = t; + if (t > _xbound) + _xbound = t; + } + } + } + } + _mingap = (float)1e38; + _target = aSlot; + _margin = margin; + _currShift = currShift; + return true; +} // end of KernCollider::initSlot + + +// Determine how much the target slot needs to kern away from the given slot. +// In other words, merge information from given slot's position with what the target slot knows +// about how it can kern. +// Return false if we know there is no collision, true if we think there might be one. +bool KernCollider::mergeSlot(Segment *seg, Slot *slot, const Position &currShift, float currSpace, int dir, GR_MAYBE_UNUSED json * const dbgout) +{ + int rtl = (dir & 1) * 2 - 1; + const Rect &bb = seg->theGlyphBBoxTemporary(slot->gid()); + const float sx = slot->origin().x + currShift.x; + float x = sx + (rtl > 0 ? bb.tr.x : bb.bl.x); + // this isn't going to reduce _mingap so skip + if ((rtl > 0 && x < _xbound - _mingap - currSpace) || (rtl <= 0 && x > _xbound + _mingap + currSpace)) + return false; + + const float sy = slot->origin().y + currShift.y; + int smin = std::max(0, int((bb.bl.y + (1 - _miny + sy)) / _sliceWidth + 1)); + int smax = std::min((int)_edges.size() - 1, int((bb.tr.y + (1 - _miny + sy)) / _sliceWidth + 1)); + bool collides = false; + + for (int i = smin; i <= smax; ++i) + { + float t; + float y = (float)(_miny - 1 + (i + .5f) * _sliceWidth); // vertical center of slice + if (x * rtl > _edges[i] * rtl - _mingap - currSpace) + { + // 2 * currSpace to account for the space that is already separating them and the space we want to add + float m = get_edge(seg, slot, currShift, y, _sliceWidth, rtl > 0) + 2 * rtl * currSpace; + t = rtl * (_edges[i] - m); + // Check slices above and below (if any). + if (i < (int)_edges.size() - 1) t = std::min(t, rtl * (_edges[i+1] - m)); + if (i > 0) t = std::min(t, rtl * (_edges[i-1] - m)); + // _mingap is positive to shrink + if (t < _mingap) + { + _mingap = t; + collides = true; + } +#if !defined GRAPHITE2_NTRACING + // Debugging - remember the closest neighboring edge for this slice. + if (rtl * m > rtl * _nearEdges[i]) + { + _slotNear[i] = slot; + _nearEdges[i] = m; + } +#endif + } + } + return collides; // note that true is not a necessarily reliable value + +} // end of KernCollider::mergeSlot + + +// Return the amount to kern by. +// TODO: do we need to make use of marginMin here? Probably not. +Position KernCollider::resolve(GR_MAYBE_UNUSED Segment *seg, GR_MAYBE_UNUSED Slot *slot, + int dir, float margin, GR_MAYBE_UNUSED json * const dbgout) +{ + float resultNeeded = (1 - 2 * (dir & 1)) * (_mingap - margin); + float result = min(_limit.tr.x - _offsetPrev.x, max(resultNeeded, _limit.bl.x - _offsetPrev.x)); + +#if !defined GRAPHITE2_NTRACING + if (dbgout) + { + *dbgout << json::object // slot + << "slot" << objectid(dslot(seg, _target)) + << "gid" << _target->gid() + << "margin" << _margin + << "limit" << _limit + << "target" << json::object + << "origin" << _target->origin() + //<< "currShift" << _currShift + << "offsetPrev" << _offsetPrev + << "bbox" << seg->theGlyphBBoxTemporary(_target->gid()) + << "slantBox" << seg->getFace()->glyphs().slant(_target->gid()) + << "fix" << "kern" + << "slices" << _edges.size() + << "sliceWidth" << _sliceWidth + << json::close; // target object + + *dbgout << "slices" << json::array; + for (int is = 0; is < (int)_edges.size(); is++) + { + *dbgout << json::flat << json::object + << "i" << is + << "targetEdge" << _edges[is] + << "neighbor" << objectid(dslot(seg, _slotNear[is])) + << "nearEdge" << _nearEdges[is] + << json::close; + } + *dbgout << json::close; // slices array + + *dbgout + << "xbound" << _xbound + << "minGap" << _mingap + << "needed" << resultNeeded + << "result" << result + << "stillBad" << (result != resultNeeded) + << json::close; // slot object + } +#endif + + return Position(result, 0.); + +} // end of KernCollider::resolve + +void KernCollider::shift(const Position &mv, int dir) +{ + for (Vector::iterator e = _edges.begin(); e != _edges.end(); ++e) + *e += mv.x; + _xbound += (1 - 2 * (dir & 1)) * mv.x; +} + +//// SLOT-COLLISION //// + +// Initialize the collision attributes for the given slot. +SlotCollision::SlotCollision(Segment *seg, Slot *slot) +{ + initFromSlot(seg, slot); +} + +void SlotCollision::initFromSlot(Segment *seg, Slot *slot) +{ + // Initialize slot attributes from glyph attributes. + // The order here must match the order in the grcompiler code, + // GrcSymbolTable::AssignInternalGlyphAttrIDs. + uint16 gid = slot->gid(); + uint16 aCol = seg->silf()->aCollision(); // flags attr ID + const GlyphFace * glyphFace = seg->getFace()->glyphs().glyphSafe(gid); + if (!glyphFace) + return; + const sparse &p = glyphFace->attrs(); + _flags = p[aCol]; + _limit = Rect(Position(p[aCol+1], p[aCol+2]), + Position(p[aCol+3], p[aCol+4])); + _margin = p[aCol+5]; + _marginWt = p[aCol+6]; + + _seqClass = p[aCol+7]; + _seqProxClass = p[aCol+8]; + _seqOrder = p[aCol+9]; + _seqAboveXoff = p[aCol+10]; + _seqAboveWt = p[aCol+11]; + _seqBelowXlim = p[aCol+12]; + _seqBelowWt = p[aCol+13]; + _seqValignHt = p[aCol+14]; + _seqValignWt = p[aCol+15]; + + // These attributes do not have corresponding glyph attribute: + _exclGlyph = 0; + _exclOffset = Position(0, 0); +} + +float SlotCollision::getKern(int dir) const +{ + if ((_flags & SlotCollision::COLL_KERN) != 0) + return float(_shift.x * ((dir & 1) ? -1 : 1)); + else + return 0; +} + diff --git a/gfx/graphite2/src/Decompressor.cpp b/gfx/graphite2/src/Decompressor.cpp new file mode 100644 index 00000000000..919678a640b --- /dev/null +++ b/gfx/graphite2/src/Decompressor.cpp @@ -0,0 +1,102 @@ +/* Copyright (c) 2012, Siyuan Fu + Copyright (c) 2015, SIL International + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + 3. Neither the name of the copyright holder nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + POSSIBILITY OF SUCH DAMAGE. +*/ +#include + +#include "inc/Decompressor.h" +#include "inc/Shrinker.h" + +using namespace shrinker; + +namespace { + +u8 const LONG_DIST = 0x10; +u8 const MATCH_LEN = 0x0f; + +template +inline +u32 read_literal(u8 const * &s, u8 const * const e, u32 l) { + if (unlikely(l == M)) + { + u8 b = 0; + do { l += b = *s++; } while(b==0xff && s != e); + } + return l; +} + +bool read_directive(u8 const * &src, u8 const * const end, u32 & literal_len, u32 & match_len, u32 & match_dist) +{ + u8 const flag = *src++; + + literal_len = read_literal<7>(src, end, flag >> 5); + match_len = read_literal<15>(src, end, flag & MATCH_LEN); + + match_dist = *src++; + if (flag & LONG_DIST) + match_dist |= ((*src++) << 8); + + return match_dist != 0xffff; +} + +} + +int shrinker::decompress(void const *in, size_t in_size, void *out, size_t out_size) +{ + u8 const * src = static_cast(in), + * const src_end = src + in_size; + + u8 * dst = static_cast(out), + * const dst_end = dst + out_size; + + u32 literal_len = 0, + match_len = 0, + match_dist = 0; + + while (read_directive(src, src_end, literal_len, match_len, match_dist)) + { + // Copy in literal + if (unlikely(dst + literal_len + sizeof(unsigned long) > dst_end)) return -1; + dst = memcpy_nooverlap(dst, src, literal_len); + src += literal_len; + + // Copy, possibly repeating, match from earlier in the + // decoded output. + u8 const * const pcpy = dst - match_dist - 1; + if (unlikely(pcpy < static_cast(out) + || dst + match_len + MINMATCH + sizeof(unsigned long) > dst_end)) return -1; + dst = memcpy_(dst, pcpy, match_len + MINMATCH); + } + + if (unlikely(dst + literal_len > dst_end)) return -1; + dst = memcpy_nooverlap_surpass(dst, src, literal_len); + + return dst - (u8*)out; +} + diff --git a/gfx/graphite2/src/Face.cpp b/gfx/graphite2/src/Face.cpp index e25f7260ac6..25e1b713095 100644 --- a/gfx/graphite2/src/Face.cpp +++ b/gfx/graphite2/src/Face.cpp @@ -28,6 +28,7 @@ of the License or (at your option) any later version. #include "graphite2/Segment.h" #include "inc/CmapCache.h" #include "inc/debug.h" +#include "inc/Decompressor.h" #include "inc/Endian.h" #include "inc/Face.h" #include "inc/FileFace.h" @@ -40,6 +41,16 @@ of the License or (at your option) any later version. using namespace graphite2; +namespace +{ +enum compression +{ + NONE, + SHRINKER +}; + +} + Face::Face(const void* appFaceHandle/*non-NULL*/, const gr_face_ops & ops) : m_appFaceHandle(appFaceHandle), m_pFileFace(NULL), @@ -84,19 +95,21 @@ bool Face::readGlyphs(uint32 faceOptions) telemetry::category _glyph_cat(tele.glyph); #endif error_context(EC_READGLYPHS); + m_pGlyphFaceCache = new GlyphCache(*this, faceOptions); + + if (e.test(!m_pGlyphFaceCache, E_OUTOFMEM) + || e.test(m_pGlyphFaceCache->numGlyphs() == 0, E_NOGLYPHS) + || e.test(m_pGlyphFaceCache->unitsPerEm() == 0, E_BADUPEM)) + { + return error(e); + } + if (faceOptions & gr_face_cacheCmap) m_cmap = new CachedCmap(*this); else m_cmap = new DirectCmap(*this); - - m_pGlyphFaceCache = new GlyphCache(*this, faceOptions); - if (e.test(!m_pGlyphFaceCache, E_OUTOFMEM) - || e.test(m_pGlyphFaceCache->numGlyphs() == 0, E_NOGLYPHS) - || e.test(m_pGlyphFaceCache->unitsPerEm() == 0, E_BADUPEM) - || e.test(!m_cmap, E_OUTOFMEM) || e.test(!*m_cmap, E_BADCMAP)) - { + if (e.test(!m_cmap, E_OUTOFMEM) || e.test(!*m_cmap, E_BADCMAP)) return error(e); - } if (faceOptions & gr_face_preloadGlyphs) nameTable(); // preload the name table along with the glyphs. @@ -119,10 +132,12 @@ bool Face::readGraphite(const Table & silf) if (version >= 0x00030000) be::skip(p); // compilerVersion m_numSilf = be::read(p); + be::skip(p); // reserved bool havePasses = false; m_silfs = new Silf[m_numSilf]; + if (e.test(!m_silfs, E_OUTOFMEM)) return error(e); for (int i = 0; i < m_numSilf; i++) { error_context(EC_ASILF + (i << 8)); @@ -158,9 +173,14 @@ bool Face::runGraphite(Segment *seg, const Silf *aSilf) const } #endif - bool res = aSilf->runGraphite(seg, 0, aSilf->justificationPass(), true); + bool res = aSilf->runGraphite(seg, 0, aSilf->positionPass(), true); if (res) - res = aSilf->runGraphite(seg, aSilf->positionPass(), aSilf->numPasses(), false); + { + seg->associateChars(0, seg->charInfoCount()); + if (aSilf->flags() & 0x20) + res &= seg->initCollisions(); + res &= aSilf->runGraphite(seg, aSilf->positionPass(), aSilf->numPasses(), false); + } #if !defined GRAPHITE2_NTRACING if (dbgout) @@ -245,17 +265,24 @@ uint16 Face::languageForLocale(const char * locale) const return 0; } -Face::Table::Table(const Face & face, const Tag n) throw() -: _f(&face) + + +Face::Table::Table(const Face & face, const Tag n, uint32 version) throw() +: _f(&face), _compressed(false) { size_t sz = 0; - _p = reinterpret_cast((*_f->m_ops.get_table)(_f->m_appFaceHandle, n, &sz)); + _p = static_cast((*_f->m_ops.get_table)(_f->m_appFaceHandle, n, &sz)); _sz = uint32(sz); + if (!TtfUtil::CheckTable(n, _p, _sz)) { this->~Table(); // Make sure we release the table buffer even if the table filed it's checks _p = 0; _sz = 0; + return; } + + if (be::peek(_p) >= version) + decompress(); } Face::Table & Face::Table::operator = (const Table & rhs) throw() @@ -267,3 +294,50 @@ Face::Table & Face::Table::operator = (const Table & rhs) throw() return *this; } +Error Face::Table::decompress() +{ + Error e; + byte * uncompressed_table = 0; + size_t uncompressed_size = 0; + + const byte * p = _p; + const uint32 version = be::read(p); // Table version number. + + // The scheme is in the top 5 bits of the 1st uint32. + const uint32 hdr = be::read(p); + switch(compression(hdr >> 27)) + { + case NONE: return e; + case SHRINKER: + { + uncompressed_size = hdr & 0x07ffffff; + uncompressed_table = gralloc(uncompressed_size); + if (!e.test(!uncompressed_table, E_OUTOFMEM)) + e.test(shrinker::decompress(p, _sz - 2*sizeof(uint32), uncompressed_table, uncompressed_size) != signed(uncompressed_size), E_SHRINKERFAILED); + break; + } + default: + e.error(E_BADSCHEME); + }; + + // Check the uncompressed version number against the original. + if (!e) + e.test(be::peek(uncompressed_table) != version, E_SHRINKERFAILED); + + // Tell the provider to release the compressed form since were replacing + // it anyway. + this->~Table(); + + if (e) + { + free(uncompressed_table); + uncompressed_table = 0; + uncompressed_size = 0; + } + + _p = uncompressed_table; + _sz = uncompressed_size + sizeof(uint32); + _compressed = true; + + return e; +} diff --git a/gfx/graphite2/src/FeatureMap.cpp b/gfx/graphite2/src/FeatureMap.cpp index 7cc3c7299bd..199ff700cf4 100644 --- a/gfx/graphite2/src/FeatureMap.cpp +++ b/gfx/graphite2/src/FeatureMap.cpp @@ -165,16 +165,16 @@ bool FeatureMap::readFeats(const Face & face) label, uiName, flags, uiSet, num_settings); } - m_defaultFeatures = new Features(bits/(sizeof(uint32)*8) + 1, *this); + new (&m_defaultFeatures) Features(bits/(sizeof(uint32)*8) + 1, *this); m_pNamedFeats = new NameAndFeatureRef[m_numFeats]; - if (!m_defaultFeatures || !m_pNamedFeats) + if (!m_pNamedFeats) { free(defVals); return false; } for (int i = 0; i < m_numFeats; ++i) { - m_feats[i].applyValToFeature(defVals[i], *m_defaultFeatures); + m_feats[i].applyValToFeature(defVals[i], m_defaultFeatures); m_pNamedFeats[i] = m_feats+i; } @@ -214,7 +214,7 @@ bool SillMap::readSill(const Face & face) uint16 numSettings = be::read(p); uint16 offset = be::read(p); if (offset + 8U * numSettings > sill.size() && numSettings > 0) return false; - Features* feats = new Features(*m_FeatureMap.m_defaultFeatures); + Features* feats = new Features(m_FeatureMap.m_defaultFeatures); if (!feats) return false; const byte *pLSet = sill + offset; @@ -250,7 +250,7 @@ Features* SillMap::cloneFeatures(uint32 langname/*0 means default*/) const return new Features(*m_langFeats[i].m_pFeatures); } } - return new Features (*m_FeatureMap.m_defaultFeatures); + return new Features (m_FeatureMap.m_defaultFeatures); } diff --git a/gfx/graphite2/src/GlyphCache.cpp b/gfx/graphite2/src/GlyphCache.cpp index aee03055e17..59f418a2d80 100644 --- a/gfx/graphite2/src/GlyphCache.cpp +++ b/gfx/graphite2/src/GlyphCache.cpp @@ -31,6 +31,7 @@ of the License or (at your option) any later version. #include "inc/GlyphCache.h" #include "inc/GlyphFace.h" #include "inc/Endian.h" +#include "inc/bits.h" using namespace graphite2; @@ -70,13 +71,14 @@ namespace protected: const byte * _e, * _v; - ptrdiff_t _n; + size_t _n; }; typedef _glat_iterator glat_iterator; typedef _glat_iterator glat2_iterator; } +const Rect GlyphCache::nullRect = Rect(); class GlyphCache::Loader { @@ -87,8 +89,10 @@ public: unsigned short int units_per_em() const throw(); unsigned short int num_glyphs() const throw(); unsigned short int num_attrs() const throw(); + bool has_boxes() const throw(); - const GlyphFace * read_glyph(unsigned short gid, GlyphFace &) const throw(); + const GlyphFace * read_glyph(unsigned short gid, GlyphFace &, int *numsubs) const throw(); + GlyphBox * read_box(uint16 gid, GlyphBox *curr, const GlyphFace & face) const throw(); CLASS_NEW_DELETE; private: @@ -101,6 +105,7 @@ private: m_pGloc; bool _long_fmt; + bool _has_boxes; unsigned short _num_glyphs_graphics, //i.e. boundary box and advance _num_glyphs_attributes, _num_attrs; // number of glyph attributes per glyph @@ -111,31 +116,49 @@ private: GlyphCache::GlyphCache(const Face & face, const uint32 face_options) : _glyph_loader(new Loader(face, bool(face_options & gr_face_dumbRendering))), _glyphs(_glyph_loader && *_glyph_loader ? grzeroalloc(_glyph_loader->num_glyphs()) : 0), + _boxes(_glyph_loader && _glyph_loader->has_boxes() ? grzeroalloc(_glyph_loader->num_glyphs()) : 0), _num_glyphs(_glyphs ? _glyph_loader->num_glyphs() : 0), _num_attrs(_glyphs ? _glyph_loader->num_attrs() : 0), _upem(_glyphs ? _glyph_loader->units_per_em() : 0) { if ((face_options & gr_face_preloadGlyphs) && _glyph_loader && _glyphs) { + int numsubs = 0; GlyphFace * const glyphs = new GlyphFace [_num_glyphs]; if (!glyphs) return; // The 0 glyph is definately required. - _glyphs[0] = _glyph_loader->read_glyph(0, glyphs[0]); + _glyphs[0] = _glyph_loader->read_glyph(0, glyphs[0], &numsubs); // glyphs[0] has the same address as the glyphs array just allocated, // thus assigning the &glyphs[0] to _glyphs[0] means _glyphs[0] points // to the entire array. const GlyphFace * loaded = _glyphs[0]; for (uint16 gid = 1; loaded && gid != _num_glyphs; ++gid) - _glyphs[gid] = loaded = _glyph_loader->read_glyph(gid, glyphs[gid]); + _glyphs[gid] = loaded = _glyph_loader->read_glyph(gid, glyphs[gid], &numsubs); if (!loaded) { _glyphs[0] = 0; delete [] glyphs; } + else if (numsubs > 0) + { + GlyphBox * boxes = (GlyphBox *)gralloc(_num_glyphs * sizeof(GlyphBox) + (numsubs-1) * 8 * sizeof(float)); + GlyphBox * currbox = boxes; + + for (uint16 gid = 0; currbox && gid != _num_glyphs; ++gid) + { + _boxes[gid] = currbox; + currbox = _glyph_loader->read_box(gid, currbox, *_glyphs[gid]); + } + if (!currbox) + { + free(boxes); + _boxes[0] = 0; + } + } delete _glyph_loader; _glyph_loader = 0; } @@ -144,6 +167,11 @@ GlyphCache::GlyphCache(const Face & face, const uint32 face_options) { free(_glyphs); _glyphs = 0; + if (_boxes) + { + free(_boxes); + _boxes = 0; + } _num_glyphs = _num_attrs = _upem = 0; } } @@ -163,6 +191,18 @@ GlyphCache::~GlyphCache() delete [] _glyphs[0]; free(_glyphs); } + if (_boxes) + { + if (_glyph_loader) + { + GlyphBox * * g = _boxes; + for (uint16 n = _num_glyphs; n; --n, ++g) + free(*g); + } + else + free(_boxes[0]); + free(_boxes); + } delete _glyph_loader; } @@ -171,13 +211,23 @@ const GlyphFace *GlyphCache::glyph(unsigned short glyphid) const //result m const GlyphFace * & p = _glyphs[glyphid]; if (p == 0 && _glyph_loader) { + int numsubs = 0; GlyphFace * g = new GlyphFace(); - if (g) p = _glyph_loader->read_glyph(glyphid, *g); + if (g) p = _glyph_loader->read_glyph(glyphid, *g, &numsubs); if (!p) { delete g; return *_glyphs; } + if (_boxes) + { + _boxes[glyphid] = (GlyphBox *)gralloc(sizeof(GlyphBox) + 8 * numsubs * sizeof(float)); + if (!_glyph_loader->read_box(glyphid, _boxes[glyphid], *_glyphs[glyphid])) + { + free(_boxes[glyphid]); + _boxes[glyphid] = 0; + } + } } return p; } @@ -191,6 +241,7 @@ GlyphCache::Loader::Loader(const Face & face, const bool dumb_font) _glyf(face, Tag::glyf), _loca(face, Tag::loca), _long_fmt(false), + _has_boxes(false), _num_glyphs_graphics(0), _num_glyphs_attributes(0), _num_attrs(0) @@ -203,7 +254,7 @@ GlyphCache::Loader::Loader(const Face & face, const bool dumb_font) _num_glyphs_graphics = TtfUtil::GlyphCount(maxp); // This will fail if the number of glyphs is wildly out of range. - if (_glyf && TtfUtil::LocaLookup(_num_glyphs_graphics-1, _loca, _loca.size(), _head) == size_t(-1)) + if (_glyf && TtfUtil::LocaLookup(_num_glyphs_graphics-1, _loca, _loca.size(), _head) == size_t(-2)) { _head = Face::Table(); return; @@ -211,7 +262,7 @@ GlyphCache::Loader::Loader(const Face & face, const bool dumb_font) if (!dumb_font) { - if ((m_pGlat = Face::Table(face, Tag::Glat)) == NULL + if ((m_pGlat = Face::Table(face, Tag::Glat, 0x00030000)) == NULL || (m_pGloc = Face::Table(face, Tag::Gloc)) == NULL || m_pGloc.size() < 6) { @@ -219,7 +270,7 @@ GlyphCache::Loader::Loader(const Face & face, const bool dumb_font) return; } const byte * p = m_pGloc; - const int version = be::read(p); + int version = be::read(p); const uint16 flags = be::read(p); _num_attrs = be::read(p); // We can accurately calculate the number of attributed glyphs by @@ -231,13 +282,22 @@ GlyphCache::Loader::Loader(const Face & face, const bool dumb_font) - sizeof(uint16)*(flags & 0x2 ? _num_attrs : 0)) / (_long_fmt ? sizeof(uint32) : sizeof(uint16)) - 1; - if (version != 0x00010000 + if (version >= 0x00020000 || _num_attrs == 0 || _num_attrs > 0x3000 // is this hard limit appropriate? || _num_glyphs_graphics > _num_glyphs_attributes) { _head = Face::Table(); return; } + + p = m_pGlat; + version = be::read(p); + if (version >= 0x00040000) // reject Glat tables that are too new + { + _head = Face::Table(); + return; + } + _has_boxes = (version == 0x00030000); } } @@ -265,7 +325,13 @@ unsigned short int GlyphCache::Loader::num_attrs() const throw() return _num_attrs; } -const GlyphFace * GlyphCache::Loader::read_glyph(unsigned short glyphid, GlyphFace & glyph) const throw() +inline +bool GlyphCache::Loader::has_boxes () const throw() +{ + return _has_boxes; +} + +const GlyphFace * GlyphCache::Loader::read_glyph(unsigned short glyphid, GlyphFace & glyph, int *numsubs) const throw() { Rect bbox; Position advance; @@ -312,30 +378,89 @@ const GlyphFace * GlyphCache::Loader::read_glyph(unsigned short glyphid, GlyphFa return 0; const uint32 glat_version = be::peek(m_pGlat); + if (glat_version == 0x00030000) + { + const byte * p = m_pGlat + glocs; + uint16 bmap = be::read(p); + int num = bit_set_count((uint32)bmap); + if (numsubs) *numsubs += num; + glocs += 6 + 8 * num; + if (glocs > gloce) + return 0; + } if (glat_version < 0x00020000) { if (gloce - glocs < 2*sizeof(byte)+sizeof(uint16) || gloce - glocs > _num_attrs*(2*sizeof(byte)+sizeof(uint16))) - { - return 0; - } - + return 0; new (&glyph) GlyphFace(bbox, advance, glat_iterator(m_pGlat + glocs), glat_iterator(m_pGlat + gloce)); } else { - if (gloce - glocs < 3*sizeof(uint16) + if (gloce - glocs < 3*sizeof(uint16) // can a glyph have no attributes? why not? || gloce - glocs > _num_attrs*3*sizeof(uint16)) - { - return 0; - } - + return 0; new (&glyph) GlyphFace(bbox, advance, glat2_iterator(m_pGlat + glocs), glat2_iterator(m_pGlat + gloce)); } - if (!glyph.attrs() || glyph.attrs().capacity() > _num_attrs) return 0; } - return &glyph; } + +inline float scale_to(uint8 t, float zmin, float zmax) +{ + return (zmin + t * (zmax - zmin) / 255); +} + +Rect readbox(Rect &b, uint8 zxmin, uint8 zymin, uint8 zxmax, uint8 zymax) +{ + return Rect(Position(scale_to(zxmin, b.bl.x, b.tr.x), scale_to(zymin, b.bl.y, b.tr.y)), + Position(scale_to(zxmax, b.bl.x, b.tr.x), scale_to(zymax, b.bl.y, b.tr.y))); +} + +GlyphBox * GlyphCache::Loader::read_box(uint16 gid, GlyphBox *curr, const GlyphFace & glyph) const throw() +{ + if (gid >= _num_glyphs_attributes) return 0; + + const byte * gloc = m_pGloc; + size_t glocs = 0, gloce = 0; + + be::skip(gloc); + be::skip(gloc,2); + if (_long_fmt) + { + be::skip(gloc, gid); + glocs = be::read(gloc); + gloce = be::peek(gloc); + } + else + { + be::skip(gloc, gid); + glocs = be::read(gloc); + gloce = be::peek(gloc); + } + + if (glocs >= m_pGlat.size() || gloce > m_pGlat.size()) + return 0; + + const byte * p = m_pGlat + glocs; + uint16 bmap = be::read(p); + int num = bit_set_count((uint32)bmap); + + Rect bbox = glyph.theBBox(); + Rect diamax(Position(bbox.bl.x + bbox.bl.y, bbox.bl.x - bbox.tr.y), + Position(bbox.tr.x + bbox.tr.y, bbox.tr.x - bbox.bl.y)); + Rect diabound = readbox(diamax, p[0], p[2], p[1], p[3]); + ::new (curr) GlyphBox(num, bmap, &diabound); + be::skip(p, 4); + + for (int i = 0; i < num * 2; ++i) + { + Rect box = readbox((i & 1) ? diamax : bbox, p[0], p[2], p[1], p[3]); + curr->addSubBox(i >> 1, i & 1, &box); + be::skip(p, 4); + } + return (GlyphBox *)((char *)(curr) + sizeof(GlyphBox) + 2 * num * sizeof(Rect)); +} + diff --git a/gfx/graphite2/src/Intervals.cpp b/gfx/graphite2/src/Intervals.cpp new file mode 100644 index 00000000000..3a252c54fed --- /dev/null +++ b/gfx/graphite2/src/Intervals.cpp @@ -0,0 +1,292 @@ +/* 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 +#include +#include + +#include "inc/Intervals.h" +#include "inc/Segment.h" +#include "inc/Slot.h" +#include "inc/debug.h" +#include "inc/bits.h" + +using namespace graphite2; + +#include + +inline +Zones::Exclusion Zones::Exclusion::split_at(float p) { + Exclusion r(*this); + r.xm = x = p; + return r; +} + +inline +void Zones::Exclusion::left_trim(float p) { + x = p; +} + +inline +Zones::Exclusion & Zones::Exclusion::operator += (Exclusion const & rhs) { + c += rhs.c; sm += rhs.sm; smx += rhs.smx; open = false; + return *this; +} + +inline +uint8 Zones::Exclusion::outcode(float val) const { + float p = val; + return ((p >= xm) << 1) | (p < x); +} + +void Zones::exclude_with_margins(float xmin, float xmax, int axis) { + remove(xmin, xmax); + weightedAxis(axis, xmin-_margin_len, xmin, 0, 0, _margin_weight, xmin-_margin_len, 0, 0, false); + weightedAxis(axis, xmax, xmax+_margin_len, 0, 0, _margin_weight, xmax+_margin_len, 0, 0, false); +} + +namespace +{ + +inline +bool separated(float a, float b) { + return a != b; + //return std::fabs(a-b) > std::numeric_limits::epsilon(); // std::epsilon may not work. but 0.5 fails exising 64 bit tests + //return std::fabs(a-b) > 0.5f; +} + +} + +void Zones::insert(Exclusion e) +{ +#if !defined GRAPHITE2_NTRACING + addDebug(&e); +#endif + e.x = std::max(e.x, _pos); + e.xm = std::min(e.xm, _posm); + if (e.x >= e.xm) return; + + for (iterator i = _exclusions.begin(), ie = _exclusions.end(); i != ie && e.x < e.xm; ++i) + { + const uint8 oca = e.outcode(i->x), + ocb = e.outcode(i->xm); + if ((oca & ocb) != 0) continue; + + switch (oca ^ ocb) // What kind of overlap? + { + case 0: // e completely covers i + // split e at i.x into e1,e2 + // split e2 at i.mx into e2,e3 + // drop e1 ,i+e2, e=e3 + *i += e; + e.left_trim(i->xm); + break; + case 1: // e overlaps on the rhs of i + // split i at e->x into i1,i2 + // split e at i.mx into e1,e2 + // trim i1, insert i2+e1, e=e2 + if (!separated(i->xm, e.x)) break; + if (separated(i->x,e.x)) { i = _exclusions.insert(i,i->split_at(e.x)); ++i; } + *i += e; + e.left_trim(i->xm); + break; + case 2: // e overlaps on the lhs of i + // split e at i->x into e1,e2 + // split i at e.mx into i1,i2 + // drop e1, insert e2+i1, trim i2 + if (!separated(e.xm, i->x)) return; + if (separated(e.xm, i->xm)) i = _exclusions.insert(i,i->split_at(e.xm)); + *i += e; + return; + case 3: // i completely covers e + // split i at e.x into i1,i2 + // split i2 at e.mx into i2,i3 + // insert i1, insert e+i2 + if (separated(e.xm, i->xm)) i = _exclusions.insert(i,i->split_at(e.xm)); + i = _exclusions.insert(i, i->split_at(e.x)); + *++i += e; + return; + } + + ie = _exclusions.end(); + } +} + + +void Zones::remove(float x, float xm) +{ +#if !defined GRAPHITE2_NTRACING + removeDebug(x, xm); +#endif + x = std::max(x, _pos); + xm = std::min(xm, _posm); + if (x >= xm) return; + + for (iterator i = _exclusions.begin(), ie = _exclusions.end(); i != ie; ++i) + { + const uint8 oca = i->outcode(x), + ocb = i->outcode(xm); + if ((oca & ocb) != 0) continue; + + switch (oca ^ ocb) // What kind of overlap? + { + case 0: // i completely covers e + if (separated(i->x, x)) { i = _exclusions.insert(i,i->split_at(x)); ++i; } + // no break + case 1: // i overlaps on the rhs of e + i->left_trim(xm); + return; + case 2: // i overlaps on the lhs of e + i->xm = x; + if (separated(i->x, i->xm)) break; + // no break + case 3: // e completely covers i + i = _exclusions.erase(i); + --i; + break; + } + + ie = _exclusions.end(); + } +} + + +Zones::const_iterator Zones::find_exclusion_under(float x) const +{ + int l = 0, h = _exclusions.size(); + + while (l < h) + { + int const p = (l+h) >> 1; + switch (_exclusions[p].outcode(x)) + { + case 0 : return _exclusions.begin()+p; + case 1 : h = p; break; + case 2 : + case 3 : l = p+1; break; + } + } + + return _exclusions.begin()+l; +} + + +float Zones::closest(float origin, float & cost) const +{ + float best_c = std::numeric_limits::max(), + best_x = 0; + + const const_iterator start = find_exclusion_under(origin); + + // Forward scan looking for lowest cost + for (const_iterator i = start, ie = _exclusions.end(); i != ie; ++i) + if (i->track_cost(best_c, best_x, origin)) break; + + // Backward scan looking for lowest cost + // We start from the exclusion to the immediate left of start since we've + // already tested start with the right most scan above. + for (const_iterator i = start-1, ie = _exclusions.begin()-1; i != ie; --i) + if (i->track_cost(best_c, best_x, origin)) break; + + cost = (best_c == std::numeric_limits::max() ? -1 : best_c); + return best_x; +} + + +// Cost and test position functions + +bool Zones::Exclusion::track_cost(float & best_cost, float & best_pos, float origin) const { + const float p = test_position(origin), + localc = cost(p - origin); + if (open && localc > best_cost) return true; + + if (localc < best_cost) + { + best_cost = localc; + best_pos = p; + } + return false; +} + +inline +float Zones::Exclusion::cost(float p) const { + return (sm * p - 2 * smx) * p + c; +} + + +float Zones::Exclusion::test_position(float origin) const { + if (sm < 0) + { + // sigh, test both ends and perhaps the middle too! + float res = x; + float cl = cost(x); + if (x < origin && xm > origin) + { + float co = cost(origin); + if (co < cl) + { + cl = co; + res = origin; + } + } + float cr = cost(xm); + return cl > cr ? xm : res; + } + else + { + float zerox = smx / sm + origin; + if (zerox < x) return x; + else if (zerox > xm) return xm; + else return zerox; + } +} + + +#if !defined GRAPHITE2_NTRACING + +void Zones::jsonDbgOut(Segment *seg) const { + + if (_dbg) + { + for (Zones::idebugs s = dbgs_begin(), e = dbgs_end(); s != e; ++s) + { + *_dbg << json::flat << json::array + << objectid(dslot(seg, (Slot *)(s->_env[0]))) + << reinterpret_cast(s->_env[1]); + if (s->_isdel) + *_dbg << "remove" << Position(s->_excl.x, s->_excl.xm); + else + *_dbg << "exclude" << json::flat << json::array + << s->_excl.x << s->_excl.xm + << s->_excl.sm << s->_excl.smx << s->_excl.c + << json::close; + *_dbg << json::close; + } + } +} + +#endif + diff --git a/gfx/graphite2/src/Justifier.cpp b/gfx/graphite2/src/Justifier.cpp index 5816ee0890f..9f95033255c 100644 --- a/gfx/graphite2/src/Justifier.cpp +++ b/gfx/graphite2/src/Justifier.cpp @@ -60,7 +60,7 @@ void JustifyTotal::accumulate(Slot *s, Segment *seg, int level) m_tWeight += s->getJustify(seg, level, 3); } -float Segment::justify(Slot *pSlot, const Font *font, float width, GR_MAYBE_UNUSED justFlags flags, Slot *pFirst, Slot *pLast) +float Segment::justify(Slot *pSlot, const Font *font, float width, GR_MAYBE_UNUSED justFlags jflags, Slot *pFirst, Slot *pLast) { Slot *s, *end; float currWidth = 0.0; @@ -76,11 +76,11 @@ float Segment::justify(Slot *pSlot, const Font *font, float width, GR_MAYBE_UNUS while (!pLast->isBase()) pLast = pLast->attachedTo(); const float base = pFirst->origin().x / scale; width = width / scale; - if ((flags & gr_justEndInline) == 0) + if ((jflags & gr_justEndInline) == 0) { do { Rect bbox = theGlyphBBoxTemporary(pLast->glyph()); - if (bbox.bl.x != 0. || bbox.bl.y != 0. || bbox.tr.x != 0. || bbox.tr.y == 0.) + if (bbox.bl.x != 0.f || bbox.bl.y != 0.f || bbox.tr.x != 0.f || bbox.tr.y == 0.f) break; pLast = pLast->prev(); } while (pLast != pFirst); @@ -116,8 +116,7 @@ float Segment::justify(Slot *pSlot, const Font *font, float width, GR_MAYBE_UNUS ++numLevels; } - JustifyTotal *stats = new JustifyTotal[numLevels]; - if (!stats) return -1.0; + Vector stats(numLevels); for (s = pFirst; s != end; s = s->nextSibling()) { float w = s->origin().x / scale + s->advance() - base; @@ -127,7 +126,7 @@ float Segment::justify(Slot *pSlot, const Font *font, float width, GR_MAYBE_UNUS s->just(0); } - for (int i = (width < 0.0) ? -1 : numLevels - 1; i >= 0; --i) + for (int i = (width < 0.0f) ? -1 : numLevels - 1; i >= 0; --i) { float diff; float error = 0.; @@ -197,7 +196,7 @@ float Segment::justify(Slot *pSlot, const Font *font, float width, GR_MAYBE_UNUS << "passes" << json::array; #endif - if (m_silf->justificationPass() != m_silf->positionPass() && (width >= 0. || (silf()->flags() & 1))) + if (m_silf->justificationPass() != m_silf->positionPass() && (width >= 0.f || (silf()->flags() & 1))) m_silf->runGraphite(this, m_silf->justificationPass(), m_silf->positionPass()); #if !defined GRAPHITE2_NTRACING diff --git a/gfx/graphite2/src/Pass.cpp b/gfx/graphite2/src/Pass.cpp index 140b45bcca0..fde290b14fd 100644 --- a/gfx/graphite2/src/Pass.cpp +++ b/gfx/graphite2/src/Pass.cpp @@ -31,10 +31,12 @@ of the License or (at your option) any later version. #include #include #include +#include #include "inc/Segment.h" #include "inc/Code.h" #include "inc/Rule.h" #include "inc/Error.h" +#include "inc/Collider.h" using namespace graphite2; using vm::Machine; @@ -49,6 +51,8 @@ Pass::Pass() m_startStates(0), m_transitions(0), m_states(0), + m_codes(0), + m_progs(0), m_flags(0), m_iMaxLoop(0), m_numGlyphs(0), @@ -70,21 +74,27 @@ Pass::~Pass() free(m_states); free(m_ruleMap); - delete [] m_rules; + if (m_rules) delete [] m_rules; + if (m_codes) delete [] m_codes; + free(m_progs); } -bool Pass::readPass(const byte * const pass_start, size_t pass_length, size_t subtable_base, GR_MAYBE_UNUSED Face & face, Error &e) +bool Pass::readPass(const byte * const pass_start, size_t pass_length, size_t subtable_base, + GR_MAYBE_UNUSED Face & face, passtype pt, GR_MAYBE_UNUSED uint32 version, Error &e) { - const byte * p = pass_start, - * const pass_end = p + pass_length; + const byte * p = pass_start, + * const pass_end = p + pass_length; size_t numRanges; if (e.test(pass_length < 40, E_BADPASSLENGTH)) return face.error(e); // Read in basic values m_flags = be::read(p); + if (e.test((m_flags & 15) && pt < PASS_TYPE_POSITIONING, E_BADCOLLISIONPASS)) + return face.error(e); m_iMaxLoop = be::read(p); be::skip(p,2); // skip maxContext & maxBackup m_numRules = be::read(p); + if (e.test(!m_numRules && !(m_flags & 7), E_BADEMPTYPASS)) return face.error(e); be::skip(p); // fsmOffset - not sure why we would want this const byte * const pcCode = pass_start + be::read(p) - subtable_base, * const rcCode = pass_start + be::read(p) - subtable_base, @@ -101,7 +111,7 @@ bool Pass::readPass(const byte * const pass_start, size_t pass_length, size_t su if ( e.test(m_numTransition > m_numStates, E_BADNUMTRANS) || e.test(m_numSuccess > m_numStates, E_BADNUMSUCCESS) || e.test(m_numSuccess + m_numTransition < m_numStates, E_BADNUMSTATES) - || e.test(numRanges == 0, E_NORANGES)) + || e.test(m_numRules && numRanges == 0, E_NORANGES)) return face.error(e); m_successStart = m_numStates - m_numSuccess; @@ -131,23 +141,26 @@ bool Pass::readPass(const byte * const pass_start, size_t pass_length, size_t su be::skip(p, m_numRules); const byte * const precontext = p; be::skip(p, m_numRules); - be::skip(p); // skip reserved byte - if (e.test(p + sizeof(uint16) > pass_end, E_BADCTXTLENS)) return face.error(e); + if (e.test(p + sizeof(uint16) + sizeof(uint8) > pass_end, E_BADCTXTLENS)) return face.error(e); + m_colThreshold = be::read(p); + if (m_colThreshold == 0) m_colThreshold = 10; // A default const size_t pass_constraint_len = be::read(p); + const uint16 * const o_constraint = reinterpret_cast(p); be::skip(p, m_numRules + 1); const uint16 * const o_actions = reinterpret_cast(p); be::skip(p, m_numRules + 1); const byte * const states = p; + if (e.test(p + 2 * m_numTransition*m_numColumns >= pass_end, E_BADPASSLENGTH)) return face.error(e); be::skip(p, m_numTransition*m_numColumns); - be::skip(p); // skip reserved byte - if (e.test(p != pcCode, E_BADPASSCCODEPTR) || e.test(p >= pass_end, E_BADPASSLENGTH)) return face.error(e); + be::skip(p); + if (e.test(p != pcCode, E_BADPASSCCODEPTR)) return face.error(e); be::skip(p, pass_constraint_len); - if (e.test(p != rcCode, E_BADRULECCODEPTR) || e.test(p >= pass_end, E_BADPASSLENGTH) + if (e.test(p != rcCode, E_BADRULECCODEPTR) || e.test(size_t(rcCode - pcCode) != pass_constraint_len, E_BADCCODELEN)) return face.error(e); be::skip(p, be::peek(o_constraint + m_numRules)); - if (e.test(p != aCode, E_BADACTIONCODEPTR) || e.test(p >= pass_end, E_BADPASSLENGTH)) return face.error(e); + if (e.test(p != aCode, E_BADACTIONCODEPTR)) return face.error(e); be::skip(p, be::peek(o_actions + m_numRules)); // We should be at the end or within the pass @@ -158,19 +171,22 @@ bool Pass::readPass(const byte * const pass_start, size_t pass_length, size_t su { face.error_context(face.error_context() + 1); m_cPConstraint = vm::Machine::Code(true, pcCode, pcCode + pass_constraint_len, - precontext[0], be::peek(sort_keys), *m_silf, face); + precontext[0], be::peek(sort_keys), *m_silf, face, PASS_TYPE_UNKNOWN); if (e.test(!m_cPConstraint, E_OUTOFMEM) - || e.test(m_cPConstraint.status(), m_cPConstraint.status() + E_CODEFAILURE)) + || e.test(!m_cPConstraint, m_cPConstraint.status() + E_CODEFAILURE)) return face.error(e); face.error_context(face.error_context() - 1); } - if (!readRanges(ranges, numRanges, e)) return face.error(e); - if (!readRules(rule_map, numEntries, precontext, sort_keys, - o_constraint, rcCode, o_actions, aCode, face, e)) return false; + if (m_numRules) + { + if (!readRanges(ranges, numRanges, e)) return face.error(e); + if (!readRules(rule_map, numEntries, precontext, sort_keys, + o_constraint, rcCode, o_actions, aCode, face, pt, e)) return false; + } #ifdef GRAPHITE2_TELEMETRY telemetry::category _states_cat(face.tele.states); #endif - return readStates(start_states, states, o_rule_map, face, e); + return m_numRules ? readStates(start_states, states, o_rule_map, face, e) : true; } @@ -178,12 +194,11 @@ bool Pass::readRules(const byte * rule_map, const size_t num_entries, const byte *precontext, const uint16 * sort_key, const uint16 * o_constraint, const byte *rc_data, const uint16 * o_action, const byte * ac_data, - Face & face, Error &e) + Face & face, passtype pt, Error &e) { const byte * const ac_data_end = ac_data + be::peek(o_action + m_numRules); const byte * const rc_data_end = rc_data + be::peek(o_constraint + m_numRules); - if (e.test(!(m_rules = new Rule [m_numRules]), E_OUTOFMEM)) return face.error(e); precontext += m_numRules; sort_key += m_numRules; o_constraint += m_numRules; @@ -193,6 +208,15 @@ bool Pass::readRules(const byte * rule_map, const size_t num_entries, const byte * ac_begin = 0, * rc_begin = 0, * ac_end = ac_data + be::peek(o_action), * rc_end = rc_data + be::peek(o_constraint); + + // Allocate pools + m_rules = new Rule [m_numRules]; + m_codes = new Code [m_numRules*2]; + m_progs = static_cast(malloc((ac_end - ac_data + rc_end - rc_data) + *(sizeof(vm::instr)+sizeof(byte)))); + byte * prog_pool_free = m_progs; + if (e.test(!(m_rules && m_codes && m_progs), E_OUTOFMEM)) return face.error(e); + Rule * r = m_rules + m_numRules - 1; for (size_t n = m_numRules; n; --n, --r, ac_end = ac_begin, rc_end = rc_begin) { @@ -211,8 +235,8 @@ bool Pass::readRules(const byte * rule_map, const size_t num_entries, if (ac_begin > ac_end || ac_begin > ac_data_end || ac_end > ac_data_end || rc_begin > rc_end || rc_begin > rc_data_end || rc_end > rc_data_end) return false; - r->action = new vm::Machine::Code(false, ac_begin, ac_end, r->preContext, r->sort, *m_silf, face); - r->constraint = new vm::Machine::Code(true, rc_begin, rc_end, r->preContext, r->sort, *m_silf, face); + r->action = new (m_codes+n*2-2) vm::Machine::Code(false, ac_begin, ac_end, r->preContext, r->sort, *m_silf, face, pt, prog_pool_free); + r->constraint = new (m_codes+n*2-1) vm::Machine::Code(true, rc_begin, rc_end, r->preContext, r->sort, *m_silf, face, pt, prog_pool_free); if (e.test(!r->action || !r->constraint, E_OUTOFMEM) || e.test(r->action->status() != Code::loaded, r->action->status() + E_CODEFAILURE) @@ -221,6 +245,17 @@ bool Pass::readRules(const byte * rule_map, const size_t num_entries, return face.error(e); } + // Shrink the program pool + ptrdiff_t const delta = static_cast(realloc(m_progs, prog_pool_free - m_progs)) - m_progs; + if (delta) + { + m_progs += delta; + for (Code * c = m_codes, * const ce = c + m_numRules*2; c != ce; ++c) + { + c->externalProgramMoved(delta); + } + } + // Load the rule entries map face.error_context((face.error_context() & 0xFFFF00) + EC_APASS); RuleEntry * re = m_ruleMap = gralloc(num_entries); @@ -325,33 +360,52 @@ bool Pass::readRanges(const byte * ranges, size_t num_ranges, Error &e) } -void Pass::runGraphite(Machine & m, FiniteStateMachine & fsm) const +bool Pass::runGraphite(vm::Machine & m, FiniteStateMachine & fsm) const { Slot *s = m.slotMap().segment.first(); - if (!s || !testPassConstraint(m)) return; - Slot *currHigh = s->next(); + if (!s || !testPassConstraint(m)) return true; + if (m_numRules) + { + Slot *currHigh = s->next(); #if !defined GRAPHITE2_NTRACING - if (fsm.dbgout) *fsm.dbgout << "rules" << json::array; - json::closer rules_array_closer(fsm.dbgout); + if (fsm.dbgout) *fsm.dbgout << "rules" << json::array; + json::closer rules_array_closer(fsm.dbgout); #endif - m.slotMap().highwater(currHigh); - int lc = m_iMaxLoop; - do - { - findNDoRule(s, m, fsm); - if (s && (m.slotMap().highpassed() || s == m.slotMap().highwater() || --lc == 0)) { - if (!lc) - { -// if (dbgout) *dbgout << json::item << json::flat << rule_event(-1, s, 1); - s = m.slotMap().highwater(); + m.slotMap().highwater(currHigh); + int lc = m_iMaxLoop; + do + { + findNDoRule(s, m, fsm); + if (s && (s == m.slotMap().highwater() || m.slotMap().highpassed() || --lc == 0)) { + if (!lc) + s = m.slotMap().highwater(); + lc = m_iMaxLoop; + if (s) + m.slotMap().highwater(s->next()); } - lc = m_iMaxLoop; - if (s) - m.slotMap().highwater(s->next()); + } while (s); + } + + if (!(m_flags & 15) || !m.slotMap().segment.hasCollisionInfo()) + return true; + + if (m_flags & 7) + { + if (!(m.slotMap().segment.flags() & Segment::SEG_INITCOLLISIONS)) + { + m.slotMap().segment.positionSlots(0, 0, 0, true); +// m.slotMap().segment.flags(m.slotMap().segment.flags() | Segment::SEG_INITCOLLISIONS); } - } while (s); + if (!collisionShift(&m.slotMap().segment, m.slotMap().segment.dir(), fsm.dbgout)) + return false; + } + if ((m_flags & 24) && !collisionKern(&m.slotMap().segment, m.slotMap().segment.dir(), fsm.dbgout)) + return false; + if ((m_flags & 15) && !collisionFinish(&m.slotMap().segment, fsm.dbgout)) + return false; + return true; } bool Pass::runFSM(FiniteStateMachine& fsm, Slot * slot) const @@ -455,6 +509,7 @@ void Pass::findNDoRule(Slot * & slot, Machine &m, FiniteStateMachine & fsm) cons } slot = slot->next(); + return; } #if !defined GRAPHITE2_NTRACING @@ -464,9 +519,10 @@ void Pass::dumpRuleEventConsidered(const FiniteStateMachine & fsm, const RuleEnt *fsm.dbgout << "considered" << json::array; for (const RuleEntry *r = fsm.rules.begin(); r != &re; ++r) { - if (r->rule->preContext > fsm.slots.context()) continue; - *fsm.dbgout << json::flat << json::object - << "id" << r->rule - m_rules + if (r->rule->preContext > fsm.slots.context()) + continue; + *fsm.dbgout << json::flat << json::object + << "id" << r->rule - m_rules << "failed" << true << "input" << json::flat << json::object << "start" << objectid(dslot(&fsm.slots.segment, input_slot(fsm.slots, -r->rule->preContext))) @@ -586,15 +642,23 @@ int Pass::doAction(const Code *codeptr, Slot * & slot_out, vm::Machine & m) cons void Pass::adjustSlot(int delta, Slot * & slot_out, SlotMap & smap) const { - if (delta < 0) + if (!slot_out) { - if (!slot_out) + if (smap.highpassed() || slot_out == smap.highwater()) { slot_out = smap.segment.last(); ++delta; - if (smap.highpassed() && !smap.highwater()) + if (!smap.highwater()) smap.highpassed(false); } + else + { + slot_out = smap.segment.first(); + --delta; + } + } + if (delta < 0) + { while (++delta <= 0 && slot_out) { if (smap.highpassed() && smap.highwater() == slot_out) @@ -604,11 +668,6 @@ void Pass::adjustSlot(int delta, Slot * & slot_out, SlotMap & smap) const } else if (delta > 0) { - if (!slot_out) - { - slot_out = smap.segment.first(); - --delta; - } while (--delta >= 0 && slot_out) { slot_out = slot_out->next(); @@ -618,3 +677,373 @@ void Pass::adjustSlot(int delta, Slot * & slot_out, SlotMap & smap) const } } +bool Pass::collisionShift(Segment *seg, int dir, json * const dbgout) const +{ + ShiftCollider shiftcoll(dbgout); + // bool isfirst = true; + const uint8 numLoops = m_flags & 7; // number of loops permitted to fix collisions; does not include kerning + bool hasCollisions = false; + Slot *start = seg->first(); // turn on collision fixing for the first slot + Slot *end = NULL; + bool moved = false; + +#if !defined GRAPHITE2_NTRACING + if (dbgout) + *dbgout << "collisions" << json::array + << json::flat << json::object << "num-loops" << numLoops << json::close; +#endif + + while (start) + { +#if !defined GRAPHITE2_NTRACING + if (dbgout) *dbgout << json::object << "phase" << "1" << "moves" << json::array; +#endif + hasCollisions = false; + end = NULL; + // phase 1 : position shiftable glyphs, ignoring kernable glyphs + for (Slot *s = start; s; s = s->next()) + { + const SlotCollision * c = seg->collisionInfo(s); + if (start && (c->flags() & (SlotCollision::COLL_FIX | SlotCollision::COLL_KERN)) == SlotCollision::COLL_FIX + && !resolveCollisions(seg, s, start, shiftcoll, false, dir, moved, hasCollisions, dbgout)) + return false; + if (s != start && c->flags() & SlotCollision::COLL_END) + { + end = s->next(); + break; + } + } + +#if !defined GRAPHITE2_NTRACING + if (dbgout) + *dbgout << json::close << json::close; // phase-1 +#endif + + // phase 2 : loop until happy. + for (int i = 0; i < numLoops - 1; ++i) + { + if (hasCollisions || moved) + { + +#if !defined GRAPHITE2_NTRACING + if (dbgout) + *dbgout << json::object << "phase" << "2a" << "loop" << i << "moves" << json::array; +#endif + // phase 2a : if any shiftable glyphs are in collision, iterate backwards, + // fixing them and ignoring other non-collided glyphs. Note that this handles ONLY + // glyphs that are actually in collision from phases 1 or 2b, and working backwards + // has the intended effect of breaking logjams. + if (hasCollisions) + { + hasCollisions = false; + #if 0 + moved = true; + for (Slot *s = start; s != end; s = s->next()) + { + SlotCollision * c = seg->collisionInfo(s); + c->setShift(Position(0, 0)); + } + #endif + Slot *lend = end ? end->prev() : seg->last(); + Slot *lstart = start->prev(); + for (Slot *s = lend; s != lstart; s = s->prev()) + { + SlotCollision * c = seg->collisionInfo(s); + if (start && (c->flags() & (SlotCollision::COLL_FIX | SlotCollision::COLL_KERN | SlotCollision::COLL_ISCOL)) + == (SlotCollision::COLL_FIX | SlotCollision::COLL_ISCOL)) // ONLY if this glyph is still colliding + { + if (!resolveCollisions(seg, s, lend, shiftcoll, true, dir, moved, hasCollisions, dbgout)) + return false; + c->setFlags(c->flags() | SlotCollision::COLL_TEMPLOCK); + } + } + } + +#if !defined GRAPHITE2_NTRACING + if (dbgout) + *dbgout << json::close << json::close // phase 2a + << json::object << "phase" << "2b" << "loop" << i << "moves" << json::array; +#endif + + // phase 2b : redo basic diacritic positioning pass for ALL glyphs. Each successive loop adjusts + // glyphs from their current adjusted position, which has the effect of gradually minimizing the + // resulting adjustment; ie, the final result will be gradually closer to the original location. + // Also it allows more flexibility in the final adjustment, since it is moving along the + // possible 8 vectors from successively different starting locations. + if (moved) + { + moved = false; + for (Slot *s = start; s != end; s = s->next()) + { + SlotCollision * c = seg->collisionInfo(s); + if (start && (c->flags() & (SlotCollision::COLL_FIX | SlotCollision::COLL_TEMPLOCK + | SlotCollision::COLL_KERN)) == SlotCollision::COLL_FIX + && !resolveCollisions(seg, s, start, shiftcoll, false, dir, moved, hasCollisions, dbgout)) + return false; + else if (c->flags() & SlotCollision::COLL_TEMPLOCK) + c->setFlags(c->flags() & ~SlotCollision::COLL_TEMPLOCK); + } + } + // if (!hasCollisions) // no, don't leave yet because phase 2b will continue to improve things + // break; +#if !defined GRAPHITE2_NTRACING + if (dbgout) + *dbgout << json::close << json::close; // phase 2 +#endif + } + } + if (!end) + break; + start = NULL; + for (Slot *s = end->prev(); s; s = s->next()) + { + if (seg->collisionInfo(s)->flags() & SlotCollision::COLL_START) + { + start = s; + break; + } + } + } + return true; +} + +bool Pass::collisionKern(Segment *seg, int dir, json * const dbgout) const +{ + KernCollider kerncoll(dbgout); + Slot *start = seg->first(); + float ymin = 1e38f; + float ymax = -1e38f; + const GlyphCache &gc = seg->getFace()->glyphs(); + + // phase 3 : handle kerning of clusters +#if !defined GRAPHITE2_NTRACING + if (dbgout) + *dbgout << json::object << "phase" << "3" << "moves" << json::array; +#endif + + for (Slot *s = seg->first(); s; s = s->next()) + { + if (!gc.check(s->gid())) + return false; + const SlotCollision * c = seg->collisionInfo(s); + const Rect &bbox = seg->theGlyphBBoxTemporary(s->gid()); + float y = s->origin().y + c->shift().y; + ymax = max(y + bbox.tr.y, ymax); + ymin = min(y + bbox.bl.y, ymin); + if (start && (c->flags() & (SlotCollision::COLL_KERN | SlotCollision::COLL_FIX)) + == (SlotCollision::COLL_KERN | SlotCollision::COLL_FIX)) + resolveKern(seg, s, start, kerncoll, dir, ymin, ymax, dbgout); + if (c->flags() & SlotCollision::COLL_END) + start = NULL; + if (c->flags() & SlotCollision::COLL_START) + start = s; + } + +#if !defined GRAPHITE2_NTRACING + if (dbgout) + *dbgout << json::close << json::close; // phase 3 +#endif + return true; +} + +bool Pass::collisionFinish(Segment *seg, GR_MAYBE_UNUSED json * const dbgout) const +{ + for (Slot *s = seg->first(); s; s = s->next()) + { + SlotCollision *c = seg->collisionInfo(s); + if (c->shift().x != 0 || c->shift().y != 0) + { + const Position newOffset = c->shift(); + const Position nullPosition(0, 0); + c->setOffset(newOffset + c->offset()); + c->setShift(nullPosition); + } + } +// seg->positionSlots(); + +#if !defined GRAPHITE2_NTRACING + if (dbgout) + *dbgout << json::close; +#endif + return true; +} + +// Can slot s be kerned, or is it attached to something that can be kerned? +static bool inKernCluster(Segment *seg, Slot *s) +{ + SlotCollision *c = seg->collisionInfo(s); + if (c->flags() & SlotCollision::COLL_KERN /** && c->flags() & SlotCollision::COLL_FIX **/ ) + return true; + while (s->attachedTo()) + { + s = s->attachedTo(); + c = seg->collisionInfo(s); + if (c->flags() & SlotCollision::COLL_KERN /** && c->flags() & SlotCollision::COLL_FIX **/ ) + return true; + } + return false; +} + +// Fix collisions for the given slot. +// Return true if everything was fixed, false if there are still collisions remaining. +// isRev means be we are processing backwards. +bool Pass::resolveCollisions(Segment *seg, Slot *slotFix, Slot *start, + ShiftCollider &coll, GR_MAYBE_UNUSED bool isRev, int dir, bool &moved, bool &hasCol, + json * const dbgout) const +{ + Slot * nbor; // neighboring slot + SlotCollision *cFix = seg->collisionInfo(slotFix); + if (!coll.initSlot(seg, slotFix, cFix->limit(), cFix->margin(), cFix->marginWt(), + cFix->shift(), cFix->offset(), dir, dbgout)) + return false; + bool collides = false; + // When we're processing forward, ignore kernable glyphs that preceed the target glyph. + // When processing backward, don't ignore these until we pass slotFix. + bool ignoreForKern = !isRev; + bool rtl = dir & 1; + Slot *base = slotFix; + while (base->attachedTo()) + base = base->attachedTo(); + Position zero(0., 0.); + + // Look for collisions with the neighboring glyphs. + for (nbor = start; nbor; nbor = isRev ? nbor->prev() : nbor->next()) + { + SlotCollision *cNbor = seg->collisionInfo(nbor); + bool sameCluster = nbor->isChildOf(base); + if (nbor != slotFix // don't process if this is the slot of interest + && !(cNbor->flags() & SlotCollision::COLL_IGNORE) // don't process if ignoring + && (nbor == base || sameCluster // process if in the same cluster as slotFix + || !inKernCluster(seg, nbor) // or this cluster is not to be kerned + || (rtl ^ ignoreForKern)) // or it comes before(ltr) or after(rtl) + && (!isRev // if processing forwards then good to merge otherwise only: + || !(cNbor->flags() & SlotCollision::COLL_FIX) // merge in immovable stuff + || (cNbor->flags() & SlotCollision::COLL_KERN && !sameCluster) // ignore other kernable clusters + || (cNbor->flags() & SlotCollision::COLL_ISCOL)) // test against other collided glyphs + && !coll.mergeSlot(seg, nbor, cNbor->shift(), !ignoreForKern, sameCluster, collides, false, dbgout)) + return false; + else if (nbor == slotFix) + // Switching sides of this glyph - if we were ignoring kernable stuff before, don't anymore. + ignoreForKern = !ignoreForKern; + + if (nbor != start && (cNbor->flags() & (isRev ? SlotCollision::COLL_START : SlotCollision::COLL_END))) + break; + } + bool isCol = false; + if (collides || cFix->shift().x != 0.f || cFix->shift().y != 0.f) + { + Position shift = coll.resolve(seg, isCol, dbgout); + // isCol has been set to true if a collision remains. + if (std::fabs(shift.x) < 1e38f && std::fabs(shift.y) < 1e38f) + { + if (sqr(shift.x-cFix->shift().x) + sqr(shift.y-cFix->shift().y) >= m_colThreshold * m_colThreshold) + moved = true; + cFix->setShift(shift); + if (slotFix->firstChild()) + { + Rect bbox; + Position here = slotFix->origin() + shift; + float clusterMin = here.x; + slotFix->firstChild()->finalise(seg, NULL, here, bbox, 0, clusterMin, false); + } + } + } + else + { + // This glyph is not colliding with anything. +#if !defined GRAPHITE2_NTRACING + if (dbgout) + { + *dbgout << json::object + << "missed" << objectid(dslot(seg, slotFix)); + coll.outputJsonDbg(dbgout, seg, -1); + *dbgout << json::close; + } +#endif + } + + // Set the is-collision flag bit. + if (isCol) + { cFix->setFlags(cFix->flags() | SlotCollision::COLL_ISCOL | SlotCollision::COLL_KNOWN); } + else + { cFix->setFlags((cFix->flags() & ~SlotCollision::COLL_ISCOL) | SlotCollision::COLL_KNOWN); } + hasCol |= isCol; + return true; +} + +float Pass::resolveKern(Segment *seg, Slot *slotFix, GR_MAYBE_UNUSED Slot *start, KernCollider &coll, int dir, + float &ymin, float &ymax, json *const dbgout) const +{ + Slot *nbor; // neighboring slot + float currSpace = 0.; + bool collides = false; + unsigned int space_count = 0; + Slot *base = slotFix; + while (base->attachedTo()) + base = base->attachedTo(); + SlotCollision *cFix = seg->collisionInfo(base); + const GlyphCache &gc = seg->getFace()->glyphs(); + + if (base != slotFix) + { + cFix->setFlags(cFix->flags() | SlotCollision::COLL_KERN | SlotCollision::COLL_FIX); + return 0; + } + bool seenEnd = (cFix->flags() & SlotCollision::COLL_END) != 0; + bool isInit = false; + + for (nbor = slotFix->next(); nbor; nbor = nbor->next()) + { + if (nbor->isChildOf(base)) + continue; + if (!gc.check(nbor->gid())) + return 0.; + const Rect &bb = seg->theGlyphBBoxTemporary(nbor->gid()); + SlotCollision *cNbor = seg->collisionInfo(nbor); + if (bb.bl.y == 0.f && bb.tr.y == 0.f) + { + if ((m_flags & 24) == 16) + break; + // Add space for a space glyph. + currSpace += nbor->advance(); + ++space_count; + } + else + { + space_count = 0; + float y = nbor->origin().y + cNbor->shift().y; + ymax = max(y + bb.tr.y, ymax); + ymin = min(y + bb.bl.y, ymin); + if (nbor != slotFix && !(cNbor->flags() & SlotCollision::COLL_IGNORE)) + { + seenEnd = true; + if (!isInit) + { + if (!coll.initSlot(seg, slotFix, cFix->limit(), cFix->margin(), + cFix->shift(), cFix->offset(), dir, ymin, ymax, dbgout)) + return 0.; + isInit = true; + } + collides |= coll.mergeSlot(seg, nbor, cNbor->shift(), currSpace, dir, dbgout); + } + } + if (cNbor->flags() & SlotCollision::COLL_END) + { + if (seenEnd && space_count < 2) + break; + else + seenEnd = true; + } + } + if (collides) + { + Position mv = coll.resolve(seg, slotFix, dir, cFix->margin(), dbgout); + coll.shift(mv, dir); + Position delta = slotFix->advancePos() + mv - cFix->shift(); + slotFix->advance(delta); + cFix->setShift(mv); + return mv.x; + } + return 0.; +} + diff --git a/gfx/graphite2/src/Position.cpp b/gfx/graphite2/src/Position.cpp new file mode 100644 index 00000000000..17247987591 --- /dev/null +++ b/gfx/graphite2/src/Position.cpp @@ -0,0 +1,98 @@ +/* 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/Position.h" +#include + +using namespace graphite2; + +bool Rect::hitTest(Rect &other) +{ + if (bl.x > other.tr.x) return false; + if (tr.x < other.bl.x) return false; + if (bl.y > other.tr.y) return false; + if (tr.y < other.bl.y) return false; + return true; +} + +Position Rect::overlap(Position &offset, Rect &other, Position &othero) +{ + float ax = (bl.x + offset.x) - (other.tr.x + othero.x); + float ay = (bl.y + offset.y) - (other.tr.y + othero.y); + float bx = (other.bl.x + othero.x) - (tr.x + offset.x); + float by = (other.bl.y + othero.y) - (tr.y + offset.y); + return Position((ax > bx ? ax : bx), (ay > by ? ay : by)); +} + +float boundmin(float move, float lim1, float lim2, float &error) +{ + // error is always positive for easy comparison + if (move < lim1 && move < lim2) + { error = 0.; return move; } + else if (lim1 < lim2) + { error = std::fabs(move - lim1); return lim1; } + else + { error = std::fabs(move - lim2); return lim2; } +} + +#if 0 +Position Rect::constrainedAvoid(Position &offset, Rect &box, Rect &sdbox, Position &other, Rect &obox, Rect &osdbox) +{ + // a = max, i = min, s = sum, d = diff + float eax, eay, eix, eiy, eas, eis, ead, eid; + float beste = INF; + Position res; + // calculate the movements in each direction and the error (amount of remaining overlap) + // first param is movement, second and third are movement over the constraining box + float ax = boundmin(obox.tr.x + other.x - box.bl.x - offset.x + 1, tr.x - offset.x, INF, &eax); + float ay = boundmin(obox.tr.y + other.y - box.bl.y - offset.y + 1, tr.y - offset.y, INF, &eay); + float ix = boundmin(obox.bl.x + other.x - box.tr.x - offset.x + 1, bl.x - offset.x, INF, &eix); + float iy = boundmin(obox.bl.y + other.y - box.tr.y - offset.y + 1, bl.y - offset.y, INF, &eiy); + float as = boundmin(ISQRT2 * (osdbox.tr.x + other.x + other.y - sdbox.bl.x - offset.x - offset.y) + 1, tr.x - offset.x, tr.y - offset.y, &eas); + float is = boundmin(ISQRT2 * (osdbox.bl.x + other.x + other.y - sdbox.tr.x - offset.x - offset.y) + 1, bl.x - offset.x, bl.y - offset.y, &eis); + float ad = boundmin(ISQRT2 * (osdbox.tr.y + other.x - other.y - sdbox.bl.y - offset.x + offset.y) + 1, tr.y - offset.y, tr.x - offset.x, &ead); + float id = boundmin(ISQRT2 * (osdbox.bl.y + other.x - other.y - sdbox.tr.y - offset.x + offset.y) + 1, bl.y - offset.y, bl.x - offset.x, &eid); + + if (eax < beste) + { res = Position(ax, 0); beste = eax; } + if (eay < beste) + { res = Position(0, ay); beste = eay; } + if (eix < beste) + { res = Position(ix, 0); beste = eix; } + if (eiy < beste) + { res = Position(0, iy); beste = eiy; } + if (SQRT2 * (eas) < beste) + { res = Position(as, ad); beste = SQRT2 * (eas); } + if (SQRT2 * (eis) < beste) + { res = Position(is, is); beste = SQRT2 * (eis); } + if (SQRT2 * (ead) < beste) + { res = Position(ad, ad); beste = SQRT2 * (ead); } + if (SQRT2 * (eid) < beste) + { res = Position(id, id); beste = SQRT2 * (eid); } + return res; +} +#endif + diff --git a/gfx/graphite2/src/Segment.cpp b/gfx/graphite2/src/Segment.cpp index 42d7be57c26..f8b2690e8cf 100644 --- a/gfx/graphite2/src/Segment.cpp +++ b/gfx/graphite2/src/Segment.cpp @@ -37,6 +37,7 @@ of the License or (at your option) any later version. #include "inc/Main.h" #include "inc/CmapCache.h" #include "inc/Bidi.h" +#include "inc/Collider.h" #include "graphite2/Segment.h" @@ -46,6 +47,7 @@ Segment::Segment(unsigned int numchars, const Face* face, uint32 script, int tex : 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), @@ -55,7 +57,8 @@ Segment::Segment(unsigned int numchars, const Face* face, uint32 script, int tex m_numCharinfo(numchars), m_passBits(m_silf->aPassBits() ? -1 : 0), m_defaultOriginal(0), - m_dir(textDir) + m_dir(textDir), + m_flags(0) { freeSlot(newSlot()); m_bufSize = log_binary(numchars)+1; @@ -335,7 +338,7 @@ void Segment::linkClusters(Slot *s, Slot * end) } } -Position Segment::positionSlots(const Font *font, Slot * iStart, Slot * iEnd) +Position Segment::positionSlots(const Font *font, Slot * iStart, Slot * iEnd, bool isFinal) { Position currpos(0., 0.); float clusterMin = 0.; @@ -349,7 +352,7 @@ Position Segment::positionSlots(const Font *font, Slot * iStart, Slot * iEnd) 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); + currpos = s->finalise(this, font, currpos, bbox, 0, clusterMin = currpos.x, isFinal); } } else @@ -357,7 +360,7 @@ Position Segment::positionSlots(const Font *font, Slot * iStart, Slot * iEnd) 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); + currpos = s->finalise(this, font, currpos, bbox, 0, clusterMin = currpos.x, isFinal); } } return currpos; @@ -434,11 +437,6 @@ bool Segment::read_text(const Face *face, const Features* pFeats/*must not be NU return true; } -void Segment::prepare_pos(const Font * /*font*/) -{ - // copy key changeable metrics into slot (if any); -} - Slot *process_bidi(Slot *start, int level, int prelevel, int &nextLevel, int dirover, int isol, int &cisol, int &isolerr, int &embederr, int init, Segment *seg, uint8 aMirror, BracketPairStack &stack); void resolveImplicit(Slot *s, Segment *seg, uint8 aMirror); void resolveWhitespace(int baseLevel, Slot *s); @@ -462,7 +460,7 @@ void Segment::bidiPass(uint8 aBidi, int paradir, uint8 aMirror) } bmask |= (1 << s->getBidiClass()); s->setBidiLevel(baseLevel); - if (glyphAttr(s->gid(), aMirror) && s->getBidiClass() == 21) + if (s->getBidiClass() == 21) ++ssize; } @@ -492,3 +490,16 @@ void Segment::bidiPass(uint8 aBidi, int paradir, uint8 aMirror) } } +bool Segment::initCollisions() +{ + if (m_collisions) free(m_collisions); + Slot *p = m_first; + m_collisions = gralloc(slotCount()); + if (!m_collisions) return false; + for (unsigned short i = 0; i < slotCount(); ++i) + { + ::new (m_collisions + p->index()) SlotCollision(this, p); + p = p->next(); + } + return true; +} diff --git a/gfx/graphite2/src/Silf.cpp b/gfx/graphite2/src/Silf.cpp index 8b77271d2b4..df7dd1e2ef6 100644 --- a/gfx/graphite2/src/Silf.cpp +++ b/gfx/graphite2/src/Silf.cpp @@ -93,6 +93,10 @@ bool Silf::readGraphite(const byte * const silf_start, size_t lSilf, Face& face, * const silf_end = p + lSilf; Error e; + if (e.test(version >= 0x00060000, E_BADSILFVERSION)) + { + releaseBuffers(); return face.error(e); + } if (version >= 0x00030000) { if (e.test(lSilf < 28, E_BADSIZE)) { releaseBuffers(); return face.error(e); } @@ -140,7 +144,9 @@ bool Silf::readGraphite(const byte * const silf_start, size_t lSilf, Face& face, m_aLig = be::read(p); m_aUser = be::read(p); m_iMaxComp = be::read(p); - be::skip(p,5); // direction and 4 reserved bytes + be::skip(p); // direction + m_aCollision = be::read(p); + be::skip(p,3); be::skip(p, be::read(p)); // don't need critical features yet be::skip(p); // reserved if (e.test(p >= silf_end, E_BADCRITFEATURES)) { releaseBuffers(); return face.error(e); } @@ -155,6 +161,7 @@ bool Silf::readGraphite(const byte * const silf_start, size_t lSilf, Face& face, || e.test(m_aBreak >= num_attrs, E_BADABREAK) || e.test(m_aBidi >= num_attrs, E_BADABIDI) || e.test(m_aMirror>= num_attrs, E_BADAMIRROR) + || e.test(m_aCollision && m_aCollision >= num_attrs - 5, E_BADACOLLISION) || e.test(m_numPasses > 128, E_BADNUMPASSES) || e.test(passes_start >= silf_end, E_BADPASSESSTART) || e.test(m_pPass < m_sPass, E_BADPASSBOUND) || e.test(m_pPass > m_numPasses, E_BADPPASS) || e.test(m_sPass > m_numPasses, E_BADSPASS) || e.test(m_jPass < m_pPass, E_BADJPASSBOUND) || e.test(m_jPass > m_numPasses, E_BADJPASS) @@ -168,11 +175,12 @@ bool Silf::readGraphite(const byte * const silf_start, size_t lSilf, Face& face, if (e.test(p + sizeof(uint16) >= passes_start, E_BADPASSESSTART)) { releaseBuffers(); return face.error(e); } m_numPseudo = be::read(p); be::skip(p, 3); // searchPseudo, pseudoSelector, pseudoShift - if (e.test(p + m_numPseudo*(sizeof(uint32) + sizeof(uint16)) >= passes_start, E_BADNUMPSEUDO)) + m_pseudos = new Pseudo[m_numPseudo]; + if (e.test(p + m_numPseudo*(sizeof(uint32) + sizeof(uint16)) >= passes_start, E_BADNUMPSEUDO) + || e.test(!m_pseudos, E_OUTOFMEM)) { releaseBuffers(); return face.error(e); } - m_pseudos = new Pseudo[m_numPseudo]; for (int i = 0; i < m_numPseudo; i++) { m_pseudos[i].uid = be::read(p); @@ -180,9 +188,11 @@ bool Silf::readGraphite(const byte * const silf_start, size_t lSilf, Face& face, } const size_t clen = readClassMap(p, passes_start - p, version, e); - if (e || e.test(p + clen > passes_start, E_BADPASSESSTART)) { releaseBuffers(); return face.error(e); } - m_passes = new Pass[m_numPasses]; + if (e || e.test(p + clen > passes_start, E_BADPASSESSTART) + || e.test(!m_passes, E_OUTOFMEM)) + { releaseBuffers(); return face.error(e); } + for (size_t i = 0; i < m_numPasses; ++i) { const byte * const pass_start = silf_start + be::read(o_passes), @@ -192,8 +202,15 @@ bool Silf::readGraphite(const byte * const silf_start, size_t lSilf, Face& face, releaseBuffers(); return face.error(e); } + enum passtype pt = PASS_TYPE_UNKNOWN; + if (i >= m_jPass) pt = PASS_TYPE_JUSTIFICATION; + else if (i >= m_pPass) pt = PASS_TYPE_POSITIONING; + else if (i >= m_sPass) pt = PASS_TYPE_SUBSTITUTE; + else pt = PASS_TYPE_LINEBREAK; + m_passes[i].init(this); - if (!m_passes[i].readPass(pass_start, pass_end - pass_start, pass_start - silf_start, face, e)) + if (!m_passes[i].readPass(pass_start, pass_end - pass_start, pass_start - silf_start, face, pt, + version, e)) { releaseBuffers(); return false; @@ -373,7 +390,7 @@ bool Silf::runGraphite(Segment *seg, uint8 firstPass, uint8 lastPass, int dobidi if (!(seg->dir() & 2)) seg->bidiPass(m_aBidi, seg->dir() & 1, m_aMirror); - else if (m_aMirror) + else if (m_aMirror && (seg->dir() & 1)) { Slot * s; for (s = seg->first(); s; s = s->next()) @@ -403,8 +420,9 @@ bool Silf::runGraphite(Segment *seg, uint8 firstPass, uint8 lastPass, int dobidi #endif // test whether to reorder, prepare for positioning - if (i >= 32 || (seg->passBits() & (1 << i)) == 0) - m_passes[i].runGraphite(m, fsm); + if ((i >= 32 || (seg->passBits() & (1 << i)) == 0 || (m_passes[i].flags() & 7)) + && !m_passes[i].runGraphite(m, fsm)) + return false; // only subsitution passes can change segment length, cached subsegments are short for their text if (m.status() != vm::Machine::finished || (i < m_pPass && (seg->slotCount() > initSize * MAX_SEG_GROWTH_FACTOR diff --git a/gfx/graphite2/src/Slot.cpp b/gfx/graphite2/src/Slot.cpp index d802cd98d24..0c25fa76137 100644 --- a/gfx/graphite2/src/Slot.cpp +++ b/gfx/graphite2/src/Slot.cpp @@ -29,6 +29,7 @@ of the License or (at your option) any later version. #include "inc/Silf.h" #include "inc/CharInfo.h" #include "inc/Rule.h" +#include "inc/Collider.h" using namespace graphite2; @@ -85,19 +86,26 @@ void Slot::update(int /*numGrSlots*/, int numCharInfo, Position &relpos) m_position = m_position + relpos; } -Position Slot::finalise(const Segment *seg, const Font *font, Position & base, Rect & bbox, uint8 attrLevel, float & clusterMin) +Position Slot::finalise(const Segment *seg, const Font *font, Position & base, Rect & bbox, uint8 attrLevel, float & clusterMin, bool isFinal) { + SlotCollision *coll = NULL; if (attrLevel && m_attLevel > attrLevel) return Position(0, 0); - float scale = 1.0; + float scale = font ? font->scale() : 1.0f; Position shift(m_shift.x * ((seg->dir() & 1) * -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) || (seg->dir() & 1)) + 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(m_glyphid); + tAdvance = (m_advance.x - glyphFace->theAdvance().x + m_just) * scale + font->advance(glyph()); else tAdvance *= scale; } @@ -107,15 +115,15 @@ Position Slot::finalise(const Segment *seg, const Font *font, Position & base, R if (!m_parent) { res = base + Position(tAdvance, m_advance.y * scale); - clusterMin = base.x; + clusterMin = m_position.x; } else { float tAdv; m_position += (m_attach - m_with) * scale; - tAdv = m_advance.x >= 0.5 ? m_position.x + tAdvance - shift.x : 0.f; + tAdv = m_advance.x >= 0.5f ? m_position.x + tAdvance - shift.x : 0.f; res = Position(tAdv, 0); - if ((m_advance.x >= 0.5 || m_position.x < 0) && m_position.x < clusterMin) clusterMin = m_position.x; + if ((m_advance.x >= 0.5f || m_position.x < 0) && m_position.x < clusterMin) clusterMin = m_position.x; } if (glyphFace) @@ -126,19 +134,19 @@ Position Slot::finalise(const Segment *seg, const Font *font, Position & base, R if (m_child && m_child != this && m_child->attachedTo() == this) { - Position tRes = m_child->finalise(seg, font, m_position, bbox, attrLevel, clusterMin); - if ((!m_parent || m_advance.x >= 0.5) && tRes.x > res.x) res = tRes; + Position tRes = m_child->finalise(seg, font, m_position, bbox, attrLevel, clusterMin, isFinal); + if ((!m_parent || m_advance.x >= 0.5f) && tRes.x > res.x) res = tRes; } if (m_parent && m_sibling && m_sibling != this && m_sibling->attachedTo() == m_parent) { - Position tRes = m_sibling->finalise(seg, font, base, bbox, attrLevel, clusterMin); + Position tRes = m_sibling->finalise(seg, font, base, bbox, attrLevel, clusterMin, isFinal); if (tRes.x > res.x) res = tRes; } if (!m_parent && clusterMin < base.x) { - Position adj = Position(base.x - clusterMin, 0.); + Position adj = Position(m_position.x - clusterMin, 0.); res += adj; m_position += adj; if (m_child) m_child->floodShift(adj); @@ -151,7 +159,7 @@ int32 Slot::clusterMetric(const Segment *seg, uint8 metric, uint8 attrLevel) Position base; Rect bbox = seg->theGlyphBBoxTemporary(glyph()); float clusterMin = 0.; - Position res = finalise(seg, NULL, base, bbox, attrLevel, clusterMin); + Position res = finalise(seg, NULL, base, bbox, attrLevel, clusterMin, false); switch (metrics(metric)) { @@ -180,6 +188,8 @@ int32 Slot::clusterMetric(const Segment *seg, uint8 metric, uint8 attrLevel) } } +#define SLOTGETCOLATTR(x) { SlotCollision *c = seg->collisionInfo(this); return c ? int(c-> x) : 0; } + int Slot::getAttr(const Segment *seg, attrCode ind, uint8 subindex) const { if (!this) return 0; @@ -211,7 +221,7 @@ int Slot::getAttr(const Segment *seg, attrCode ind, uint8 subindex) const case gr_slatBreak : return seg->charinfo(m_original)->breakWeight(); case gr_slatCompRef : return 0; case gr_slatDir : if (m_bidiCls == -1) - const_cast(this)->setBidiClass(seg->glyphAttr(gid(), seg->silf()->aBidi())); + const_cast(this)->setBidiClass(int8(seg->glyphAttr(gid(), seg->silf()->aBidi()))); return m_bidiCls; case gr_slatInsert : return isInsertBefore(); case gr_slatPosX : return int(m_position.x); // but need to calculate it @@ -224,10 +234,42 @@ int Slot::getAttr(const Segment *seg, attrCode ind, uint8 subindex) const case gr_slatUserDefn : return m_userAttr[subindex]; case gr_slatSegSplit : return seg->charinfo(m_original)->flags() & 3; case gr_slatBidiLevel: return m_bidiLevel; - default : return 0; + 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 subindex, int16 value, const SlotMap & map) { if (!this) return; @@ -252,9 +294,9 @@ void Slot::setAttr(Segment *seg, attrCode ind, uint8 subindex, int16 value, cons if (idx < map.size() && map[idx]) { Slot *other = map[idx]; - if (other == this) break; + if (other == this || other == m_parent) break; if (m_parent) m_parent->removeChild(this); - if (other->child(this)) + if (!other->isChildOf(this) && other->child(this)) { attachTo(other); if (((seg->dir() & 1) != 0) ^ (idx > subindex)) @@ -280,7 +322,7 @@ void Slot::setAttr(Segment *seg, attrCode ind, uint8 subindex, int16 value, cons seg->charinfo(m_original)->breakWeight(value); break; case gr_slatCompRef : break; // not sure what to do here - case gr_slatDir : m_bidiCls = value; break; + case gr_slatDir : m_bidiCls = int8(value); break; case gr_slatInsert : markInsertBefore(value? true : false); break; @@ -293,6 +335,29 @@ void Slot::setAttr(Segment *seg, attrCode ind, uint8 subindex, int16 value, cons case gr_slatJWidth : just(value); break; case gr_slatSegSplit : seg->charinfo(m_original)->addflags(value & 3); break; case gr_slatUserDefn : m_userAttr[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; } @@ -374,6 +439,7 @@ bool Slot::removeSibling(Slot *ap) else if (ap == m_sibling) { m_sibling = m_sibling->nextSibling(); + ap->sibling(NULL); return true; } else @@ -425,3 +491,29 @@ void SlotJustify::LoadSlot(const Slot *s, const Segment *seg) v[3] = seg->glyphAttr(s->gid(), justs->attrWeight()); } } + +Slot * Slot::nextInCluster(const Slot *s) const +{ + Slot *base; + if (s->firstChild()) + return s->firstChild(); + else if (s->nextSibling()) + return s->nextSibling(); + while ((base = s->attachedTo())) + { + if (base->firstChild() == s && base->nextSibling()) + return base->nextSibling(); + s = base; + } + return NULL; +} + +bool Slot::isChildOf(const Slot *base) const +{ + if (m_parent == base) + return true; + else if (!m_parent) + return false; + else + return m_parent->isChildOf(base); +} diff --git a/gfx/graphite2/src/TtfUtil.cpp b/gfx/graphite2/src/TtfUtil.cpp index fcd2478a266..ad0ec965b75 100644 --- a/gfx/graphite2/src/TtfUtil.cpp +++ b/gfx/graphite2/src/TtfUtil.cpp @@ -873,11 +873,11 @@ const void * FindCmapSubtable(const void * pCmap, int nPlatformId, /* =3 */ int /*---------------------------------------------------------------------------------------------- Check the Microsoft Unicode subtable for expected values ----------------------------------------------------------------------------------------------*/ -bool CheckCmapSubtable4(const void * pCmapSubtable4) +bool CheckCmapSubtable4(const void * pCmapSubtable4 /*, unsigned int maxgid*/) { if (!pCmapSubtable4) return false; const Sfnt::CmapSubTable * pTable = reinterpret_cast(pCmapSubtable4); - // Bob H says ome freeware TT fonts have version 1 (eg, CALIGULA.TTF) + // Bob H say some freeware TT fonts have version 1 (eg, CALIGULA.TTF) // so don't check subtable version. 21 Mar 2002 spec changes version to language. if (be::swap(pTable->format) != 4) return false; const Sfnt::CmapSubTableFormat4 * pTable4 = reinterpret_cast(pCmapSubtable4); @@ -889,7 +889,37 @@ bool CheckCmapSubtable4(const void * pCmapSubtable4) return false; // check last range is properly terminated uint16 chEnd = be::peek(pTable4->end_code + nRanges - 1); - return (chEnd == 0xFFFF); + if (chEnd != 0xFFFF) + return false; +#if 0 + int lastend = -1; + for (int i = 0; i < nRanges; ++i) + { + uint16 end = be::peek(pTable4->end_code + i); + uint16 start = be::peek(pTable4->end_code + nRanges + 1 + i); + int16 delta = be::peek(pTable4->end_code + 2*nRanges + 1 + i); + uint16 offset = be::peek(pTable4->end_code + 3*nRanges + 1 + i); + if (lastend >= end || lastend >= start) + return false; + if (offset) + { + const uint16 *gstart = pTable4->end_code + 3*nRanges + 1 + i + (offset >> 1); + const uint16 *gend = gstart + end - start; + if ((char *)gend >= (char *)pCmapSubtable4 + length) + return false; + while (gstart <= gend) + { + uint16 g = be::peek(gstart++); + if (g && ((g + delta) & 0xFFFF) > maxgid) + return false; + } + } + else if (((delta + end) & 0xFFFF) > maxgid) + return false; + lastend = end; + } +#endif + return true;; } /*---------------------------------------------------------------------------------------------- @@ -1032,7 +1062,7 @@ unsigned int CmapSubtable4NextCodepoint(const void *pCmap31, unsigned int nUnico /*---------------------------------------------------------------------------------------------- Check the Microsoft UCS-4 subtable for expected values. ----------------------------------------------------------------------------------------------*/ -bool CheckCmapSubtable12(const void *pCmapSubtable12) +bool CheckCmapSubtable12(const void *pCmapSubtable12 /*, unsigned int maxgid*/) { if (!pCmapSubtable12) return false; const Sfnt::CmapSubTable * pTable = reinterpret_cast(pCmapSubtable12); @@ -1042,9 +1072,19 @@ bool CheckCmapSubtable12(const void *pCmapSubtable12) uint32 length = be::swap(pTable12->length); if (length < sizeof(Sfnt::CmapSubTableFormat12)) return false; - - return (length == (sizeof(Sfnt::CmapSubTableFormat12) + (be::swap(pTable12->num_groups) - 1) - * sizeof(uint32) * 3)); + uint32 num_groups = be::swap(pTable12->num_groups); + if (length != (sizeof(Sfnt::CmapSubTableFormat12) + (num_groups - 1) * sizeof(uint32) * 3)) + return false; +#if 0 + for (unsigned int i = 0; i < num_groups; ++i) + { + if (be::swap(pTable12->group[i].end_char_code) - be::swap(pTable12->group[i].start_char_code) + be::swap(pTable12->group[i].start_glyph_id) > maxgid) + return false; + if (i > 0 && be::swap(pTable12->group[i].start_char_code) <= be::swap(pTable12->group[i-1].end_char_code)) + return false; + } +#endif + return true; } /*---------------------------------------------------------------------------------------------- @@ -1145,6 +1185,7 @@ size_t LocaLookup(gid16 nGlyphId, const void * pHead) // throw (std::out_of_range) { const Sfnt::FontHeader * pTable = reinterpret_cast(pHead); + size_t res = -2; // CheckTable verifies the index_to_loc_format is valid if (be::swap(pTable->index_to_loc_format) == Sfnt::FontHeader::ShortIndexLocFormat) @@ -1152,21 +1193,24 @@ size_t LocaLookup(gid16 nGlyphId, if (nGlyphId < (lLocaSize >> 1) - 1) // allow sentinel value to be accessed { const uint16 * pShortTable = reinterpret_cast(pLoca); - return (be::peek(pShortTable + nGlyphId) << 1); + res = be::peek(pShortTable + nGlyphId) << 1; + if (res == static_cast(be::peek(pShortTable + nGlyphId + 1) << 1)) + return -1; } } - - if (be::swap(pTable->index_to_loc_format) == Sfnt::FontHeader::LongIndexLocFormat) + else if (be::swap(pTable->index_to_loc_format) == Sfnt::FontHeader::LongIndexLocFormat) { // loca entries are four bytes if (nGlyphId < (lLocaSize >> 2) - 1) { const uint32 * pLongTable = reinterpret_cast(pLoca); - return be::peek(pLongTable + nGlyphId); + res = be::peek(pLongTable + nGlyphId); + if (res == static_cast(be::peek(pLongTable + nGlyphId + 1))) + return -1; } } // only get here if glyph id was bad - return -1; + return res; //throw std::out_of_range("glyph id out of range for font"); } @@ -1177,7 +1221,7 @@ size_t LocaLookup(gid16 nGlyphId, void * GlyfLookup(const void * pGlyf, size_t nGlyfOffset, size_t nTableLen) { const uint8 * pByte = reinterpret_cast(pGlyf); - if (nGlyfOffset == size_t(-1) || nGlyfOffset >= nTableLen) + if (nGlyfOffset == size_t(-1) || nGlyfOffset == size_t(-2) || nGlyfOffset >= nTableLen) return NULL; return const_cast(pByte + nGlyfOffset); } diff --git a/gfx/graphite2/src/files.mk b/gfx/graphite2/src/files.mk index a9950a58d70..b1ff7736f2b 100644 --- a/gfx/graphite2/src/files.mk +++ b/gfx/graphite2/src/files.mk @@ -51,15 +51,19 @@ $(_NS)_SOURCES = \ $($(_NS)_BASE)/src/CachedFace.cpp \ $($(_NS)_BASE)/src/CmapCache.cpp \ $($(_NS)_BASE)/src/Code.cpp \ + $($(_NS)_BASE)/src/Collider.cpp \ + $($(_NS)_BASE)/src/Decompressor.cpp \ $($(_NS)_BASE)/src/Face.cpp \ $($(_NS)_BASE)/src/FeatureMap.cpp \ $($(_NS)_BASE)/src/FileFace.cpp \ $($(_NS)_BASE)/src/Font.cpp \ $($(_NS)_BASE)/src/GlyphCache.cpp \ $($(_NS)_BASE)/src/GlyphFace.cpp \ + $($(_NS)_BASE)/src/Intervals.cpp \ $($(_NS)_BASE)/src/Justifier.cpp \ $($(_NS)_BASE)/src/NameTable.cpp \ $($(_NS)_BASE)/src/Pass.cpp \ + $($(_NS)_BASE)/src/Position.cpp \ $($(_NS)_BASE)/src/SegCache.cpp \ $($(_NS)_BASE)/src/SegCacheEntry.cpp \ $($(_NS)_BASE)/src/SegCacheStore.cpp \ @@ -74,10 +78,14 @@ $(_NS)_PRIVATE_HEADERS = \ $($(_NS)_BASE)/src/inc/bits.h \ $($(_NS)_BASE)/src/inc/debug.h \ $($(_NS)_BASE)/src/inc/json.h \ + $($(_NS)_BASE)/src/inc/locale2lcid.h \ + $($(_NS)_BASE)/src/inc/Bidi.h \ $($(_NS)_BASE)/src/inc/CachedFace.h \ $($(_NS)_BASE)/src/inc/CharInfo.h \ $($(_NS)_BASE)/src/inc/CmapCache.h \ $($(_NS)_BASE)/src/inc/Code.h \ + $($(_NS)_BASE)/src/inc/Collider.h \ + $($(_NS)_BASE)/src/inc/Decompressor.h \ $($(_NS)_BASE)/src/inc/Endian.h \ $($(_NS)_BASE)/src/inc/Error.h \ $($(_NS)_BASE)/src/inc/Face.h \ @@ -87,6 +95,7 @@ $(_NS)_PRIVATE_HEADERS = \ $($(_NS)_BASE)/src/inc/Font.h \ $($(_NS)_BASE)/src/inc/GlyphCache.h \ $($(_NS)_BASE)/src/inc/GlyphFace.h \ + $($(_NS)_BASE)/src/inc/Intervals.h \ $($(_NS)_BASE)/src/inc/List.h \ $($(_NS)_BASE)/src/inc/locale2lcid.h \ $($(_NS)_BASE)/src/inc/Machine.h \ @@ -101,6 +110,7 @@ $(_NS)_PRIVATE_HEADERS = \ $($(_NS)_BASE)/src/inc/SegCacheEntry.h \ $($(_NS)_BASE)/src/inc/SegCacheStore.h \ $($(_NS)_BASE)/src/inc/Segment.h \ + $($(_NS)_BASE)/src/inc/Shrinker.h \ $($(_NS)_BASE)/src/inc/Silf.h \ $($(_NS)_BASE)/src/inc/Slot.h \ $($(_NS)_BASE)/src/inc/Sparse.h \ diff --git a/gfx/graphite2/src/gr_face.cpp b/gfx/graphite2/src/gr_face.cpp index a245da4da47..fe1eb838207 100644 --- a/gfx/graphite2/src/gr_face.cpp +++ b/gfx/graphite2/src/gr_face.cpp @@ -46,7 +46,7 @@ namespace #ifdef GRAPHITE2_TELEMETRY telemetry::category _misc_cat(face.tele.misc); #endif - Face::Table silf(face, Tag::Silf); + Face::Table silf(face, Tag::Silf, 0x00050000); if (silf) options &= ~gr_face_dumbRendering; else if (!(options & gr_face_dumbRendering)) return false; diff --git a/gfx/graphite2/src/gr_logging.cpp b/gfx/graphite2/src/gr_logging.cpp index 3bf34139249..a4afcc2a505 100644 --- a/gfx/graphite2/src/gr_logging.cpp +++ b/gfx/graphite2/src/gr_logging.cpp @@ -32,6 +32,7 @@ of the License or (at your option) any later version. #include "inc/Slot.h" #include "inc/Segment.h" #include "inc/json.h" +#include "inc/Collider.h" #if defined _WIN32 #include "windows.h" @@ -184,6 +185,7 @@ json & graphite2::operator << (json & j, const dslot & ds) throw() assert(ds.second); const Segment & seg = *ds.first; const Slot & s = *ds.second; + const SlotCollision *cslot = seg.collisionInfo(ds.second); j << json::object << "id" << objectid(ds) @@ -220,6 +222,28 @@ json & graphite2::operator << (json & j, const dslot & ds) throw() j << objectid(dslot(&seg, c)); j << json::close; } + if (cslot) + { + // Note: the reason for using Positions to lump together related attributes is to make the + // JSON output slightly more compact. + j << "collision" << json::flat << json::object +// << "shift" << cslot->shift() -- not used pass level, only within the collision routine itself + << "offset" << cslot->offset() + << "limit" << cslot->limit() + << "flags" << cslot->flags() + << "margin" << Position(cslot->margin(), cslot->marginWt()) + << "exclude" << cslot->exclGlyph() + << "excludeoffset" << cslot->exclOffset(); + if (cslot->seqOrder() != 0) + { + j << "seqclass" << Position(cslot->seqClass(), cslot->seqProxClass()) + << "seqorder" << cslot->seqOrder() + << "seqabove" << Position(cslot->seqAboveXoff(), cslot->seqAboveWt()) + << "seqbelow" << Position(cslot->seqBelowXlim(), cslot->seqBelowWt()) + << "seqvalign" << Position(cslot->seqValignHt(), cslot->seqValignWt()); + } + j << json::close; + } return j << json::close; } diff --git a/gfx/graphite2/src/gr_segment.cpp b/gfx/graphite2/src/gr_segment.cpp index 71e40bcdddf..aab74e40ff5 100644 --- a/gfx/graphite2/src/gr_segment.cpp +++ b/gfx/graphite2/src/gr_segment.cpp @@ -48,10 +48,6 @@ namespace delete pRes; return NULL; } - // run the line break passes - // run the substitution passes - pRes->prepare_pos(font); - // run the positioning passes pRes->finalise(font); return static_cast(pRes); diff --git a/gfx/graphite2/src/gr_slot.cpp b/gfx/graphite2/src/gr_slot.cpp index c615e40ffe5..0e41f6cb519 100644 --- a/gfx/graphite2/src/gr_slot.cpp +++ b/gfx/graphite2/src/gr_slot.cpp @@ -103,11 +103,11 @@ float gr_slot_advance_X(const gr_slot* p/*not NULL*/, const gr_face *face, const return res; } -float gr_slot_advance_Y(const gr_slot *p/*not NULL*/, const gr_face *face, const gr_font *font) +float gr_slot_advance_Y(const gr_slot *p/*not NULL*/, GR_MAYBE_UNUSED const gr_face *face, const gr_font *font) { assert(p); float res = p->advancePos().y; - if (font && (face || !face)) + if (font) return res * font->scale(); else return res; diff --git a/gfx/graphite2/src/inc/Code.h b/gfx/graphite2/src/inc/Code.h index 1ce148ab430..c27ff0d9da1 100644 --- a/gfx/graphite2/src/inc/Code.h +++ b/gfx/graphite2/src/inc/Code.h @@ -41,6 +41,14 @@ namespace graphite2 { class Silf; class Face; +enum passtype { + PASS_TYPE_UNKNOWN = 0, + PASS_TYPE_LINEBREAK, + PASS_TYPE_SUBSTITUTE, + PASS_TYPE_POSITIONING, + PASS_TYPE_JUSTIFICATION +}; + namespace vm { class Machine::Code @@ -56,10 +64,12 @@ public: jump_past_end, arguments_exhausted, missing_return, - nested_context_item + nested_context_item, + underfull_stack }; private: + static byte * local_memory; class decoder; instr * _code; @@ -79,7 +89,8 @@ private: public: Code() throw(); Code(bool is_constraint, const byte * bytecode_begin, const byte * const bytecode_end, - uint8 pre_context, uint16 rule_length, const Silf &, const Face &); + uint8 pre_context, uint16 rule_length, const Silf &, const Face &, + enum passtype pt, byte * & _out = local_memory); Code(const Machine::Code &) throw(); ~Code() throw(); @@ -92,12 +103,14 @@ public: bool immutable() const throw(); bool deletes() const throw(); size_t maxRef() const throw(); + void externalProgramMoved(ptrdiff_t) throw(); int32 run(Machine &m, slotref * & map) const; CLASS_NEW_DELETE; }; + inline Machine::Code::Code() throw() : _code(0), _data(0), _data_size(0), _instr_count(0), _max_ref(0), _status(loaded), _constraint(false), _modify(false),_delete(false), @@ -171,5 +184,14 @@ inline size_t Machine::Code::maxRef() const throw() return _max_ref; } +inline void Machine::Code::externalProgramMoved(ptrdiff_t dist) throw() +{ + if (_code && !_own) + { + _code += dist / sizeof(instr); + _data += dist; + } +} + } // namespace vm } // namespace graphite2 diff --git a/gfx/graphite2/src/inc/Collider.h b/gfx/graphite2/src/inc/Collider.h new file mode 100644 index 00000000000..635f9b07324 --- /dev/null +++ b/gfx/graphite2/src/inc/Collider.h @@ -0,0 +1,220 @@ +/* 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. +*/ +#pragma once + +#include +#include "inc/List.h" +#include "inc/Slot.h" +#include "inc/Position.h" +#include "inc/Intervals.h" +#include "inc/debug.h" +//#include "inc/Segment.h" + +namespace graphite2 { + +class json; +class Slot; +class Segment; + +#define SLOTCOLSETUINTPROP(x, y) uint16 x() const { return _ ##x; } void y (uint16 v) { _ ##x = v; } +#define SLOTCOLSETINTPROP(x, y) int16 x() const { return _ ##x; } void y (int16 v) { _ ##x = v; } +#define SLOTCOLSETPOSITIONPROP(x, y) const Position &x() const { return _ ##x; } void y (const Position &v) { _ ##x = v; } + +// Slot attributes related to collision-fixing +class SlotCollision +{ +public: + enum { + // COLL_TESTONLY = 0, // default - test other glyphs for collision with this one, but don't move this one + COLL_FIX = 1, // fix collisions involving this glyph + COLL_IGNORE = 2, // ignore this glyph altogether + COLL_START = 4, // start of range of possible collisions + COLL_END = 8, // end of range of possible collisions + COLL_KERN = 16, // collisions with this glyph are fixed by adding kerning space after it + COLL_ISCOL = 32, // this glyph has a collision + COLL_KNOWN = 64, // we've figured out what's happening with this glyph + COLL_TEMPLOCK = 128, // Lock glyphs that have been given priority positioning + ////COLL_JUMPABLE = 128, // moving glyphs may jump this stationary glyph in any direction - DELETE + ////COLL_OVERLAP = 256, // use maxoverlap to restrict - DELETE + }; + + // Behavior for the collision.order attribute. To GDL this is an enum, to us it's a bitfield, with only 1 bit set + // Allows for easier inversion. + enum { + SEQ_ORDER_LEFTDOWN = 1, + SEQ_ORDER_RIGHTUP = 2, + SEQ_ORDER_NOABOVE = 4, + SEQ_ORDER_NOBELOW = 8, + SEQ_ORDER_NOLEFT = 16, + SEQ_ORDER_NORIGHT = 32 + }; + + SlotCollision(Segment *seg, Slot *slot); + void initFromSlot(Segment *seg, Slot *slot); + + const Rect &limit() const { return _limit; } + void setLimit(const Rect &r) { _limit = r; } + SLOTCOLSETPOSITIONPROP(shift, setShift) + SLOTCOLSETPOSITIONPROP(offset, setOffset) + SLOTCOLSETPOSITIONPROP(exclOffset, setExclOffset) + SLOTCOLSETUINTPROP(margin, setMargin) + SLOTCOLSETUINTPROP(marginWt, setMarginWt) + SLOTCOLSETUINTPROP(flags, setFlags) + SLOTCOLSETUINTPROP(exclGlyph, setExclGlyph) + SLOTCOLSETUINTPROP(seqClass, setSeqClass) + SLOTCOLSETUINTPROP(seqProxClass, setSeqProxClass) + SLOTCOLSETUINTPROP(seqOrder, setSeqOrder) + SLOTCOLSETINTPROP(seqAboveXoff, setSeqAboveXoff) + SLOTCOLSETUINTPROP(seqAboveWt, setSeqAboveWt) + SLOTCOLSETINTPROP(seqBelowXlim, setSeqBelowXlim) + SLOTCOLSETUINTPROP(seqBelowWt, setSeqBelowWt) + SLOTCOLSETUINTPROP(seqValignHt, setSeqValignHt) + SLOTCOLSETUINTPROP(seqValignWt, setSeqValignWt) + + float getKern(int dir) const; + +private: + Rect _limit; + Position _shift; // adjustment within the given pass + Position _offset; // total adjustment for collisions + Position _exclOffset; + uint16 _margin; + uint16 _marginWt; + uint16 _flags; + uint16 _exclGlyph; + uint16 _seqClass; + uint16 _seqProxClass; + uint16 _seqOrder; + int16 _seqAboveXoff; + uint16 _seqAboveWt; + int16 _seqBelowXlim; + uint16 _seqBelowWt; + uint16 _seqValignHt; + uint16 _seqValignWt; + +}; // end of class SlotColllision + +class BBox; +class SlantBox; + +class ShiftCollider +{ +public: + typedef std::pair fpair; + typedef Vector vfpairs; + typedef vfpairs::iterator ivfpairs; + + ShiftCollider(GR_MAYBE_UNUSED json *dbgout) + { +#if !defined GRAPHITE2_NTRACING + for (int i = 0; i < 4; ++i) + _ranges[i].setdebug(dbgout); +#endif + } + ~ShiftCollider() throw() { }; + + bool initSlot(Segment *seg, Slot *aSlot, const Rect &constraint, + float margin, float marginMin, const Position &currShift, + const Position &currOffset, int dir, GR_MAYBE_UNUSED json * const dbgout); + bool mergeSlot(Segment *seg, Slot *slot, const Position &currShift, bool isAfter, + bool sameCluster, bool &hasCol, bool isExclusion, GR_MAYBE_UNUSED json * const dbgout); + Position resolve(Segment *seg, bool &isCol, GR_MAYBE_UNUSED json * const dbgout); + void addBox_slope(bool isx, const Rect &box, const BBox &bb, const SlantBox &sb, const Position &org, float weight, float m, bool minright, int mode); + void removeBox(const Rect &box, const BBox &bb, const SlantBox &sb, const Position &org, int mode); + const Position &origin() const { return _origin; } + +#if !defined GRAPHITE2_NTRACING + void outputJsonDbg(json * const dbgout, Segment *seg, int axis); + void outputJsonDbgStartSlot(json * const dbgout, Segment *seg); + void outputJsonDbgEndSlot(json * const dbgout, Position resultPos, int bestAxis, bool isCol); + void outputJsonDbgOneVector(json * const dbgout, Segment *seg, int axis, float tleft, float bestCost, float bestVal); + void outputJsonDbgRawRanges(json * const dbgout, int axis); + void outputJsonDbgRemovals(json * const dbgout, int axis, Segment *seg); +#endif + + CLASS_NEW_DELETE; + +protected: + Zones _ranges[4]; // possible movements in 4 directions (horizontally, vertically, diagonally); + Slot * _target; // the glyph to fix + Rect _limit; + Position _currShift; + Position _currOffset; + Position _origin; // Base for all relative calculations + float _margin; + float _marginWt; + float _len[4]; + uint16 _seqClass; + uint16 _seqProxClass; + uint16 _seqOrder; + + //bool _scraping[4]; + +}; // end of class ShiftCollider + +class KernCollider +{ +public: + KernCollider(GR_MAYBE_UNUSED json *dbg) : _miny(-1e38f), _maxy(1e38f) { }; + ~KernCollider() throw() { }; + bool initSlot(Segment *seg, Slot *aSlot, const Rect &constraint, float margin, + const Position &currShift, const Position &offsetPrev, int dir, + float ymin, float ymax, json * const dbgout); + bool mergeSlot(Segment *seg, Slot *slot, const Position &currShift, float currSpace, int dir, json * const dbgout); + Position resolve(Segment *seg, Slot *slot, int dir, float margin, json * const dbgout); + void shift(const Position &mv, int dir); + + CLASS_NEW_DELETE; + +private: + Slot * _target; // the glyph to fix + Rect _limit; + float _margin; + Position _offsetPrev; // kern from a previous pass + Position _currShift; // NOT USED?? + float _miny; // y-coordinates offset by global slot position + float _maxy; + Vector _edges; // edges of horizontal slices + float _sliceWidth; // width of each slice + float _mingap; + float _xbound; // max or min edge + +#if !defined GRAPHITE2_NTRACING + // Debugging + Segment * _seg; + Vector _nearEdges; // closest potential collision in each slice + Vector _slotNear; +#endif +}; // end of class KernCollider + + +inline +float sqr(float x) { return x * x; } + + +}; // end of namespace graphite2 + diff --git a/gfx/graphite2/src/inc/Decompressor.h b/gfx/graphite2/src/inc/Decompressor.h new file mode 100644 index 00000000000..e79410455ee --- /dev/null +++ b/gfx/graphite2/src/inc/Decompressor.h @@ -0,0 +1,52 @@ +/* Copyright (c) 2012, Siyuan Fu + Copyright (c) 2015, SIL International + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + 3. Neither the name of the copyright holder nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + POSSIBILITY OF SUCH DAMAGE. +*/ +#pragma once + +#include + +namespace shrinker +{ + +int decompress(void const *in, size_t in_size, void *out, size_t out_size); +/* +in: inbuf --- compressed data +out: outbuf --- decompressed data to place in +size: decompressed(original) data size should be + +return value: + positive integer means decompress success and it's the sizeof decompressed data, + which should be equal to size. + or -1 means decompress failed +*/ + +} // end of namespace shrinker + + diff --git a/gfx/graphite2/src/inc/Error.h b/gfx/graphite2/src/inc/Error.h index dd958de6fcd..899b44fd2c9 100644 --- a/gfx/graphite2/src/inc/Error.h +++ b/gfx/graphite2/src/inc/Error.h @@ -111,16 +111,23 @@ enum errors { E_BADRULEMAPPING = 50, // The structure of the rule mapping is bad E_BADRANGE = 51, // Bad column range structure including a glyph in more than one column E_BADRULENUM = 52, // A reference to a rule is out of range (too high) + E_BADACOLLISION = 53, // Bad Silf table collision attribute number (too high) + E_BADEMPTYPASS = 54, // Can't have empty passes (no rules) except for collision passes + E_BADSILFVERSION = 55, // The Silf table has a bad version (probably too high) + E_BADCOLLISIONPASS = 56, // Collision flags set on a non positioning pass // Code errors E_CODEFAILURE = 60, // Base code error. The following subcodes must align with Machine::Code::status_t in Code.h - E_CODEALLOC = 61, // Out of memory - E_INVALIDOPCODE = 62, // Invalid op code - E_UNIMPOPCODE = 63, // Unimplemented op code encountered - E_OUTOFRANGECODE = 64, // Code argument out of range - E_BADJUMPCODE = 65, // Code jumps past end of op codes - E_CODEBADARGS = 66, // Code arguments exhausted - E_CODENORETURN = 67, // Missing return type op code at end of code - E_CODENESTEDCTXT = 68 // Nested context encountered in code + E_CODEALLOC = 61, // Out of memory + E_INVALIDOPCODE = 62, // Invalid op code + E_UNIMPOPCODE = 63, // Unimplemented op code encountered + E_OUTOFRANGECODE = 64, // Code argument out of range + E_BADJUMPCODE = 65, // Code jumps past end of op codes + E_CODEBADARGS = 66, // Code arguments exhausted + E_CODENORETURN = 67, // Missing return type op code at end of code + E_CODENESTEDCTXT = 68, // Nested context encountered in code +// Compression errors + E_BADSCHEME = 69, + E_SHRINKERFAILED = 70, }; } diff --git a/gfx/graphite2/src/inc/Face.h b/gfx/graphite2/src/inc/Face.h index f8ef4041412..252646f32d9 100644 --- a/gfx/graphite2/src/inc/Face.h +++ b/gfx/graphite2/src/inc/Face.h @@ -170,10 +170,13 @@ class Face::Table const Face * _f; mutable const byte * _p; uint32 _sz; + bool _compressed; + + Error decompress(); public: Table() throw(); - Table(const Face & face, const Tag n) throw(); + Table(const Face & face, const Tag n, uint32 version=0xffffffff) throw(); Table(const Table & rhs) throw(); ~Table() throw(); @@ -185,13 +188,13 @@ public: inline Face::Table::Table() throw() -: _f(0), _p(0), _sz(0) +: _f(0), _p(0), _sz(0), _compressed(false) { } inline Face::Table::Table(const Table & rhs) throw() -: _f(rhs._f), _p(rhs._p), _sz(rhs._sz) +: _f(rhs._f), _p(rhs._p), _sz(rhs._sz), _compressed(rhs._compressed) { rhs._p = 0; } @@ -199,7 +202,9 @@ Face::Table::Table(const Table & rhs) throw() inline Face::Table::~Table() throw() { - if (_p && _f->m_ops.release_table) + if (_compressed) + free(const_cast(_p)); + else if (_p && _f->m_ops.release_table) (*_f->m_ops.release_table)(_f->m_appFaceHandle, _p); } diff --git a/gfx/graphite2/src/inc/FeatureMap.h b/gfx/graphite2/src/inc/FeatureMap.h index bc8a5db170d..a199a87d71f 100644 --- a/gfx/graphite2/src/inc/FeatureMap.h +++ b/gfx/graphite2/src/inc/FeatureMap.h @@ -117,9 +117,8 @@ class NameAndFeatureRef class FeatureMap { public: - FeatureMap() : m_numFeats(0), m_feats(NULL), m_pNamedFeats(NULL), - m_defaultFeatures(NULL) {} - ~FeatureMap() { delete [] m_feats; delete[] m_pNamedFeats; delete m_defaultFeatures; } + FeatureMap() : m_numFeats(0), m_feats(NULL), m_pNamedFeats(NULL) {} + ~FeatureMap() { delete [] m_feats; delete[] m_pNamedFeats; } bool readFeats(const Face & face); const FeatureRef *findFeatureRef(uint32 name) const; @@ -135,7 +134,7 @@ friend class SillMap; FeatureRef *m_feats; NameAndFeatureRef* m_pNamedFeats; //owned - FeatureVal* m_defaultFeatures; //owned + FeatureVal m_defaultFeatures; //owned private: //defensive on m_feats, m_pNamedFeats, and m_defaultFeatures FeatureMap(const FeatureMap&); diff --git a/gfx/graphite2/src/inc/GlyphCache.h b/gfx/graphite2/src/inc/GlyphCache.h index ab5b0b7c5fe..821aae53f2d 100644 --- a/gfx/graphite2/src/inc/GlyphCache.h +++ b/gfx/graphite2/src/inc/GlyphCache.h @@ -29,14 +29,65 @@ of the License or (at your option) any later version. #include "graphite2/Font.h" #include "inc/Main.h" +#include "inc/Position.h" +#include "inc/GlyphFace.h" namespace graphite2 { class Face; class FeatureVal; -class GlyphFace; class Segment; + +class SlantBox +{ +public: + SlantBox(float psi = 0., float pdi = 0., float psa = 0., float pda = 0.) : si(psi), di(pdi), sa(psa), da(pda) {}; + float width() const { return sa - si; } + float height() const { return da - di; } + float si; // min + float di; // min + float sa; // max + float da; // max +}; + +static SlantBox nullSlant(0, 0, 0, 0); + +class BBox +{ +public: + BBox(float pxi = 0, float pyi = 0., float pxa = 0., float pya = 0.) : xi(pxi), yi(pyi), xa(pxa), ya(pya) {}; + float width() const { return xa - xi; } + float height() const { return ya - yi; } + float xi; // min + float yi; // min + float xa; // max + float ya; // max +}; + +static BBox nullBBox(0, 0, 0, 0); + +class GlyphBox +{ + GlyphBox(const GlyphBox &); + GlyphBox & operator = (const GlyphBox &); + +public: + GlyphBox(uint8 numsubs, unsigned short bitmap, Rect *slanted) : _num(numsubs), _bitmap(bitmap), _slant(*slanted) {}; + + void addSubBox(int subindex, int boundary, Rect *val) { _subs[subindex * 2 + boundary] = *val; } + Rect &subVal(int subindex, int boundary) { return _subs[subindex * 2 + boundary]; } + const Rect &slant() const { return _slant; } + uint8 num() const { return _num; } + const Rect *subs() const { return _subs; } + +private: + uint8 _num; + unsigned short _bitmap; + Rect _slant; + Rect _subs[1]; +}; + class GlyphCache { class Loader; @@ -44,6 +95,8 @@ class GlyphCache GlyphCache(const GlyphCache&); GlyphCache& operator=(const GlyphCache&); + static const Rect nullRect; + public: GlyphCache(const Face & face, const uint32 face_options); ~GlyphCache(); @@ -54,12 +107,22 @@ public: const GlyphFace *glyph(unsigned short glyphid) const; //result may be changed by subsequent call with a different glyphid const GlyphFace *glyphSafe(unsigned short glyphid) const; + float getBoundingMetric(unsigned short glyphid, uint8 metric) const; + uint8 numSubBounds(unsigned short glyphid) const; + float getSubBoundingMetric(unsigned short glyphid, uint8 subindex, uint8 metric) const; + const Rect & slant(unsigned short glyphid) const { return _boxes[glyphid] ? _boxes[glyphid]->slant() : nullRect; } + const SlantBox & getBoundingSlantBox(unsigned short glyphid) const; + const BBox & getBoundingBBox(unsigned short glyphid) const; + const SlantBox & getSubBoundingSlantBox(unsigned short glyphid, uint8 subindex) const; + const BBox & getSubBoundingBBox(unsigned short glyphid, uint8 subindex) const; + bool check(unsigned short glyphid) const; CLASS_NEW_DELETE; private: const Loader * _glyph_loader; const GlyphFace * * _glyphs; + GlyphBox * * _boxes; unsigned short _num_glyphs, _num_attrs, _upem; @@ -83,10 +146,82 @@ unsigned short GlyphCache::unitsPerEm() const throw() return _upem; } +inline +bool GlyphCache::check(unsigned short glyphid) const +{ + return glyphid < _num_glyphs; +} + inline const GlyphFace *GlyphCache::glyphSafe(unsigned short glyphid) const { return glyphid < _num_glyphs ? glyph(glyphid) : NULL; } +inline +float GlyphCache::getBoundingMetric(unsigned short glyphid, uint8 metric) const +{ + if (glyphid >= _num_glyphs) return 0.; + switch (metric) { + case 0: return (float)(glyph(glyphid)->theBBox().bl.x); // x_min + case 1: return (float)(glyph(glyphid)->theBBox().bl.y); // y_min + case 2: return (float)(glyph(glyphid)->theBBox().tr.x); // x_max + case 3: return (float)(glyph(glyphid)->theBBox().tr.y); // y_max + case 4: return (float)(_boxes[glyphid] ? _boxes[glyphid]->slant().bl.x : 0.f); // sum_min + case 5: return (float)(_boxes[glyphid] ? _boxes[glyphid]->slant().bl.y : 0.f); // diff_min + case 6: return (float)(_boxes[glyphid] ? _boxes[glyphid]->slant().tr.x : 0.f); // sum_max + case 7: return (float)(_boxes[glyphid] ? _boxes[glyphid]->slant().tr.y : 0.f); // diff_max + default: return 0.; + } +} + +inline const SlantBox &GlyphCache::getBoundingSlantBox(unsigned short glyphid) const +{ + return _boxes[glyphid] ? *(SlantBox *)(&(_boxes[glyphid]->slant())) : nullSlant; +} + +inline const BBox &GlyphCache::getBoundingBBox(unsigned short glyphid) const +{ + return *(BBox *)(&(glyph(glyphid)->theBBox())); +} + +inline +float GlyphCache::getSubBoundingMetric(unsigned short glyphid, uint8 subindex, uint8 metric) const +{ + GlyphBox *b = _boxes[glyphid]; + if (b == NULL || subindex >= b->num()) return 0; + + switch (metric) { + case 0: return b->subVal(subindex, 0).bl.x; + case 1: return b->subVal(subindex, 0).bl.y; + case 2: return b->subVal(subindex, 0).tr.x; + case 3: return b->subVal(subindex, 0).tr.y; + case 4: return b->subVal(subindex, 1).bl.x; + case 5: return b->subVal(subindex, 1).bl.y; + case 6: return b->subVal(subindex, 1).tr.x; + case 7: return b->subVal(subindex, 1).tr.y; + default: return 0.; + } +} + +inline const SlantBox &GlyphCache::getSubBoundingSlantBox(unsigned short glyphid, uint8 subindex) const +{ + GlyphBox *b = _boxes[glyphid]; +// if (b == NULL || subindex >= b->num()) return nullSlant; + return *(SlantBox *)(b->subs() + 2 * subindex + 1); +} + +inline const BBox &GlyphCache::getSubBoundingBBox(unsigned short glyphid, uint8 subindex) const +{ + GlyphBox *b = _boxes[glyphid]; +// if (b == NULL || subindex >= b->num()) return nullBBox; + return *(BBox *)(b->subs() + 2 * subindex); +} + +inline +uint8 GlyphCache::numSubBounds(unsigned short glyphid) const +{ + return _boxes[glyphid] ? _boxes[glyphid]->num() : 0; +} + } // namespace graphite2 diff --git a/gfx/graphite2/src/inc/Intervals.h b/gfx/graphite2/src/inc/Intervals.h new file mode 100644 index 00000000000..bc9fab65ab1 --- /dev/null +++ b/gfx/graphite2/src/inc/Intervals.h @@ -0,0 +1,231 @@ +/* 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. +*/ +#pragma once + +#include + +#include "inc/Main.h" +#include "inc/List.h" +#include "inc/json.h" +#include "inc/Position.h" + +// An IntervalSet represents the possible movement of a given glyph in a given direction +// (horizontally, vertically, or diagonally). +// A vector is needed to represent disjoint ranges, eg, -300..-150, 20..200, 500..750. +// Each pair represents the min/max of a sub-range. + +namespace graphite2 { + +class Segment; + +enum zones_t {SD, XY}; + +class Zones +{ + struct Exclusion + { + template + static Exclusion weighted(float xmin, float xmax, float f, float a0, + float m, float xi, float ai, float c, bool nega); + + float x, // x position + xm, // xmax position + c, // constant + sum(MiXi^2) + sm, // sum(Mi) + smx; // sum(MiXi) + bool open; + + Exclusion(float x, float w, float smi, float smxi, float c); + Exclusion & operator += (Exclusion const & rhs); + uint8 outcode(float p) const; + + Exclusion split_at(float p); + void left_trim(float p); + + bool track_cost(float & cost, float & x, float origin) const; + + private: + float test_position(float origin) const; + float cost(float x) const; + }; + + typedef Vector exclusions; + + typedef exclusions::iterator iterator; + typedef Exclusion * pointer; + typedef Exclusion & reference; + typedef std::reverse_iterator reverse_iterator; + +public: + typedef exclusions::const_iterator const_iterator; + typedef Exclusion const * const_pointer; + typedef Exclusion const & const_reference; + typedef std::reverse_iterator const_reverse_iterator; + +#if !defined GRAPHITE2_NTRACING + struct Debug + { + Exclusion _excl; + bool _isdel; + Vector _env; + + Debug(Exclusion *e, bool isdel, json *dbg) : _excl(*e), _isdel(isdel), _env(dbg->getenvs()) { }; + }; + + typedef Vector debugs; + typedef debugs::const_iterator idebugs; + void addDebug(Exclusion *e); + void removeDebug(float pos, float posm); + void setdebug(json *dbgout) { _dbg = dbgout; } + idebugs dbgs_begin() const { return _dbgs.begin(); } + idebugs dbgs_end() const { return _dbgs.end(); } + void jsonDbgOut(Segment *seg) const; + Position position() const { return Position(_pos, _posm); } +#endif + + Zones(); + template + void initialise(float xmin, float xmax, float margin_len, float margin_weight, float ao); + + void exclude(float xmin, float xmax); + void exclude_with_margins(float xmin, float xmax, int axis); + + template + void weighted(float xmin, float xmax, float f, float a0, float mi, float xi, float ai, float c, bool nega); + void weightedAxis(int axis, float xmin, float xmax, float f, float a0, float mi, float xi, float ai, float c, bool nega); + + float closest( float origin, float &cost) const; + + const_iterator begin() const { return _exclusions.begin(); } + const_iterator end() const { return _exclusions.end(); } + +private: + exclusions _exclusions; +#if !defined GRAPHITE2_NTRACING + json * _dbg; + debugs _dbgs; +#endif + float _margin_len, + _margin_weight, + _pos, + _posm; + + void insert(Exclusion e); + void remove(float x, float xm); + const_iterator find_exclusion_under(float x) const; +}; + + +inline +Zones::Zones() +: _margin_len(0), _margin_weight(0), _pos(0), _posm(0) +{ + _exclusions.reserve(8); +} + +inline +Zones::Exclusion::Exclusion(float x_, float xm_, float smi, float smxi, float c_) +: x(x_), xm(xm_), c(c_), sm(smi), smx(smxi), open(false) +{ } + +template +inline +void Zones::initialise(float xmin, float xmax, float margin_len, + float margin_weight, float a0) +{ + _margin_len = margin_len; + _margin_weight = margin_weight; + _pos = xmin; + _posm = xmax; + _exclusions.clear(); + _exclusions.push_back(Exclusion::weighted(xmin, xmax, 1, a0, 0, 0, 0, 0, false)); + _exclusions.front().open = true; +#if !defined GRAPHITE2_NTRACING + _dbgs.clear(); +#endif +} + +inline +void Zones::exclude(float xmin, float xmax) { + remove(xmin, xmax); +} + +template +inline +void Zones::weighted(float xmin, float xmax, float f, float a0, + float m, float xi, float ai, float c, bool nega) { + insert(Exclusion::weighted(xmin, xmax, f, a0, m, xi, ai, c, nega)); +} + +inline +void Zones::weightedAxis(int axis, float xmin, float xmax, float f, float a0, + float m, float xi, float ai, float c, bool nega) { + if (axis < 2) + weighted(xmin, xmax, f, a0, m, xi, ai, c, nega); + else + weighted(xmin, xmax, f, a0, m, xi, ai, c, nega); +} + +#if !defined GRAPHITE2_NTRACING +inline +void Zones::addDebug(Exclusion *e) { + if (_dbg) + _dbgs.push_back(Debug(e, false, _dbg)); +} + +inline +void Zones::removeDebug(float pos, float posm) { + if (_dbg) + { + Exclusion e(pos, posm, 0, 0, 0); + _dbgs.push_back(Debug(&e, true, _dbg)); + } +} +#endif + +template<> +inline +Zones::Exclusion Zones::Exclusion::weighted(float xmin, float xmax, float f, float a0, + float m, float xi, GR_MAYBE_UNUSED float ai, float c, GR_MAYBE_UNUSED bool nega) { + return Exclusion(xmin, xmax, + m + f, + m * xi, + m * xi * xi + f * a0 * a0 + c); +} + +template<> +inline +Zones::Exclusion Zones::Exclusion::weighted(float xmin, float xmax, float f, float a0, + float m, float xi, float ai,float c, bool nega) { + float xia = nega ? xi - ai : xi + ai; + return Exclusion(xmin, xmax, + 0.25f * (m + 2.f * f), + 0.25f * m * xia, + 0.25f * (m * xia * xia + 2.f * f * a0 * a0) + c); +} + +} // end of namespace graphite2 diff --git a/gfx/graphite2/src/inc/List.h b/gfx/graphite2/src/inc/List.h index f99036912d8..41e51738c3e 100644 --- a/gfx/graphite2/src/inc/List.h +++ b/gfx/graphite2/src/inc/List.h @@ -70,6 +70,7 @@ public: size_t capacity() const{ return m_end - m_first; } void reserve(size_t n); + void resize(size_t n, const T & v = T()); reference front() { assert(size() > 0); return *begin(); } const_reference front() const { assert(size() > 0); return *begin(); } @@ -82,7 +83,7 @@ public: void assign(size_t n, const T& u) { clear(); insert(begin(), n, u); } void assign(const_iterator first, const_iterator last) { clear(); insert(begin(), first, last); } - iterator insert(iterator p, const T & x) { p = _insert_default(p, 1); *p = x; return p; } + iterator insert(iterator p, const T & x) { p = _insert_default(p, 1); new (p) T(x); return p; } void insert(iterator p, size_t n, const T & x); void insert(iterator p, const_iterator first, const_iterator last); void pop_back() { assert(size() > 0); --m_last; } @@ -109,13 +110,21 @@ void Vector::reserve(size_t n) } } +template +inline +void Vector::resize(size_t n, const T & v) { + const ptrdiff_t d = n-size(); + if (d < 0) erase(end()+d, end()); + else if (d > 0) insert(end(), d, v); +} + template inline typename Vector::iterator Vector::_insert_default(iterator p, size_t n) { assert(begin() <= p && p <= end()); const ptrdiff_t i = p - begin(); - reserve((size() + n + 7) >> 3 << 3); + reserve(((size() + n + 7) >> 3) << 3); p = begin() + i; // Move tail if there is one if (p != end()) memmove(p + n, p, distance(p,end())*sizeof(T)); diff --git a/gfx/graphite2/src/inc/Machine.h b/gfx/graphite2/src/inc/Machine.h index 21f8addbb44..4efb70cd946 100644 --- a/gfx/graphite2/src/inc/Machine.h +++ b/gfx/graphite2/src/inc/Machine.h @@ -110,7 +110,9 @@ enum opcode { PUSH_PROC_STATE, PUSH_VERSION, PUT_SUBS, PUT_SUBS2, PUT_SUBS3, PUT_GLYPH, PUSH_GLYPH_ATTR, PUSH_ATT_TO_GLYPH_ATTR, - MAX_OPCODE, + BITOR, BITAND, BITNOT, + BITSET, SET_FEAT, + MAX_OPCODE, // private opcodes for internal use only, comes after all other on disk opcodes TEMP_COPY = MAX_OPCODE }; @@ -148,7 +150,7 @@ public: SlotMap & slotMap() const throw(); status_t status() const throw(); - operator bool () const throw(); +// operator bool () const throw(); private: void check_final_stack(const stack_t * const sp); diff --git a/gfx/graphite2/src/inc/Main.h b/gfx/graphite2/src/inc/Main.h index 8f7c201b96b..8ff062a71d6 100644 --- a/gfx/graphite2/src/inc/Main.h +++ b/gfx/graphite2/src/inc/Main.h @@ -125,3 +125,8 @@ inline T max(const T a, const T b) #else #define GR_MAYBE_UNUSED #endif + +#ifdef _MSC_VER +#pragma warning(disable: 4800) +#pragma warning(once: 4355) +#endif diff --git a/gfx/graphite2/src/inc/Pass.h b/gfx/graphite2/src/inc/Pass.h index 33abbdce503..f0f9a5f1249 100644 --- a/gfx/graphite2/src/inc/Pass.h +++ b/gfx/graphite2/src/inc/Pass.h @@ -39,6 +39,11 @@ struct RuleEntry; struct State; class FiniteStateMachine; class Error; +class ShiftCollider; +class KernCollider; +class json; + +enum passtype; class Pass { @@ -46,10 +51,11 @@ public: Pass(); ~Pass(); - bool readPass(const byte * pPass, size_t pass_length, size_t subtable_base, Face & face, Error &e); - void runGraphite(vm::Machine & m, FiniteStateMachine & fsm) const; + bool readPass(const byte * pPass, size_t pass_length, size_t subtable_base, Face & face, + enum passtype pt, uint32 version, Error &e); + bool runGraphite(vm::Machine & m, FiniteStateMachine & fsm) const; void init(Silf *silf) { m_silf = silf; } - byte spaceContextuals() const { return (m_flags & 0x0E) >> 1; } + byte flags() const { return m_flags; } CLASS_NEW_DELETE private: @@ -61,7 +67,7 @@ private: const byte *precontext, const uint16 * sort_key, const uint16 * o_constraint, const byte *constraint_data, const uint16 * o_action, const byte * action_data, - Face &, Error &e); + Face &, enum passtype pt, Error &e); bool readStates(const byte * starts, const byte * states, const byte * o_rule_map, Face &, Error &e); bool readRanges(const byte * ranges, size_t num_ranges, Error &e); uint16 glyphToCol(const uint16 gid) const; @@ -69,14 +75,24 @@ private: void dumpRuleEventConsidered(const FiniteStateMachine & fsm, const RuleEntry & re) const; void dumpRuleEventOutput(const FiniteStateMachine & fsm, const Rule & r, Slot * os) const; void adjustSlot(int delta, Slot * & slot_out, SlotMap &) const; - const Silf* m_silf; - uint16 * m_cols; - Rule * m_rules; // rules - RuleEntry * m_ruleMap; - uint16 * m_startStates; // prectxt length - uint16 * m_transitions; - State * m_states; - + bool collisionShift(Segment *seg, int dir, json * const dbgout) const; + bool collisionKern(Segment *seg, int dir, json * const dbgout) const; + bool collisionFinish(Segment *seg, GR_MAYBE_UNUSED json * const dbgout) const; + bool resolveCollisions(Segment *seg, Slot *slot, Slot *start, ShiftCollider &coll, bool isRev, + int dir, bool &moved, bool &hasCol, json * const dbgout) const; + float resolveKern(Segment *seg, Slot *slot, Slot *start, KernCollider &coll, int dir, + float &ymin, float &ymax, json *const dbgout) const; + + const Silf * m_silf; + uint16 * m_cols; + Rule * m_rules; // rules + RuleEntry * m_ruleMap; + uint16 * m_startStates; // prectxt length + uint16 * m_transitions; + State * m_states; + vm::Machine::Code * m_codes; + byte * m_progs; + byte m_flags; byte m_iMaxLoop; uint16 m_numGlyphs; @@ -88,6 +104,7 @@ private: uint16 m_numColumns; byte m_minPreCtxt; byte m_maxPreCtxt; + byte m_colThreshold; vm::Machine::Code m_cPConstraint; private: //defensive diff --git a/gfx/graphite2/src/inc/Position.h b/gfx/graphite2/src/inc/Position.h index c6f1b755644..97bc1cdf7a8 100644 --- a/gfx/graphite2/src/inc/Position.h +++ b/gfx/graphite2/src/inc/Position.h @@ -50,7 +50,16 @@ public : Rect(const Position& botLeft, const Position& topRight): bl(botLeft), tr(topRight) {} Rect widen(const Rect& other) { return Rect(Position(bl.x > other.bl.x ? other.bl.x : bl.x, bl.y > other.bl.y ? other.bl.y : bl.y), Position(tr.x > other.tr.x ? tr.x : other.tr.x, tr.y > other.tr.y ? tr.y : other.tr.y)); } Rect operator + (const Position &a) const { return Rect(Position(bl.x + a.x, bl.y + a.y), Position(tr.x + a.x, tr.y + a.y)); } + Rect operator - (const Position &a) const { return Rect(Position(bl.x - a.x, bl.y - a.y), Position(tr.x - a.x, tr.y - a.y)); } Rect operator * (float m) const { return Rect(Position(bl.x, bl.y) * m, Position(tr.x, tr.y) * m); } + float width() const { return tr.x - bl.x; } + float height() const { return tr.y - bl.y; } + + bool hitTest(Rect &other); + + // returns Position(overlapx, overlapy) where overlap<0 if overlapping else positive) + Position overlap(Position &offset, Rect &other, Position &otherOffset); + //Position constrainedAvoid(Position &offset, Rect &box, Rect &sdbox, Position &other, Rect &obox, Rect &osdbox); Position bl; Position tr; diff --git a/gfx/graphite2/src/inc/Rule.h b/gfx/graphite2/src/inc/Rule.h index 8891ac86334..caec236274b 100644 --- a/gfx/graphite2/src/inc/Rule.h +++ b/gfx/graphite2/src/inc/Rule.h @@ -53,8 +53,6 @@ private: inline Rule::~Rule() { - delete constraint; - delete action; } diff --git a/gfx/graphite2/src/inc/Segment.h b/gfx/graphite2/src/inc/Segment.h index ef15e125ef2..b814c5f0f9e 100644 --- a/gfx/graphite2/src/inc/Segment.h +++ b/gfx/graphite2/src/inc/Segment.h @@ -40,6 +40,7 @@ of the License or (at your option) any later version. #include "inc/Position.h" #include "inc/List.h" #include "inc/Bidi.h" +#include "inc/Collider.h" #define MAX_SEG_GROWTH_FACTOR 256 @@ -86,6 +87,11 @@ class Segment Segment& operator=(const Segment&); public: + + enum { + SEG_INITCOLLISIONS = 1 + }; + unsigned int slotCount() const { return m_numGlyphs; } //one slot per glyph void extendLength(int num) { m_numGlyphs += num; } Position advance() const { return m_advance; } @@ -107,6 +113,8 @@ public: Slot * endSlot, const Slot * srcSlot, const size_t numGlyphs); #endif + uint8 flags() const { return m_flags; } + void flags(uint8 f) { m_flags = f; } Slot *first() { return m_first; } void first(Slot *p) { m_first = p; } Slot *last() { return m_last; } @@ -116,13 +124,20 @@ public: void freeSlot(Slot *); SlotJustify *newJustify(); void freeJustify(SlotJustify *aJustify); - Position positionSlots(const Font *font, Slot *first=0, Slot *last=0); + Position positionSlots(const Font *font=0, Slot *first=0, Slot *last=0, bool isFinal = true); void associateChars(int offset, int num); void linkClusters(Slot *first, Slot *last); uint16 getClassGlyph(uint16 cid, uint16 offset) const { return m_silf->getClassGlyph(cid, offset); } uint16 findClassIndex(uint16 cid, uint16 gid) const { return m_silf->findClassIndex(cid, gid); } int addFeatures(const Features& feats) { m_feats.push_back(feats); return m_feats.size() - 1; } uint32 getFeature(int index, uint8 findex) const { const FeatureRef* pFR=m_face->theSill().theFeatureMap().featureRef(findex); if (!pFR) return 0; else return pFR->getFeatureVal(m_feats[index]); } + void setFeature(int index, uint8 findex, uint32 val) { + const FeatureRef* pFR=m_face->theSill().theFeatureMap().featureRef(findex); + if (pFR) + { + if (val > pFR->maxVal()) val = pFR->maxVal(); + pFR->applyValToFeature(val, m_feats[index]); + } } void dir(int8 val) { m_dir = val; } unsigned int passBits() const { return m_passBits; } void mergePassBits(const unsigned int val) { m_passBits &= val; } @@ -141,14 +156,16 @@ public: bool hasJustification() const { return m_justifies.size() != 0; } bool isWhitespace(const int cid) const; + bool hasCollisionInfo() const { return m_collisions != 0; } + SlotCollision *collisionInfo(const Slot *s) const { return m_collisions ? m_collisions + s->index() : NULL; } CLASS_NEW_DELETE public: //only used by: GrSegment* makeAndInitialize(const GrFont *font, const GrFace *face, uint32 script, const FeaturesHandle& pFeats/*must not be IsNull*/, encform enc, const void* pStart, size_t nChars, int dir); bool read_text(const Face *face, const Features* pFeats/*must not be NULL*/, gr_encform enc, const void*pStart, size_t nChars); - void prepare_pos(const Font *font); void finalise(const Font *font); float justify(Slot *pSlot, const Font *font, float width, enum justFlags flags, Slot *pFirst, Slot *pLast); + bool initCollisions(); private: Position m_advance; // whole segment advance @@ -159,6 +176,7 @@ private: Slot * m_freeSlots; // linked list of free slots SlotJustify * m_freeJustifies; // Slot justification blocks free list CharInfo * m_charinfo; // character info, one per input character + SlotCollision * m_collisions; // Array of SlotCollisions for each slot const Face * m_face; // GrFace const Silf * m_silf; Slot * m_first; // first slot in segment @@ -169,6 +187,7 @@ private: m_passBits; // if bit set then skip pass int m_defaultOriginal; // number of whitespace chars in the string int8 m_dir; + uint8 m_flags; // General purpose flags }; @@ -179,7 +198,7 @@ void Segment::finalise(const Font *font) if (!m_first) return; m_advance = positionSlots(font); - associateChars(0, m_numCharinfo); + //associateChars(0, m_numCharinfo); linkClusters(m_first, m_last); } diff --git a/gfx/graphite2/src/inc/Shrinker.h b/gfx/graphite2/src/inc/Shrinker.h new file mode 100644 index 00000000000..e4db2f135a6 --- /dev/null +++ b/gfx/graphite2/src/inc/Shrinker.h @@ -0,0 +1,119 @@ +/* Copyright (c) 2012, Siyuan Fu + Copyright (c) 2015, SIL International + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + 3. Neither the name of the copyright holder nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + POSSIBILITY OF SUCH DAMAGE. +*/ + +#pragma once + +#include +#include +#include + +#include + +//the code from LZ4 +#if (GCC_VERSION >= 302) || (__INTEL_COMPILER >= 800) || defined(__clang__) +# define expect(expr,value) (__builtin_expect ((expr),(value)) ) +#else +# define expect(expr,value) (expr) +#endif +#define likely(expr) expect((expr) != 0, 1) +#define unlikely(expr) expect((expr) != 0, 0) +//////////////////// + + +namespace +{ + +#if defined(_MSC_VER) +typedef unsigned __int8 u8; +typedef unsigned __int16 u16; +typedef unsigned __int32 u32; +typedef unsigned __int64 u64; +#else +#include +typedef uint8_t u8; +typedef uint16_t u16; +typedef uint32_t u32; +typedef uint64_t u64; +#endif + +ptrdiff_t const MINMATCH = 4; + +template +inline +void unaligned_copy(void * d, void const * s) { + ::memcpy(d, s, S); +} + +inline +u8 * memcpy_nooverlap(u8 * d, u8 const * s, size_t n) { + size_t const WS = sizeof(unsigned long); + u8 const * e = s + n; + do + { + unaligned_copy(d, s); + d += WS; + s += WS; + } + while (s < e); + d-=(s-e); + + return d; +} + + +inline +u8 * memcpy_nooverlap_surpass(u8 * d, u8 const * s, size_t n) { + size_t const WS = sizeof(unsigned long); + size_t wn = n/WS; + while (wn--) + { + unaligned_copy(d, s); + d += WS; + s += WS; + } + n &= WS-1; + while (n--) {*d++ = *s++; } + + return d; +} + + +inline +u8 * memcpy_(u8 * d, u8 const * s, size_t n) { + if (likely(d>s+sizeof(unsigned long))) + return memcpy_nooverlap(d,s,n); + else while (n--) *d++ = *s++; + return d; +} + +} // end of anonymous namespace + + diff --git a/gfx/graphite2/src/inc/Silf.h b/gfx/graphite2/src/inc/Silf.h index c7e9b5d6b6a..a31d4bf35f9 100644 --- a/gfx/graphite2/src/inc/Silf.h +++ b/gfx/graphite2/src/inc/Silf.h @@ -85,6 +85,7 @@ public: uint8 aMirror() const {return m_aMirror; } uint8 aPassBits() const { return m_aPassBits; } uint8 aBidi() const { return m_aBidi; } + uint8 aCollision() const { return m_aCollision; } uint8 substitutionPass() const { return m_sPass; } uint8 positionPass() const { return m_pPass; } uint8 justificationPass() const { return m_jPass; } @@ -114,13 +115,10 @@ private: uint8 m_sPass, m_pPass, m_jPass, m_bPass, m_flags; - uint8 m_aPseudo, m_aBreak, m_aUser, m_aBidi, m_aMirror, m_aPassBits, - m_iMaxComp; - uint16 m_aLig, - m_numPseudo, - m_nClass, - m_nLinear, - m_gEndLine; + uint8 m_aPseudo, m_aBreak, m_aUser, m_aBidi, m_aMirror, m_aPassBits, + m_iMaxComp, m_aCollision; + uint16 m_aLig, m_numPseudo, m_nClass, m_nLinear, + m_gEndLine; gr_faceinfo m_silfinfo; void releaseBuffers() throw(); diff --git a/gfx/graphite2/src/inc/Slot.h b/gfx/graphite2/src/inc/Slot.h index 1847269d733..65ee5af8bc8 100644 --- a/gfx/graphite2/src/inc/Slot.h +++ b/gfx/graphite2/src/inc/Slot.h @@ -75,6 +75,7 @@ public: unsigned short gid() const { return m_glyphid; } Position origin() const { return m_position; } float advance() const { return m_advance.x; } + void advance(Position &val) { m_advance = val; } Position advancePos() const { return m_advance; } int before() const { return m_before; } int after() const { return m_after; } @@ -98,7 +99,7 @@ public: void after(int ind) { m_after = ind; } bool isBase() const { return (!m_parent); } void update(int numSlots, int numCharInfo, Position &relpos); - Position finalise(const Segment* seg, const Font* font, Position & base, Rect & bbox, uint8 attrLevel, float & clusterMin); + Position finalise(const Segment* seg, const Font* font, Position & base, Rect & bbox, uint8 attrLevel, float & clusterMin, bool isFinal); bool isDeleted() const { return (m_flags & DELETED) ? true : false; } void markDeleted(bool state) { if (state) m_flags |= DELETED; else m_flags &= ~DELETED; } bool isCopied() const { return (m_flags & COPIED) ? true : false; } @@ -122,8 +123,10 @@ public: Slot *attachedTo() const { return m_parent; } Position attachOffset() const { return m_attach - m_with; } Slot* firstChild() const { return m_child; } + void firstChild(Slot *ap) { m_child = ap; } bool child(Slot *ap); Slot* nextSibling() const { return m_sibling; } + void nextSibling(Slot *ap) { m_sibling = ap; } bool sibling(Slot *ap); bool removeChild(Slot *ap); bool removeSibling(Slot *ap); @@ -132,6 +135,8 @@ public: void floodShift(Position adj); float just() const { return m_just; } void just(float j) { m_just = j; } + Slot *nextInCluster(const Slot *s) const; + bool isChildOf(const Slot *base) const; CLASS_NEW_DELETE diff --git a/gfx/graphite2/src/inc/TtfUtil.h b/gfx/graphite2/src/inc/TtfUtil.h index b7b7ffee6c8..428dea09f13 100644 --- a/gfx/graphite2/src/inc/TtfUtil.h +++ b/gfx/graphite2/src/inc/TtfUtil.h @@ -137,11 +137,11 @@ public: ////////////////////////////////// cmap lookup tools const void * FindCmapSubtable(const void * pCmap, int nPlatformId = 3, int nEncodingId = 1, size_t length = 0); - bool CheckCmapSubtable4(const void * pCmap31); + bool CheckCmapSubtable4(const void * pCmap31 /*, unsigned int maxgid*/); gid16 CmapSubtable4Lookup(const void * pCmapSubtabel4, unsigned int nUnicodeId, int rangeKey = 0); unsigned int CmapSubtable4NextCodepoint(const void *pCmap31, unsigned int nUnicodeId, int * pRangeKey = 0); - bool CheckCmapSubtable12(const void *pCmap310); + bool CheckCmapSubtable12(const void *pCmap310 /*, unsigned int maxgid*/); gid16 CmapSubtable12Lookup(const void * pCmap310, unsigned int uUnicodeId, int rangeKey = 0); unsigned int CmapSubtable12NextCodepoint(const void *pCmap310, unsigned int nUnicodeId, int * pRangeKey = 0); diff --git a/gfx/graphite2/src/inc/bits.h b/gfx/graphite2/src/inc/bits.h index 908049f7285..615c6cba67f 100644 --- a/gfx/graphite2/src/inc/bits.h +++ b/gfx/graphite2/src/inc/bits.h @@ -29,15 +29,63 @@ of the License or (at your option) any later version. namespace graphite2 { + +#if defined GRAPHITE2_BUILTINS && (defined __GNUC__ || defined __clang__) + template inline unsigned int bit_set_count(T v) { - v = v - ((v >> 1) & T(~T(0)/3)); // temp - v = (v & T(~T(0)/15*3)) + ((v >> 2) & T(~T(0)/15*3)); // temp - v = (v + (v >> 4)) & T(~T(0)/255*15); // temp - return (T)(v * T(~T(0)/255)) >> (sizeof(T)-1)*8; // count + return __builtin_popcount(v); } +template<> +inline unsigned int bit_set_count(int16 v) +{ + return __builtin_popcount(static_cast(v)); +} + +template<> +inline unsigned int bit_set_count(int8 v) +{ + return __builtin_popcount(static_cast(v)); +} + +template<> +inline unsigned int bit_set_count(unsigned long v) +{ + return __builtin_popcountl(v); +} + +template<> +inline unsigned int bit_set_count(signed long v) +{ + return __builtin_popcountl(v); +} + +template<> +inline unsigned int bit_set_count(unsigned long long v) +{ + return __builtin_popcountll(v); +} + +template<> +inline unsigned int bit_set_count(signed long long v) +{ + return __builtin_popcountll(v); +} +#else + +template +inline unsigned int bit_set_count(T v) +{ + v = v - ((v >> 1) & T(~(0UL)/3)); // temp + v = (v & T(~(0UL)/15*3)) + ((v >> 2) & T(~(0UL)/15*3)); // temp + v = (v + (v >> 4)) & T(~(0UL)/255*15); // temp + return (T)(v * T(~(0UL)/255)) >> (sizeof(T)-1)*8; // count +} + +#endif + template inline unsigned long _mask_over_val(unsigned long v) @@ -87,4 +135,12 @@ inline T zero_bytes(const T x, unsigned char n) return T((has_zero(x^t) >> 7)*n); } +#if 0 +inline float float_round(float x, uint32 m) +{ + *reinterpret_cast(&x) &= m; + return *reinterpret_cast(&x); +} +#endif + } diff --git a/gfx/graphite2/src/inc/debug.h b/gfx/graphite2/src/inc/debug.h index a4438c9269d..97175eb2cc4 100644 --- a/gfx/graphite2/src/inc/debug.h +++ b/gfx/graphite2/src/inc/debug.h @@ -54,6 +54,7 @@ struct objectid json & operator << (json & j, const Position &) throw(); +json & operator << (json & j, const Rect &) throw(); json & operator << (json & j, const CharInfo &) throw(); json & operator << (json & j, const dslot &) throw(); json & operator << (json & j, const objectid &) throw(); @@ -68,6 +69,13 @@ json & operator << (json & j, const Position & p) throw() } +inline +json & operator << (json & j, const Rect & p) throw() +{ + return j << json::flat << json::array << p.bl.x << p.bl.y << p.tr.x << p.tr.y << json::close; +} + + inline json & operator << (json & j, const objectid & sid) throw() { diff --git a/gfx/graphite2/src/inc/json.h b/gfx/graphite2/src/inc/json.h index ac8ffbba66e..e9826832e9b 100644 --- a/gfx/graphite2/src/inc/json.h +++ b/gfx/graphite2/src/inc/json.h @@ -29,9 +29,11 @@ of the License or (at your option) any later version. // Author: Tim Eves #pragma once + #include "inc/Main.h" #include #include +#include "inc/List.h" namespace graphite2 { @@ -49,6 +51,7 @@ class json * _context, // current context (top of stack) * _flatten; // if !0 points to context above which // pretty printed output should occur. + Vector _env; void context(const char current) throw(); void indent(const int d=0) throw(); @@ -64,6 +67,10 @@ public: typedef bool boolean; static const _null_t null; + void setenv(unsigned int index, void *val) { _env.reserve(index + 1); if (index >= _env.size()) _env.insert(_env.end(), _env.size() - index + 1, 0); _env[index] = val; } + void *getenv(unsigned int index) const { return _env[index]; } + const Vector &getenvs() const { return _env; } + static void flat(json &) throw(); static void close(json &) throw(); static void object(json &) throw(); diff --git a/gfx/graphite2/src/inc/opcode_table.h b/gfx/graphite2/src/inc/opcode_table.h index 06916f6c875..73a99c82043 100644 --- a/gfx/graphite2/src/inc/opcode_table.h +++ b/gfx/graphite2/src/inc/opcode_table.h @@ -43,7 +43,7 @@ of the License or (at your option) any later version. // level - any byte static const opcode_t opcode_table[] = { - {{do2(nop)}, 0, "NOP"}, + {{do2(nop)}, 0, "NOP"}, {{do2(push_byte)}, 1, "PUSH_BYTE"}, // number {{do2(push_byte_u)}, 1, "PUSH_BYTE_U"}, // number @@ -114,6 +114,11 @@ static const opcode_t opcode_table[] = {{do_(put_glyph), NILOP}, 2, "PUT_GLYPH"}, // output_class output_class {{do2(push_glyph_attr)}, 3, "PUSH_GLYPH_ATTR"}, // gattrnum gattrnum slot {{do2(push_att_to_glyph_attr)}, 3, "PUSH_ATT_TO_GLYPH_ATTR"}, // gattrnum gattrnum slot + {{do2(bor)}, 0, "BITOR"}, + {{do2(band)}, 0, "BITAND"}, + {{do2(bnot)}, 0, "BITNOT"}, // 0x40 + {{do2(setbits)}, 4, "BITSET"}, + {{do2(set_feat)}, 2, "SET_FEAT"}, // private opcodes for internal use only, comes after all other on disk opcodes. {{do_(temp_copy), NILOP}, 0, "TEMP_COPY"} }; diff --git a/gfx/graphite2/src/inc/opcodes.h b/gfx/graphite2/src/inc/opcodes.h index 9ad35bfd373..1c36c301b64 100644 --- a/gfx/graphite2/src/inc/opcodes.h +++ b/gfx/graphite2/src/inc/opcodes.h @@ -241,20 +241,24 @@ ENDOP STARTOP(put_copy) declare_params(1); const int slot_ref = int8(*param); - if (is && (slot_ref ||is != *map)) + if (is) { - int16 *tempUserAttrs = is->userAttrs(); slotref ref = slotat(slot_ref); - if (ref) + if (ref && ref != is) { - memcpy(tempUserAttrs, ref->userAttrs(), seg.numAttrs() * sizeof(uint16)); + int16 *tempUserAttrs = is->userAttrs(); + if (is->attachedTo() || is->firstChild()) DIE Slot *prev = is->prev(); Slot *next = is->next(); - memcpy(is, slotat(slot_ref), sizeof(Slot)); + memcpy(tempUserAttrs, ref->userAttrs(), seg.numAttrs() * sizeof(uint16)); + memcpy(is, ref, sizeof(Slot)); + is->firstChild(NULL); + is->nextSibling(NULL); is->userAttrs(tempUserAttrs); is->next(next); is->prev(prev); - is->sibling(NULL); + if (is->attachedTo()) + is->attachedTo()->child(is); } is->markCopied(false); is->markDeleted(false); @@ -316,7 +320,7 @@ STARTOP(insert) ENDOP STARTOP(delete_) - if (!is) DIE + if (!is || is->isDeleted()) DIE is->markDeleted(true); if (is->prev()) is->prev()->next(is->next()); @@ -644,3 +648,37 @@ STARTOP(temp_copy) newSlot->markCopied(true); *map = newSlot; ENDOP + +STARTOP(band) + binop(&); +ENDOP + +STARTOP(bor) + binop(|); +ENDOP + +STARTOP(bnot) + *sp = ~*sp; +ENDOP + +STARTOP(setbits) + declare_params(4); + const uint16 m = uint16(param[0]) << 8 + | uint8(param[1]); + const uint16 v = uint16(param[2]) << 8 + | uint8(param[3]); + *sp = ((*sp) & ~m) | v; +ENDOP + +STARTOP(set_feat) + declare_params(2); + const unsigned int feat = uint8(param[0]); + const int slot_ref = int8(param[1]); + slotref slot = slotat(slot_ref); + if (slot) + { + uint8 fid = seg.charinfo(slot->original())->fid(); + seg.setFeature(fid, feat, pop()); + } +ENDOP + diff --git a/gfx/graphite2/src/json.cpp b/gfx/graphite2/src/json.cpp index 1f0652cb032..48a563ce01d 100644 --- a/gfx/graphite2/src/json.cpp +++ b/gfx/graphite2/src/json.cpp @@ -30,6 +30,7 @@ of the License or (at your option) any later version. #if !defined GRAPHITE2_NTRACING #include +#include #include "inc/json.h" using namespace graphite2; @@ -116,7 +117,20 @@ json & json::operator << (json::string s) throw() return *this; } -json & json::operator << (json::number f) throw() { context(seq); fprintf(_stream, "%g", f); return *this; } +json & json::operator << (json::number f) throw() +{ + context(seq); + if (std::numeric_limits::infinity() == f) + fputs("Infinity", _stream); + else if (-std::numeric_limits::infinity() == f) + fputs("-Infinity", _stream); + else if (std::numeric_limits::quiet_NaN() == f || + std::numeric_limits::signaling_NaN() == f) + fputs("NaN", _stream); + else + fprintf(_stream, "%g", f); + return *this; +} json & json::operator << (json::integer d) throw() { context(seq); fprintf(_stream, "%ld", d); return *this; } json & json::operator << (long unsigned d) throw() { context(seq); fprintf(_stream, "%ld", d); return *this; } json & json::operator << (json::boolean b) throw() { context(seq); fputs(b ? "true" : "false", _stream); return *this; } diff --git a/gfx/graphite2/src/moz.build b/gfx/graphite2/src/moz.build index adfc038ac61..fbcd0501d00 100644 --- a/gfx/graphite2/src/moz.build +++ b/gfx/graphite2/src/moz.build @@ -27,6 +27,8 @@ UNIFIED_SOURCES += [ 'CachedFace.cpp', 'CmapCache.cpp', 'Code.cpp', + 'Collider.cpp', + 'Decompressor.cpp', 'Face.cpp', 'FeatureMap.cpp', 'FileFace.cpp', @@ -40,10 +42,12 @@ UNIFIED_SOURCES += [ 'gr_logging.cpp', 'gr_segment.cpp', 'gr_slot.cpp', + 'Intervals.cpp', 'json.cpp', 'Justifier.cpp', 'NameTable.cpp', 'Pass.cpp', + 'Position.cpp', 'SegCache.cpp', 'SegCacheEntry.cpp', 'SegCacheStore.cpp',