Exemplo n.º 1
0
        public Layout get(LayoutCacheKey key, LayoutContext ctx, FontCollection collection)
        {
            Layout layout = mCache.get(key);

            if (layout == null)
            {
                key.copyText();
                layout = new Layout();
                key.doLayout(layout, ctx, collection);
                mCache.put(key, layout);
            }
            return(layout);
        }
Exemplo n.º 2
0
        public void doLayout(UInt16 buf, int start, int count, int bufSize, bool isRtl, FontStyle style, MinikinPaint paint, FontCollection collection)
        {
            std::lock_guard <std::recursive_mutex> _l = new std::lock_guard <std::recursive_mutex>(gMinikinLock);

            LayoutContext ctx = new LayoutContext();

            //C++ TO C# CONVERTER TODO TASK: The following line was determined to be a copy assignment (rather than a reference assignment) - this should be verified and a 'CopyFrom' method should be created:
            //ORIGINAL LINE: ctx.style = style;
            ctx.style.CopyFrom(style);
            //C++ TO C# CONVERTER TODO TASK: The following line was determined to be a copy assignment (rather than a reference assignment) - this should be verified and a 'CopyFrom' method should be created:
            //ORIGINAL LINE: ctx.paint = paint;
            ctx.paint.CopyFrom(paint);

            reset();
            mAdvances.resize(count, 0);

            doLayoutRunCached(buf, start, count, bufSize, isRtl, ctx, start, collection, this, null);

            ctx.clearHbFonts();
        }
