gfx/graphite2/src/Justifier.cpp

changeset 0
6474c204b198
     1.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     1.2 +++ b/gfx/graphite2/src/Justifier.cpp	Wed Dec 31 06:09:35 2014 +0100
     1.3 @@ -0,0 +1,270 @@
     1.4 +/*  GRAPHITE2 LICENSING
     1.5 +
     1.6 +    Copyright 2012, SIL International
     1.7 +    All rights reserved.
     1.8 +
     1.9 +    This library is free software; you can redistribute it and/or modify
    1.10 +    it under the terms of the GNU Lesser General Public License as published
    1.11 +    by the Free Software Foundation; either version 2.1 of License, or
    1.12 +    (at your option) any later version.
    1.13 +
    1.14 +    This program is distributed in the hope that it will be useful,
    1.15 +    but WITHOUT ANY WARRANTY; without even the implied warranty of
    1.16 +    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
    1.17 +    Lesser General Public License for more details.
    1.18 +
    1.19 +    You should also have received a copy of the GNU Lesser General Public
    1.20 +    License along with this library in the file named "LICENSE".
    1.21 +    If not, write to the Free Software Foundation, 51 Franklin Street, 
    1.22 +    Suite 500, Boston, MA 02110-1335, USA or visit their web page on the 
    1.23 +    internet at http://www.fsf.org/licenses/lgpl.html.
    1.24 +
    1.25 +Alternatively, the contents of this file may be used under the terms of the
    1.26 +Mozilla Public License (http://mozilla.org/MPL) or the GNU General Public
    1.27 +License, as published by the Free Software Foundation, either version 2
    1.28 +of the License or (at your option) any later version.
    1.29 +*/
    1.30 +
    1.31 +#include "inc/Segment.h"
    1.32 +#include "graphite2/Font.h"
    1.33 +#include "inc/debug.h"
    1.34 +#include "inc/CharInfo.h"
    1.35 +#include "inc/Slot.h"
    1.36 +#include "inc/Main.h"
    1.37 +#include <math.h>
    1.38 +
    1.39 +using namespace graphite2;
    1.40 +
    1.41 +class JustifyTotal {
    1.42 +public:
    1.43 +    JustifyTotal() : m_numGlyphs(0), m_tStretch(0), m_tShrink(0), m_tStep(0), m_tWeight(0) {}
    1.44 +    void accumulate(Slot *s, Segment *seg, int level);
    1.45 +    int weight() const { return m_tWeight; }
    1.46 +
    1.47 +    CLASS_NEW_DELETE
    1.48 +
    1.49 +private:
    1.50 +    int m_numGlyphs;
    1.51 +    int m_tStretch;
    1.52 +    int m_tShrink;
    1.53 +    int m_tStep;
    1.54 +    int m_tWeight;
    1.55 +};
    1.56 +
    1.57 +void JustifyTotal::accumulate(Slot *s, Segment *seg, int level)
    1.58 +{
    1.59 +    ++m_numGlyphs;
    1.60 +    m_tStretch += s->getJustify(seg, level, 0);
    1.61 +    m_tShrink += s->getJustify(seg, level, 1);
    1.62 +    m_tStep += s->getJustify(seg, level, 2);
    1.63 +    m_tWeight += s->getJustify(seg, level, 3);
    1.64 +}
    1.65 +
    1.66 +float Segment::justify(Slot *pSlot, const Font *font, float width, GR_MAYBE_UNUSED justFlags flags, Slot *pFirst, Slot *pLast)
    1.67 +{
    1.68 +    Slot *s, *end;
    1.69 +    float currWidth = 0.0;
    1.70 +    const float scale = font ? font->scale() : 1.0f;
    1.71 +    Position res;
    1.72 +
    1.73 +    if (width < 0 && !(silf()->flags()))
    1.74 +        return width;
    1.75 +
    1.76 +    if (!pFirst) pFirst = pSlot;
    1.77 +    while (!pFirst->isBase()) pFirst = pFirst->attachedTo();
    1.78 +    if (!pLast) pLast = last();
    1.79 +    while (!pLast->isBase()) pLast = pLast->attachedTo();
    1.80 +    const float base = pFirst->origin().x / scale;
    1.81 +    width = width / scale;
    1.82 +    if ((flags & gr_justEndInline) == 0)
    1.83 +    {
    1.84 +        do {
    1.85 +            Rect bbox = theGlyphBBoxTemporary(pLast->glyph());
    1.86 +            if (bbox.bl.x != 0. || bbox.bl.y != 0. || bbox.tr.x != 0. || bbox.tr.y == 0.)
    1.87 +                break;
    1.88 +            pLast = pLast->prev();
    1.89 +        } while (pLast != pFirst);
    1.90 +    }
    1.91 +
    1.92 +    end = pLast->nextSibling();
    1.93 +    pFirst = pFirst->nextSibling();
    1.94 +
    1.95 +    int icount = 0;
    1.96 +    int numLevels = silf()->numJustLevels();
    1.97 +    if (!numLevels)
    1.98 +    {
    1.99 +        for (s = pSlot; s != end; s = s->next())
   1.100 +        {
   1.101 +            CharInfo *c = charinfo(s->before());
   1.102 +            if (isWhitespace(c->unicodeChar()))
   1.103 +            {
   1.104 +                s->setJustify(this, 0, 3, 1);
   1.105 +                s->setJustify(this, 0, 2, 1);
   1.106 +                s->setJustify(this, 0, 0, -1);
   1.107 +                ++icount;
   1.108 +            }
   1.109 +        }
   1.110 +        if (!icount)
   1.111 +        {
   1.112 +            for (s = pSlot; s != end; s = s->nextSibling())
   1.113 +            {
   1.114 +                s->setJustify(this, 0, 3, 1);
   1.115 +                s->setJustify(this, 0, 2, 1);
   1.116 +                s->setJustify(this, 0, 0, -1);
   1.117 +            }
   1.118 +        }
   1.119 +        ++numLevels;
   1.120 +    }
   1.121 +
   1.122 +    JustifyTotal *stats = new JustifyTotal[numLevels];
   1.123 +    if (!stats) return -1.0;
   1.124 +    for (s = pFirst; s != end; s = s->nextSibling())
   1.125 +    {
   1.126 +        float w = s->origin().x / scale + s->advance() - base;
   1.127 +        if (w > currWidth) currWidth = w;
   1.128 +        for (int j = 0; j < numLevels; ++j)
   1.129 +            stats[j].accumulate(s, this, j);
   1.130 +        s->just(0);
   1.131 +    }
   1.132 +
   1.133 +    for (int i = (width < 0.0) ? -1 : numLevels - 1; i >= 0; --i)
   1.134 +    {
   1.135 +        float diff;
   1.136 +        float error = 0.;
   1.137 +        float diffpw;
   1.138 +        int tWeight = stats[i].weight();
   1.139 +
   1.140 +        do {
   1.141 +            error = 0.;
   1.142 +            diff = width - currWidth;
   1.143 +            diffpw = diff / tWeight;
   1.144 +            tWeight = 0;
   1.145 +            for (s = pFirst; s != end; s = s->nextSibling()) // don't include final glyph
   1.146 +            {
   1.147 +                int w = s->getJustify(this, i, 3);
   1.148 +                float pref = diffpw * w + error;
   1.149 +                int step = s->getJustify(this, i, 2);
   1.150 +                if (!step) step = 1;        // handle lazy font developers
   1.151 +                if (pref > 0)
   1.152 +                {
   1.153 +                    float max = uint16(s->getJustify(this, i, 0));
   1.154 +                    if (i == 0) max -= s->just();
   1.155 +                    if (pref > max) pref = max;
   1.156 +                    else tWeight += w;
   1.157 +                }
   1.158 +                else
   1.159 +                {
   1.160 +                    float max = uint16(s->getJustify(this, i, 1));
   1.161 +                    if (i == 0) max += s->just();
   1.162 +                    if (-pref > max) pref = -max;
   1.163 +                    else tWeight += w;
   1.164 +                }
   1.165 +                int actual = step ? int(pref / step) * step : int(pref);
   1.166 +
   1.167 +                if (actual)
   1.168 +                {
   1.169 +                    error += diffpw * w - actual;
   1.170 +                    if (i == 0)
   1.171 +                        s->just(s->just() + actual);
   1.172 +                    else
   1.173 +                        s->setJustify(this, i, 4, actual);
   1.174 +                }
   1.175 +            }
   1.176 +            currWidth += diff - error;
   1.177 +        } while (i == 0 && int(abs(error)) > 0 && tWeight);
   1.178 +    }
   1.179 +
   1.180 +    Slot *oldFirst = m_first;
   1.181 +    Slot *oldLast = m_last;
   1.182 +    if (silf()->flags() & 1)
   1.183 +    {
   1.184 +        m_first = pSlot = addLineEnd(pSlot);
   1.185 +        m_last = pLast = addLineEnd(end);
   1.186 +        if (!m_first || !m_last) return -1.0;
   1.187 +    }
   1.188 +    else
   1.189 +    {
   1.190 +        m_first = pSlot;
   1.191 +        m_last = pLast;
   1.192 +    }
   1.193 +
   1.194 +    // run justification passes here
   1.195 +#if !defined GRAPHITE2_NTRACING
   1.196 +    json * const dbgout = m_face->logger();
   1.197 +    if (dbgout)
   1.198 +        *dbgout << json::object
   1.199 +                    << "justifies"  << objectid(this)
   1.200 +                    << "passes"     << json::array;
   1.201 +#endif
   1.202 +
   1.203 +    if (m_silf->justificationPass() != m_silf->positionPass() && (width >= 0. || (silf()->flags() & 1)))
   1.204 +        m_silf->runGraphite(this, m_silf->justificationPass(), m_silf->positionPass());
   1.205 +
   1.206 +#if !defined GRAPHITE2_NTRACING
   1.207 +    if (dbgout)
   1.208 +    {
   1.209 +        *dbgout     << json::item << json::close; // Close up the passes array
   1.210 +        positionSlots(NULL, pSlot, pLast);
   1.211 +        Slot *lEnd = pLast->nextSibling();
   1.212 +        *dbgout << "output" << json::array;
   1.213 +        for(Slot * t = pSlot; t != lEnd; t = t->next())
   1.214 +            *dbgout     << dslot(this, t);
   1.215 +        *dbgout         << json::close << json::close;
   1.216 +    }
   1.217 +#endif
   1.218 +
   1.219 +    res = positionSlots(font, pSlot, pLast);
   1.220 +
   1.221 +    if (silf()->flags() & 1)
   1.222 +    {
   1.223 +        delLineEnd(m_first);
   1.224 +        delLineEnd(m_last);
   1.225 +    }
   1.226 +    m_first = oldFirst;
   1.227 +    m_last = oldLast;
   1.228 +    return res.x;
   1.229 +}
   1.230 +
   1.231 +Slot *Segment::addLineEnd(Slot *nSlot)
   1.232 +{
   1.233 +    Slot *eSlot = newSlot();
   1.234 +    if (!eSlot) return NULL;
   1.235 +    const uint16 gid = silf()->endLineGlyphid();
   1.236 +    const GlyphFace * theGlyph = m_face->glyphs().glyphSafe(gid);
   1.237 +    eSlot->setGlyph(this, gid, theGlyph);
   1.238 +    if (nSlot)
   1.239 +    {
   1.240 +        eSlot->next(nSlot);
   1.241 +        eSlot->prev(nSlot->prev());
   1.242 +        nSlot->prev(eSlot);
   1.243 +        eSlot->before(nSlot->before());
   1.244 +        if (eSlot->prev())
   1.245 +            eSlot->after(eSlot->prev()->after());
   1.246 +        else
   1.247 +            eSlot->after(nSlot->before());
   1.248 +    }
   1.249 +    else
   1.250 +    {
   1.251 +        nSlot = m_last;
   1.252 +        eSlot->prev(nSlot);
   1.253 +        nSlot->next(eSlot);
   1.254 +        eSlot->after(eSlot->prev()->after());
   1.255 +        eSlot->before(nSlot->after());
   1.256 +    }
   1.257 +    return eSlot;
   1.258 +}
   1.259 +
   1.260 +void Segment::delLineEnd(Slot *s)
   1.261 +{
   1.262 +    Slot *nSlot = s->next();
   1.263 +    if (nSlot)
   1.264 +    {
   1.265 +        nSlot->prev(s->prev());
   1.266 +        if (s->prev())
   1.267 +            s->prev()->next(nSlot);
   1.268 +    }
   1.269 +    else
   1.270 +        s->prev()->next(NULL);
   1.271 +    freeSlot(s);
   1.272 +}
   1.273 +

mercurial