void Canvas::drawText(const uint16_t* text, int start, int count, int contextCount,
        float x, float y, int bidiFlags, const Paint& origPaint, Typeface* typeface) {
    // minikin may modify the original paint
    Paint paint(origPaint);

    Layout layout;
    MinikinUtils::doLayout(&layout, &paint, bidiFlags, typeface, text, start, count, contextCount);

    size_t nGlyphs = layout.nGlyphs();
    std::unique_ptr<uint16_t[]> glyphs(new uint16_t[nGlyphs]);
    std::unique_ptr<float[]> pos(new float[nGlyphs * 2]);

    x += MinikinUtils::xOffsetForTextAlign(&paint, layout);

    MinikinRect bounds;
    layout.getBounds(&bounds);
    if (!drawTextAbsolutePos()) {
        bounds.offset(x, y);
    }

    // Set align to left for drawing, as we don't want individual
    // glyphs centered or right-aligned; the offset above takes
    // care of all alignment.
    paint.setTextAlign(Paint::kLeft_Align);

    DrawTextFunctor f(layout, this, glyphs.get(), pos.get(),
            paint, x, y, bounds, layout.getAdvance());
    MinikinUtils::forFontRun(layout, &paint, f);
}
void MinikinRect::join(const MinikinRect& r) {
    if (isEmpty()) {
        set(r);
    } else if (!r.isEmpty()) {
        mLeft = std::min(mLeft, r.mLeft);
        mTop = std::min(mTop, r.mTop);
        mRight = std::max(mRight, r.mRight);
        mBottom = std::max(mBottom, r.mBottom);
    }
}
void Layout::doLayoutRun(const uint16_t* buf, size_t start, size_t count, size_t bufSize,
        bool isRtl, LayoutContext* ctx) {
    hb_buffer_t* buffer = LayoutEngine::getInstance().hbBuffer;
    vector<FontCollection::Run> items;
    mCollection->itemize(buf + start, count, ctx->style, &items);
    if (isRtl) {
        std::reverse(items.begin(), items.end());
    }

    vector<hb_feature_t> features;
    // 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 (fabs(ctx->paint.letterSpacing) > 0.03)
    {
        static const hb_feature_t no_liga = { HB_TAG('l', 'i', 'g', 'a'), 0, 0, ~0u };
        static const hb_feature_t no_clig = { 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;
    double letterSpace = ctx->paint.letterSpacing * size * scaleX;
    double letterSpaceHalfLeft;
    if ((ctx->paint.paintFlags & LinearTextFlag) == 0) {
        letterSpace = round(letterSpace);
        letterSpaceHalfLeft = floor(letterSpace * 0.5);
    } else {
        letterSpaceHalfLeft = letterSpace * 0.5;
    }
    double letterSpaceHalfRight = letterSpace - letterSpaceHalfLeft;

    float x = mAdvance;
    float y = 0;
    for (size_t run_ix = 0; run_ix < items.size(); 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;
        hb_font_t* hbFont = ctx->hbFonts[font_ix];
#ifdef VERBOSE
        std::cout << "Run " << run_ix << ", font " << font_ix <<
            " [" << run.start << ":" << run.end << "]" << std::endl;
#endif

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

        // 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.
        ssize_t srunend;
        for (ssize_t srunstart = run.start; srunstart < run.end; srunstart = srunend) {
            srunend = srunstart;
            hb_script_t script = getScriptRun(buf + start, run.end, &srunend);

            hb_buffer_reset(buffer);
            hb_buffer_set_script(buffer, script);
            hb_buffer_set_direction(buffer, isRtl? HB_DIRECTION_RTL : HB_DIRECTION_LTR);
            FontLanguage language = ctx->style.getLanguage();
            if (language) {
                string lang = language.getString();
                hb_buffer_set_language(buffer, hb_language_from_string(lang.c_str(), -1));
            }
            hb_buffer_add_utf16(buffer, buf, bufSize, srunstart + start, srunend - srunstart);
            hb_shape(hbFont, buffer, features.empty() ? NULL : &features[0], features.size());
            unsigned int 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);
            if (numGlyphs)
            {
                mAdvances[info[0].cluster - start] += letterSpaceHalfLeft;
                x += letterSpaceHalfLeft;
            }
            for (unsigned int i = 0; i < numGlyphs; i++) {
    #ifdef VERBOSE
                std::cout << positions[i].x_advance << " " << positions[i].y_advance << " " << positions[i].x_offset << " " << positions[i].y_offset << std::endl;            std::cout << "DoLayout " << info[i].codepoint <<
                ": " << HBFixedToFloat(positions[i].x_advance) << "; " << positions[i].x_offset << ", " << positions[i].y_offset << std::endl;
    #endif
                if (i > 0 && info[i - 1].cluster != info[i].cluster) {
                    mAdvances[info[i - 1].cluster - start] += letterSpaceHalfRight;
                    mAdvances[info[i].cluster - start] += 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 = {font_ix, glyph_ix, x + xoff, y + yoff};
                mGlyphs.push_back(glyph);
                float xAdvance = HBFixedToFloat(positions[i].x_advance);
                if ((ctx->paint.paintFlags & LinearTextFlag) == 0) {
                    xAdvance = roundf(xAdvance);
                }
                MinikinRect glyphBounds;
                ctx->paint.font->GetBounds(&glyphBounds, glyph_ix, ctx->paint);
                glyphBounds.offset(x + xoff, y + yoff);
                mBounds.join(glyphBounds);
                mAdvances[info[i].cluster - start] += xAdvance;
                x += xAdvance;
            }
            if (numGlyphs)
            {
                mAdvances[info[numGlyphs - 1].cluster - start] += letterSpaceHalfRight;
                x += letterSpaceHalfRight;
            }
        }
    }
    mAdvance = x;
}