Exemplo n.º 3
0
        // Lay out a single bidi run
        public void doLayoutRun(UInt16[] buf, int start, int count, int bufSize, bool isRtl, LayoutContext ctx, FontCollection collection)
        {
            hb_buffer_t buffer = LayoutEngine.getInstance().hbBuffer;
            vector <FontCollection.Run> items = new vector <FontCollection.Run>();

            collection.itemize(buf + start, count, ctx.style, items);

            vector <hb_feature_t> features = new vector <hb_feature_t>();

            // Disable default-on non-required ligature features if letter-spacing
            // See http://dev.w3.org/csswg/css-text-3/#letter-spacing-property
            // "When the effective spacing between two characters is not zero (due to
            // either justification or a non-zero value of letter-spacing), user agents
            // should not apply optional ligatures."
            if (Math.Abs(ctx.paint.letterSpacing) > 0.03)
            {
                hb_feature_t no_liga = new hb_feature_t(HB_TAG('l', 'i', 'g', 'a'), 0, 0, ~0u);
                hb_feature_t no_clig = new hb_feature_t(HB_TAG('c', 'l', 'i', 'g'), 0, 0, ~0u);
                features.push_back(no_liga);
                features.push_back(no_clig);
            }
            addFeatures(ctx.paint.fontFeatureSettings, features);

            double size   = ctx.paint.size;
            double scaleX = ctx.paint.scaleX;

            float x = mAdvance;
            float y = 0F;

            for (int run_ix = isRtl ? items.size() - 1 : 0; isRtl?run_ix >= 0 : run_ix < (int)items.size(); isRtl ?--run_ix :++run_ix)
            {
                FontCollection.Run run = items[run_ix];
                if (run.fakedFont.font == null)
                {
                    ALOGE("no font for run starting u+%04x length %d", buf[run.start], run.end - run.start);
                    continue;
                }
                int font_ix = findFace(run.fakedFont, ctx);
                ctx.paint.font   = mFaces[font_ix].font;
                ctx.paint.fakery = mFaces[font_ix].fakery;
                HarfBuzzSharp.Font hbFont = ctx.hbFonts[font_ix];
        #if VERBOSE_DEBUG
                ALOGD("Run %zu, font %d [%d:%d]", run_ix, font_ix, run.start, run.end);
        #endif

                hb_font_set_ppem(hbFont, size * scaleX, size);
                hb_font_set_scale(hbFont, HBFloatToFixed(size * scaleX), HBFloatToFixed(size));

                bool is_color_bitmap_font = isColorBitmapFont(hbFont);

                // TODO: if there are multiple scripts within a font in an RTL run,
                // we need to reorder those runs. This is unlikely with our current
                // font stack, but should be done for correctness.

                // Note: scriptRunStart and scriptRunEnd, as well as run.start and run.end,
                // run between 0 and count.
                uint scriptRunEnd;
                for (uint scriptRunStart = run.start; scriptRunStart < run.end; scriptRunStart = scriptRunEnd)
                {
                    scriptRunEnd = scriptRunStart;
                    hb_script_t script = getScriptRun(buf + start, run.end, ref scriptRunEnd);
                    // After the last line, scriptRunEnd is guaranteed to have increased,
                    // since the only time getScriptRun does not increase its iterator is when
                    // it has already reached the end of the buffer. But that can't happen,
                    // since if we have already reached the end of the buffer, we should have
                    // had (scriptRunEnd == run.end), which means (scriptRunStart == run.end)
                    // which is impossible due to the exit condition of the for loop. So we
                    // can be sure that scriptRunEnd > scriptRunStart.

                    double letterSpace          = 0.0;
                    double letterSpaceHalfLeft  = 0.0;
                    double letterSpaceHalfRight = 0.0;

                    if (ctx.paint.letterSpacing != 0.0 && isScriptOkForLetterspacing(new hb_script_t(script)))
                    {
                        letterSpace = ctx.paint.letterSpacing * size * scaleX;
                        if ((ctx.paint.paintFlags & LinearTextFlag) == 0)
                        {
                            letterSpace         = Math.Round(letterSpace);
                            letterSpaceHalfLeft = Math.Floor(letterSpace * 0.5);
                        }
                        else
                        {
                            letterSpaceHalfLeft = letterSpace * 0.5;
                        }
                        letterSpaceHalfRight = letterSpace - letterSpaceHalfLeft;
                    }

                    hb_buffer_clear_contents(buffer);
                    hb_buffer_set_script(buffer, script);
                    hb_buffer_set_direction(buffer, isRtl ? HB_DIRECTION_RTL : HB_DIRECTION_LTR);
                    FontLanguages langList = FontLanguageListCache.getById(ctx.style.getLanguageListId());
                    if (langList.size() != 0)
                    {
                        FontLanguage hbLanguage = langList[0];
                        for (int i = 0; i < langList.size(); ++i)
                        {
                            if (langList[i].supportsHbScript(script))
                            {
                                hbLanguage = langList[i];
                                break;
                            }
                        }
                        hb_buffer_set_language(buffer, hbLanguage.getHbLanguage());
                    }

                    uint clusterStart = addToHbBuffer(buffer, new UInt16(buf), start, count, bufSize, scriptRunStart, scriptRunEnd, ctx.paint.hyphenEdit, hbFont);

                    hb_shape(hbFont, buffer, features.empty() ? null : features[0], features.size());
                    uint numGlyphs;
                    hb_glyph_info_t[]   info      = hb_buffer_get_glyph_infos(buffer, numGlyphs);
                    hb_glyph_position_t positions = hb_buffer_get_glyph_positions(buffer, null);

                    // At this point in the code, the cluster values in the info buffer
                    // correspond to the input characters with some shift. The cluster value
                    // clusterStart corresponds to the first character passed to HarfBuzz,
                    // which is at buf[start + scriptRunStart] whose advance needs to be saved
                    // into mAdvances[scriptRunStart]. So cluster values need to be reduced by
                    // (clusterStart - scriptRunStart) to get converted to indices of
                    // mAdvances.
                    uint clusterOffset = clusterStart - scriptRunStart;

                    if (numGlyphs != 0)
                    {
                        mAdvances[info[0].cluster - clusterOffset] += letterSpaceHalfLeft;
                        x += letterSpaceHalfLeft;
                    }
                    for (uint i = 0; i < numGlyphs; i++)
                    {
        #if VERBOSE_DEBUG
                        ALOGD("%d %d %d %d", positions[i].x_advance, positions[i].y_advance, positions[i].x_offset, positions[i].y_offset);
                        ALOGD("DoLayout %u: %f; %d, %d", info[i].codepoint, HBFixedToFloat(positions[i].x_advance), positions[i].x_offset, positions[i].y_offset);
        #endif
                        if (i > 0 && info[i - 1].cluster != info[i].cluster)
                        {
                            mAdvances[info[i - 1].cluster - clusterOffset] += letterSpaceHalfRight;
                            mAdvances[info[i].cluster - clusterOffset]     += letterSpaceHalfLeft;
                            x += letterSpace;
                        }

                        hb_codepoint_t glyph_ix = info[i].codepoint;
                        float          xoff     = HBFixedToFloat(positions[i].x_offset);
                        float          yoff     = -HBFixedToFloat(positions[i].y_offset);
                        xoff += yoff * ctx.paint.skewX;
                        LayoutGlyph glyph = new LayoutGlyph(font_ix, glyph_ix, x + xoff, y + yoff, (uint)(info[i].cluster - clusterOffset));
                        mGlyphs.push_back(glyph);
                        float xAdvance = HBFixedToFloat(positions[i].x_advance);
                        if ((ctx.paint.paintFlags & LinearTextFlag) == 0)
                        {
                            xAdvance = roundf(xAdvance);
                        }
                        MinikinRect        glyphBounds = new MinikinRect();
                        hb_glyph_extents_t extents     = new hb_glyph_extents_t();
                        if (is_color_bitmap_font && hb_font_get_glyph_extents(hbFont, glyph_ix, extents))
                        {
                            // Note that it is technically possible for a TrueType font to have
                            // outline and embedded bitmap at the same time. We ignore modified
                            // bbox of hinted outline glyphs in that case.
                            glyphBounds.mLeft   = roundf(HBFixedToFloat(extents.x_bearing));
                            glyphBounds.mTop    = roundf(HBFixedToFloat(-extents.y_bearing));
                            glyphBounds.mRight  = roundf(HBFixedToFloat(extents.x_bearing + extents.width));
                            glyphBounds.mBottom = roundf(HBFixedToFloat(-extents.y_bearing - extents.height));
                        }
                        else
                        {
                            ctx.paint.font.GetBounds(glyphBounds, glyph_ix, ctx.paint);
                        }
                        glyphBounds.offset(x + xoff, y + yoff);
                        mBounds.join(glyphBounds);
                        if ((int)(info[i].cluster - clusterOffset) < count)
                        {
                            mAdvances[info[i].cluster - clusterOffset] += xAdvance;
                        }
                        else
                        {
                            ALOGE("cluster %zu (start %zu) out of bounds of count %zu", info[i].cluster - clusterOffset, start, count);
                        }
                        x += xAdvance;
                    }
                    if (numGlyphs != 0)
                    {
                        mAdvances[info[numGlyphs - 1].cluster - clusterOffset] += letterSpaceHalfRight;
                        x += letterSpaceHalfRight;
                    }
                }
            }
            mAdvance = x;
        }
Exemplo n.º 4
0
        // Lay out a single word
        public float doLayoutWord(UInt16[] buf, int start, int count, int bufSize, bool isRtl, LayoutContext ctx, int bufStart, FontCollection collection, Layout layout, float[] advances)
        {
            LayoutCache    cache = LayoutEngine.getInstance().layoutCache.functorMethod;
            LayoutCacheKey key   = new LayoutCacheKey(collection, ctx.paint, new FontStyle(ctx.style), new UInt16(buf), start, count, bufSize, isRtl);

            float wordSpacing = count == 1 && isWordSpace(buf[start]) ? ctx.paint.wordSpacing : 0F;

            float advance;

            if (ctx.paint.skipCache())
            {
                Layout layoutForWord = new Layout();
                key.doLayout(layoutForWord, ctx, collection);
                if (layout != null)
                {
                    layout.appendLayout(layoutForWord, bufStart, wordSpacing);
                }
                if (advances != 0F)
                {
                    layoutForWord.getAdvances(advances);
                }
                advance = layoutForWord.getAdvance();
            }
            else
            {
                Layout layoutForWord = cache.get(key, ctx, collection);
                if (layout != null)
                {
                    layout.appendLayout(layoutForWord, bufStart, wordSpacing);
                }
                if (advances != 0F)
                {
                    layoutForWord.getAdvances(advances);
                }
                advance = layoutForWord.getAdvance();
            }

            if (wordSpacing != 0F)
            {
                advance += wordSpacing;
                if (advances != 0F)
                {
                    advances[0] += wordSpacing;
                }
            }
            return(advance);
        }
Exemplo n.º 5
0
        // Lay out a single bidi run
        // When layout is not null, layout info will be stored in the object.
        // When advances is not null, measurement results will be stored in the array.
        public float doLayoutRunCached(UInt16 buf, int start, int count, int bufSize, bool isRtl, LayoutContext ctx, int dstStart, FontCollection collection, Layout layout, ref float advances)
        {
            uint  originalHyphen = ctx.paint.hyphenEdit.getHyphen();
            float advance        = 0F;

            if (!isRtl)
            {
                // left to right
                int wordstart = start == bufSize != null ? start : getPrevWordBreakForCache(buf, start + 1, bufSize);
                int wordend   = new int();
                for (int iter = start; iter < start + count; iter = wordend)
                {
                    wordend = getNextWordBreakForCache(buf, iter, bufSize);
                    // Only apply hyphen to the first or last word in the string.
                    uint hyphen = new uint(originalHyphen);
                    if (iter != start)
                    { // Not the first word
                        hyphen &= ~HyphenEdit.MASK_START_OF_LINE;
                    }
                    if (wordend < start + count)
                    { // Not the last word
                        hyphen &= ~HyphenEdit.MASK_END_OF_LINE;
                    }
                    ctx.paint.hyphenEdit = hyphen;
                    int wordcount = Math.Min(start + count, wordend) - iter;
                    advance += doLayoutWord(buf + wordstart, iter - wordstart, wordcount, wordend - wordstart, isRtl, ctx, iter - dstStart, collection, layout, advances != 0 ? advances + (iter - start) : advances);
                    //C++ TO C# CONVERTER TODO TASK: The following line was determined to be a copy assignment (rather than a reference assignment) - this should be verified and a 'CopyFrom' method should be created:
                    //ORIGINAL LINE: wordstart = wordend;
                    wordstart.CopyFrom(wordend);
                }
            }
            else
            {
                // right to left
                int wordstart = new int();
                int end       = start + count;
                int wordend   = end == 0 ? 0 : getNextWordBreakForCache(buf, end - 1, bufSize);
                for (int iter = end; iter > start; iter = wordstart)
                {
                    wordstart = getPrevWordBreakForCache(buf, iter, bufSize);
                    // Only apply hyphen to the first (rightmost) or last (leftmost) word in
                    // the string.
                    uint hyphen = new uint(originalHyphen);
                    if (wordstart > start)
                    { // Not the first word
                        hyphen &= ~HyphenEdit.MASK_START_OF_LINE;
                    }
                    if (iter != end)
                    { // Not the last word
                        hyphen &= ~HyphenEdit.MASK_END_OF_LINE;
                    }
                    ctx.paint.hyphenEdit = hyphen;
                    int bufStart = Math.Max(start, wordstart);
                    advance += doLayoutWord(buf + wordstart, bufStart - wordstart, iter - bufStart, wordend - wordstart, isRtl, ctx, bufStart - dstStart, collection, layout, advances != 0 ? advances + (bufStart - start) : advances);
                    //C++ TO C# CONVERTER TODO TASK: The following line was determined to be a copy assignment (rather than a reference assignment) - this should be verified and a 'CopyFrom' method should be created:
                    //ORIGINAL LINE: wordend = wordstart;
                    wordend.CopyFrom(wordstart);
                }
            }
            return(advance);
        }
Exemplo n.º 6
0
//C++ TO C# CONVERTER WARNING: 'const' methods are not available in C#:
//ORIGINAL LINE: void doLayout(Layout* layout, LayoutContext* ctx, const FontCollection*& collection) const
        public void doLayout(Layout layout, LayoutContext ctx, FontCollection collection)
        {
            layout.mAdvances.resize(mCount, 0);
            ctx.clearHbFonts();
            layout.doLayoutRun(mChars, mStart, mCount, mNchars, mIsRtl, ctx, collection);
        }
Exemplo n.º 7
0
        // TODO: this class is actually fairly close to being general and not tied to
        // using Minikin to do the shaping of the strings. The main thing that would
        // need to be changed is having some kind of callback (or virtual class, or
        // maybe even template), which could easily be instantiated with Minikin's
        // Layout. Future work for when needed.
        public float addStyleRun(MinikinPaint paint, FontCollection typeface, FontStyle style, int start, int end, bool isRtl)
        {
            float width     = 0.0f;
            int   bidiFlags = isRtl ? kBidi_Force_RTL : kBidi_Force_LTR;

            float hyphenPenalty = 0.0F;

            if (paint != null)
            {
                width = Layout.measureText(mTextBuf.data(), start, end - start, mTextBuf.size(), bidiFlags, style, paint, typeface, mCharWidths.data() + start);

                // a heuristic that seems to perform well
                hyphenPenalty = 0.5 * paint.size * paint.scaleX * mLineWidths.getLineWidth(0);
                if (mHyphenationFrequency == kHyphenationFrequency_Normal)
                {
                    hyphenPenalty *= 4.0; // TODO: Replace with a better value after some testing
                }

                if (mJustified)
                {
                    // Make hyphenation more aggressive for fully justified text (so that
                    // "normal" in justified mode is the same as "full" in ragged-right).
                    hyphenPenalty *= 0.25;
                }
                else
                {
                    // Line penalty is zero for justified text.
                    mLinePenalty = Math.Max(mLinePenalty, hyphenPenalty * LINE_PENALTY_MULTIPLIER);
                }
            }

            int       current        = (int)mWordBreaker.current();
            int       afterWord      = start;
            int       lastBreak      = start;
            ParaWidth lastBreakWidth = mWidth;
            ParaWidth postBreak      = mWidth;
            int       postSpaceCount = mSpaceCount;

            for (int i = start; i < end; i++)
            {
                UInt16 c = mTextBuf[i];
                if (c == CHAR_TAB)
                {
                    mWidth = mPreBreak + mTabStops.nextTab(mWidth - mPreBreak);
                    if (mFirstTabIndex == INT_MAX)
                    {
                        mFirstTabIndex = (int)i;
                    }
                    // fall back to greedy; other modes don't know how to deal with tabs
                    mStrategy = kBreakStrategy_Greedy;
                }
                else
                {
                    if (isWordSpace(new UInt16(c)))
                    {
                        mSpaceCount += 1;
                    }
                    mWidth += mCharWidths[i];
                    if (!isLineEndSpace(new UInt16(c)))
                    {
                        postBreak      = mWidth;
                        postSpaceCount = mSpaceCount;
                        //C++ TO C# CONVERTER TODO TASK: The following line was determined to be a copy assignment (rather than a reference assignment) - this should be verified and a 'CopyFrom' method should be created:
                        //ORIGINAL LINE: afterWord = i + 1;
                        afterWord.CopyFrom(i + 1);
                    }
                }
                if (i + 1 == current != null)
                {
                    int wordStart = mWordBreaker.wordStart();
                    int wordEnd   = mWordBreaker.wordEnd();
                    if (paint != null && mHyphenator != null && mHyphenationFrequency != kHyphenationFrequency_None && wordStart >= start != null && wordEnd > wordStart && wordEnd - wordStart <= LONGEST_HYPHENATED_WORD)
                    {
                        mHyphenator.hyphenate(mHyphBuf, mTextBuf[wordStart], wordEnd - wordStart, mLocale);
        #if VERBOSE_DEBUG
                        string hyphenatedString;
                        for (int j = wordStart; j < wordEnd; j++)
                        {
                            if (mHyphBuf[j - wordStart] == HyphenationType.BREAK_AND_INSERT_HYPHEN)
                            {
                                hyphenatedString.push_back('-');
                            }
                            // Note: only works with ASCII, should do UTF-8 conversion here
                            hyphenatedString.push_back(buffer()[j]);
                        }
                        ALOGD("hyphenated string: %s", hyphenatedString);
        #endif

                        // measure hyphenated substrings
                        for (int j = wordStart; j < wordEnd; j++)
                        {
                            HyphenationType hyph = mHyphBuf[j - wordStart];
                            if (hyph != HyphenationType.DONT_BREAK)
                            {
                                paint.hyphenEdit = HyphenEdit.editForThisLine(hyph);
                                float     firstPartWidth = Layout.measureText(mTextBuf.data(), lastBreak, j - lastBreak, mTextBuf.size(), bidiFlags, style, paint, typeface, null);
                                ParaWidth hyphPostBreak  = lastBreakWidth + firstPartWidth;

                                paint.hyphenEdit = HyphenEdit.editForNextLine(hyph);
                                float     secondPartWidth = Layout.measureText(mTextBuf.data(), j, afterWord - j, mTextBuf.size(), bidiFlags, style, paint, typeface, null);
                                ParaWidth hyphPreBreak    = postBreak - secondPartWidth;

                                addWordBreak(j, hyphPreBreak, hyphPostBreak, postSpaceCount, postSpaceCount, hyphenPenalty, hyph);

                                paint.hyphenEdit = HyphenEdit.NO_EDIT;
                            }
                        }
                    }

                    // Skip break for zero-width characters inside replacement span
                    if (paint != null || current == end || mCharWidths[current] > 0)
                    {
                        float penalty = hyphenPenalty * mWordBreaker.breakBadness();
                        addWordBreak(current, mWidth, postBreak, mSpaceCount, postSpaceCount, penalty, HyphenationType.DONT_BREAK);
                    }
                    //C++ TO C# CONVERTER TODO TASK: The following line was determined to be a copy assignment (rather than a reference assignment) - this should be verified and a 'CopyFrom' method should be created:
                    //ORIGINAL LINE: lastBreak = current;
                    lastBreak.CopyFrom(current);
                    lastBreakWidth = mWidth;
                    //C++ TO C# CONVERTER TODO TASK: The following line was determined to be a copy assignment (rather than a reference assignment) - this should be verified and a 'CopyFrom' method should be created:
                    //ORIGINAL LINE: current = (int)mWordBreaker.next();
                    current.CopyFrom((int)mWordBreaker.next());
                }
            }

            return(width);
        }