Canvas.cpp revision 5e00c7ce063116c11315639f0035aca8ad73e8cc
10c1bc742181ded4930842b46e9507372f0b1b963James Dong/*
20c1bc742181ded4930842b46e9507372f0b1b963James Dong * Copyright (C) 2015 The Android Open Source Project
30c1bc742181ded4930842b46e9507372f0b1b963James Dong *
40c1bc742181ded4930842b46e9507372f0b1b963James Dong * Licensed under the Apache License, Version 2.0 (the "License");
50c1bc742181ded4930842b46e9507372f0b1b963James Dong * you may not use this file except in compliance with the License.
60c1bc742181ded4930842b46e9507372f0b1b963James Dong * You may obtain a copy of the License at
70c1bc742181ded4930842b46e9507372f0b1b963James Dong *
80c1bc742181ded4930842b46e9507372f0b1b963James Dong *      http://www.apache.org/licenses/LICENSE-2.0
90c1bc742181ded4930842b46e9507372f0b1b963James Dong *
100c1bc742181ded4930842b46e9507372f0b1b963James Dong * Unless required by applicable law or agreed to in writing, software
110c1bc742181ded4930842b46e9507372f0b1b963James Dong * distributed under the License is distributed on an "AS IS" BASIS,
120c1bc742181ded4930842b46e9507372f0b1b963James Dong * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
130c1bc742181ded4930842b46e9507372f0b1b963James Dong * See the License for the specific language governing permissions and
140c1bc742181ded4930842b46e9507372f0b1b963James Dong * limitations under the License.
150c1bc742181ded4930842b46e9507372f0b1b963James Dong */
160c1bc742181ded4930842b46e9507372f0b1b963James Dong
170c1bc742181ded4930842b46e9507372f0b1b963James Dong#include "Canvas.h"
180c1bc742181ded4930842b46e9507372f0b1b963James Dong
190c1bc742181ded4930842b46e9507372f0b1b963James Dong#include "RecordingCanvas.h"
200c1bc742181ded4930842b46e9507372f0b1b963James Dong#include "MinikinUtils.h"
210c1bc742181ded4930842b46e9507372f0b1b963James Dong#include "Paint.h"
220c1bc742181ded4930842b46e9507372f0b1b963James Dong#include "Typeface.h"
230c1bc742181ded4930842b46e9507372f0b1b963James Dong
240c1bc742181ded4930842b46e9507372f0b1b963James Dong#include <SkDrawFilter.h>
250c1bc742181ded4930842b46e9507372f0b1b963James Dong
260c1bc742181ded4930842b46e9507372f0b1b963James Dongnamespace android {
270c1bc742181ded4930842b46e9507372f0b1b963James Dong
280c1bc742181ded4930842b46e9507372f0b1b963James DongCanvas* Canvas::create_recording_canvas(int width, int height) {
290c1bc742181ded4930842b46e9507372f0b1b963James Dong    return new uirenderer::RecordingCanvas(width, height);
300c1bc742181ded4930842b46e9507372f0b1b963James Dong}
310c1bc742181ded4930842b46e9507372f0b1b963James Dong
320c1bc742181ded4930842b46e9507372f0b1b963James Dongvoid Canvas::drawTextDecorations(float x, float y, float length, const SkPaint& paint) {
330c1bc742181ded4930842b46e9507372f0b1b963James Dong    uint32_t flags;
340c1bc742181ded4930842b46e9507372f0b1b963James Dong    SkDrawFilter* drawFilter = getDrawFilter();
35db43b34c3428e480f8c4c66e7e88f4001f37f91eMark Salyzyn    if (drawFilter) {
360c1bc742181ded4930842b46e9507372f0b1b963James Dong        SkPaint paintCopy(paint);
370c1bc742181ded4930842b46e9507372f0b1b963James Dong        drawFilter->filter(&paintCopy, SkDrawFilter::kText_Type);
380c1bc742181ded4930842b46e9507372f0b1b963James Dong        flags = paintCopy.getFlags();
390c1bc742181ded4930842b46e9507372f0b1b963James Dong    } else {
400c1bc742181ded4930842b46e9507372f0b1b963James Dong        flags = paint.getFlags();
410c1bc742181ded4930842b46e9507372f0b1b963James Dong    }
420c1bc742181ded4930842b46e9507372f0b1b963James Dong    if (flags & (SkPaint::kUnderlineText_Flag | SkPaint::kStrikeThruText_Flag)) {
430c1bc742181ded4930842b46e9507372f0b1b963James Dong        // Same values used by Skia
440c1bc742181ded4930842b46e9507372f0b1b963James Dong        const float kStdStrikeThru_Offset   = (-6.0f / 21.0f);
450c1bc742181ded4930842b46e9507372f0b1b963James Dong        const float kStdUnderline_Offset    = (1.0f / 9.0f);
460c1bc742181ded4930842b46e9507372f0b1b963James Dong        const float kStdUnderline_Thickness = (1.0f / 18.0f);
470c1bc742181ded4930842b46e9507372f0b1b963James Dong
480c1bc742181ded4930842b46e9507372f0b1b963James Dong        SkScalar left = x;
490c1bc742181ded4930842b46e9507372f0b1b963James Dong        SkScalar right = x + length;
500c1bc742181ded4930842b46e9507372f0b1b963James Dong        float textSize = paint.getTextSize();
510c1bc742181ded4930842b46e9507372f0b1b963James Dong        float strokeWidth = fmax(textSize * kStdUnderline_Thickness, 1.0f);
520c1bc742181ded4930842b46e9507372f0b1b963James Dong        if (flags & SkPaint::kUnderlineText_Flag) {
530c1bc742181ded4930842b46e9507372f0b1b963James Dong            SkScalar top = y + textSize * kStdUnderline_Offset - 0.5f * strokeWidth;
540c1bc742181ded4930842b46e9507372f0b1b963James Dong            SkScalar bottom = y + textSize * kStdUnderline_Offset + 0.5f * strokeWidth;
550c1bc742181ded4930842b46e9507372f0b1b963James Dong            drawRect(left, top, right, bottom, paint);
560c1bc742181ded4930842b46e9507372f0b1b963James Dong        }
570c1bc742181ded4930842b46e9507372f0b1b963James Dong        if (flags & SkPaint::kStrikeThruText_Flag) {
580c1bc742181ded4930842b46e9507372f0b1b963James Dong            SkScalar top = y + textSize * kStdStrikeThru_Offset - 0.5f * strokeWidth;
590c1bc742181ded4930842b46e9507372f0b1b963James Dong            SkScalar bottom = y + textSize * kStdStrikeThru_Offset + 0.5f * strokeWidth;
600c1bc742181ded4930842b46e9507372f0b1b963James Dong            drawRect(left, top, right, bottom, paint);
610c1bc742181ded4930842b46e9507372f0b1b963James Dong        }
620c1bc742181ded4930842b46e9507372f0b1b963James Dong    }
630c1bc742181ded4930842b46e9507372f0b1b963James Dong}
640c1bc742181ded4930842b46e9507372f0b1b963James Dong
650c1bc742181ded4930842b46e9507372f0b1b963James Dongstatic void simplifyPaint(int color, SkPaint* paint) {
660c1bc742181ded4930842b46e9507372f0b1b963James Dong    paint->setColor(color);
670c1bc742181ded4930842b46e9507372f0b1b963James Dong    paint->setShader(nullptr);
680c1bc742181ded4930842b46e9507372f0b1b963James Dong    paint->setColorFilter(nullptr);
690c1bc742181ded4930842b46e9507372f0b1b963James Dong    paint->setLooper(nullptr);
700c1bc742181ded4930842b46e9507372f0b1b963James Dong    paint->setStrokeWidth(4 + 0.04 * paint->getTextSize());
710c1bc742181ded4930842b46e9507372f0b1b963James Dong    paint->setStrokeJoin(SkPaint::kRound_Join);
720c1bc742181ded4930842b46e9507372f0b1b963James Dong    paint->setLooper(nullptr);
730c1bc742181ded4930842b46e9507372f0b1b963James Dong}
740c1bc742181ded4930842b46e9507372f0b1b963James Dong
750c1bc742181ded4930842b46e9507372f0b1b963James Dongclass DrawTextFunctor {
760c1bc742181ded4930842b46e9507372f0b1b963James Dongpublic:
770c1bc742181ded4930842b46e9507372f0b1b963James Dong    DrawTextFunctor(const minikin::Layout& layout, Canvas* canvas, uint16_t* glyphs, float* pos,
780c1bc742181ded4930842b46e9507372f0b1b963James Dong            const SkPaint& paint, float x, float y, minikin::MinikinRect& bounds,
79db43b34c3428e480f8c4c66e7e88f4001f37f91eMark Salyzyn            float totalAdvance)
800c1bc742181ded4930842b46e9507372f0b1b963James Dong        : layout(layout)
810c1bc742181ded4930842b46e9507372f0b1b963James Dong        , canvas(canvas)
82db43b34c3428e480f8c4c66e7e88f4001f37f91eMark Salyzyn        , glyphs(glyphs)
830c1bc742181ded4930842b46e9507372f0b1b963James Dong        , pos(pos)
840c1bc742181ded4930842b46e9507372f0b1b963James Dong        , paint(paint)
85db43b34c3428e480f8c4c66e7e88f4001f37f91eMark Salyzyn        , x(x)
860c1bc742181ded4930842b46e9507372f0b1b963James Dong        , y(y)
870c1bc742181ded4930842b46e9507372f0b1b963James Dong        , bounds(bounds)
88db43b34c3428e480f8c4c66e7e88f4001f37f91eMark Salyzyn        , totalAdvance(totalAdvance) {
890c1bc742181ded4930842b46e9507372f0b1b963James Dong    }
900c1bc742181ded4930842b46e9507372f0b1b963James Dong
91db43b34c3428e480f8c4c66e7e88f4001f37f91eMark Salyzyn    void operator()(size_t start, size_t end) {
920c1bc742181ded4930842b46e9507372f0b1b963James Dong        if (canvas->drawTextAbsolutePos()) {
930c1bc742181ded4930842b46e9507372f0b1b963James Dong            for (size_t i = start; i < end; i++) {
94db43b34c3428e480f8c4c66e7e88f4001f37f91eMark Salyzyn                glyphs[i] = layout.getGlyphId(i);
950c1bc742181ded4930842b46e9507372f0b1b963James Dong                pos[2 * i] = x + layout.getX(i);
960c1bc742181ded4930842b46e9507372f0b1b963James Dong                pos[2 * i + 1] = y + layout.getY(i);
970c1bc742181ded4930842b46e9507372f0b1b963James Dong            }
980c1bc742181ded4930842b46e9507372f0b1b963James Dong        } else {
990c1bc742181ded4930842b46e9507372f0b1b963James Dong            for (size_t i = start; i < end; i++) {
1000c1bc742181ded4930842b46e9507372f0b1b963James Dong                glyphs[i] = layout.getGlyphId(i);
1010c1bc742181ded4930842b46e9507372f0b1b963James Dong                pos[2 * i] = layout.getX(i);
1020c1bc742181ded4930842b46e9507372f0b1b963James Dong                pos[2 * i + 1] = layout.getY(i);
1030c1bc742181ded4930842b46e9507372f0b1b963James Dong            }
1040c1bc742181ded4930842b46e9507372f0b1b963James Dong        }
105db43b34c3428e480f8c4c66e7e88f4001f37f91eMark Salyzyn
106db43b34c3428e480f8c4c66e7e88f4001f37f91eMark Salyzyn        size_t glyphCount = end - start;
1070c1bc742181ded4930842b46e9507372f0b1b963James Dong
1080c1bc742181ded4930842b46e9507372f0b1b963James Dong        if (CC_UNLIKELY(canvas->isHighContrastText() && paint.getAlpha() != 0)) {
1090c1bc742181ded4930842b46e9507372f0b1b963James Dong            // high contrast draw path
1100c1bc742181ded4930842b46e9507372f0b1b963James Dong            int color = paint.getColor();
1110c1bc742181ded4930842b46e9507372f0b1b963James Dong            int channelSum = SkColorGetR(color) + SkColorGetG(color) + SkColorGetB(color);
1120c1bc742181ded4930842b46e9507372f0b1b963James Dong            bool darken = channelSum < (128 * 3);
1130c1bc742181ded4930842b46e9507372f0b1b963James Dong
1140c1bc742181ded4930842b46e9507372f0b1b963James Dong            // outline
1150c1bc742181ded4930842b46e9507372f0b1b963James Dong            SkPaint outlinePaint(paint);
1160c1bc742181ded4930842b46e9507372f0b1b963James Dong            simplifyPaint(darken ? SK_ColorWHITE : SK_ColorBLACK, &outlinePaint);
1170c1bc742181ded4930842b46e9507372f0b1b963James Dong            outlinePaint.setStyle(SkPaint::kStrokeAndFill_Style);
1180c1bc742181ded4930842b46e9507372f0b1b963James Dong            canvas->drawGlyphs(glyphs + start, pos + (2 * start), glyphCount, outlinePaint, x, y,
1190c1bc742181ded4930842b46e9507372f0b1b963James Dong                    bounds.mLeft, bounds.mTop, bounds.mRight, bounds.mBottom, totalAdvance);
1200c1bc742181ded4930842b46e9507372f0b1b963James Dong
1210c1bc742181ded4930842b46e9507372f0b1b963James Dong            // inner
1220c1bc742181ded4930842b46e9507372f0b1b963James Dong            SkPaint innerPaint(paint);
1230c1bc742181ded4930842b46e9507372f0b1b963James Dong            simplifyPaint(darken ? SK_ColorBLACK : SK_ColorWHITE, &innerPaint);
1240c1bc742181ded4930842b46e9507372f0b1b963James Dong            innerPaint.setStyle(SkPaint::kFill_Style);
1250c1bc742181ded4930842b46e9507372f0b1b963James Dong            canvas->drawGlyphs(glyphs + start, pos + (2 * start), glyphCount, innerPaint, x, y,
1260c1bc742181ded4930842b46e9507372f0b1b963James Dong                    bounds.mLeft, bounds.mTop, bounds.mRight, bounds.mBottom, totalAdvance);
1270c1bc742181ded4930842b46e9507372f0b1b963James Dong        } else {
1280c1bc742181ded4930842b46e9507372f0b1b963James Dong            // standard draw path
1290c1bc742181ded4930842b46e9507372f0b1b963James Dong            canvas->drawGlyphs(glyphs + start, pos + (2 * start), glyphCount, paint, x, y,
1300c1bc742181ded4930842b46e9507372f0b1b963James Dong                    bounds.mLeft, bounds.mTop, bounds.mRight, bounds.mBottom, totalAdvance);
1310c1bc742181ded4930842b46e9507372f0b1b963James Dong        }
1320c1bc742181ded4930842b46e9507372f0b1b963James Dong    }
1330c1bc742181ded4930842b46e9507372f0b1b963James Dongprivate:
1340c1bc742181ded4930842b46e9507372f0b1b963James Dong    const minikin::Layout& layout;
1350c1bc742181ded4930842b46e9507372f0b1b963James Dong    Canvas* canvas;
1360c1bc742181ded4930842b46e9507372f0b1b963James Dong    uint16_t* glyphs;
1370c1bc742181ded4930842b46e9507372f0b1b963James Dong    float* pos;
1380c1bc742181ded4930842b46e9507372f0b1b963James Dong    const SkPaint& paint;
1390c1bc742181ded4930842b46e9507372f0b1b963James Dong    float x;
1400c1bc742181ded4930842b46e9507372f0b1b963James Dong    float y;
1410c1bc742181ded4930842b46e9507372f0b1b963James Dong    minikin::MinikinRect& bounds;
1420c1bc742181ded4930842b46e9507372f0b1b963James Dong    float totalAdvance;
1430c1bc742181ded4930842b46e9507372f0b1b963James Dong};
1440c1bc742181ded4930842b46e9507372f0b1b963James Dong
1450c1bc742181ded4930842b46e9507372f0b1b963James Dongvoid Canvas::drawText(const uint16_t* text, int start, int count, int contextCount,
1460c1bc742181ded4930842b46e9507372f0b1b963James Dong        float x, float y, int bidiFlags, const Paint& origPaint, Typeface* typeface) {
1470c1bc742181ded4930842b46e9507372f0b1b963James Dong    // minikin may modify the original paint
1480c1bc742181ded4930842b46e9507372f0b1b963James Dong    Paint paint(origPaint);
1490c1bc742181ded4930842b46e9507372f0b1b963James Dong
1500c1bc742181ded4930842b46e9507372f0b1b963James Dong    minikin::Layout layout;
1510c1bc742181ded4930842b46e9507372f0b1b963James Dong    MinikinUtils::doLayout(&layout, &paint, bidiFlags, typeface, text, start, count, contextCount);
1520c1bc742181ded4930842b46e9507372f0b1b963James Dong
1530c1bc742181ded4930842b46e9507372f0b1b963James Dong    size_t nGlyphs = layout.nGlyphs();
1540c1bc742181ded4930842b46e9507372f0b1b963James Dong    std::unique_ptr<uint16_t[]> glyphs(new uint16_t[nGlyphs]);
1550c1bc742181ded4930842b46e9507372f0b1b963James Dong    std::unique_ptr<float[]> pos(new float[nGlyphs * 2]);
1560c1bc742181ded4930842b46e9507372f0b1b963James Dong
1570c1bc742181ded4930842b46e9507372f0b1b963James Dong    x += MinikinUtils::xOffsetForTextAlign(&paint, layout);
1580c1bc742181ded4930842b46e9507372f0b1b963James Dong
1590c1bc742181ded4930842b46e9507372f0b1b963James Dong    minikin::MinikinRect bounds;
1600c1bc742181ded4930842b46e9507372f0b1b963James Dong    layout.getBounds(&bounds);
1610c1bc742181ded4930842b46e9507372f0b1b963James Dong    if (!drawTextAbsolutePos()) {
1620c1bc742181ded4930842b46e9507372f0b1b963James Dong        bounds.offset(x, y);
1630c1bc742181ded4930842b46e9507372f0b1b963James Dong    }
1640c1bc742181ded4930842b46e9507372f0b1b963James Dong
1650c1bc742181ded4930842b46e9507372f0b1b963James Dong    // Set align to left for drawing, as we don't want individual
1660c1bc742181ded4930842b46e9507372f0b1b963James Dong    // glyphs centered or right-aligned; the offset above takes
1670c1bc742181ded4930842b46e9507372f0b1b963James Dong    // care of all alignment.
1680c1bc742181ded4930842b46e9507372f0b1b963James Dong    paint.setTextAlign(Paint::kLeft_Align);
1690c1bc742181ded4930842b46e9507372f0b1b963James Dong
1700c1bc742181ded4930842b46e9507372f0b1b963James Dong    DrawTextFunctor f(layout, this, glyphs.get(), pos.get(),
1710c1bc742181ded4930842b46e9507372f0b1b963James Dong            paint, x, y, bounds, layout.getAdvance());
1720c1bc742181ded4930842b46e9507372f0b1b963James Dong    MinikinUtils::forFontRun(layout, &paint, f);
1730c1bc742181ded4930842b46e9507372f0b1b963James Dong}
1740c1bc742181ded4930842b46e9507372f0b1b963James Dong
1750c1bc742181ded4930842b46e9507372f0b1b963James Dongclass DrawTextOnPathFunctor {
1760c1bc742181ded4930842b46e9507372f0b1b963James Dongpublic:
1770c1bc742181ded4930842b46e9507372f0b1b963James Dong    DrawTextOnPathFunctor(const minikin::Layout& layout, Canvas* canvas, float hOffset,
1780c1bc742181ded4930842b46e9507372f0b1b963James Dong            float vOffset, const Paint& paint, const SkPath& path)
1790c1bc742181ded4930842b46e9507372f0b1b963James Dong        : layout(layout)
1800c1bc742181ded4930842b46e9507372f0b1b963James Dong        , canvas(canvas)
1810c1bc742181ded4930842b46e9507372f0b1b963James Dong        , hOffset(hOffset)
1820c1bc742181ded4930842b46e9507372f0b1b963James Dong        , vOffset(vOffset)
1830c1bc742181ded4930842b46e9507372f0b1b963James Dong        , paint(paint)
1840c1bc742181ded4930842b46e9507372f0b1b963James Dong        , path(path) {
1850c1bc742181ded4930842b46e9507372f0b1b963James Dong    }
1860c1bc742181ded4930842b46e9507372f0b1b963James Dong
1870c1bc742181ded4930842b46e9507372f0b1b963James Dong    void operator()(size_t start, size_t end) {
1880c1bc742181ded4930842b46e9507372f0b1b963James Dong        uint16_t glyphs[1];
1890c1bc742181ded4930842b46e9507372f0b1b963James Dong        for (size_t i = start; i < end; i++) {
1900c1bc742181ded4930842b46e9507372f0b1b963James Dong            glyphs[0] = layout.getGlyphId(i);
1910c1bc742181ded4930842b46e9507372f0b1b963James Dong            float x = hOffset + layout.getX(i);
1920c1bc742181ded4930842b46e9507372f0b1b963James Dong            float y = vOffset + layout.getY(i);
1930c1bc742181ded4930842b46e9507372f0b1b963James Dong            canvas->drawGlyphsOnPath(glyphs, 1, path, x, y, paint);
1940c1bc742181ded4930842b46e9507372f0b1b963James Dong        }
1950c1bc742181ded4930842b46e9507372f0b1b963James Dong    }
1960c1bc742181ded4930842b46e9507372f0b1b963James Dongprivate:
1970c1bc742181ded4930842b46e9507372f0b1b963James Dong    const minikin::Layout& layout;
1980c1bc742181ded4930842b46e9507372f0b1b963James Dong    Canvas* canvas;
1990c1bc742181ded4930842b46e9507372f0b1b963James Dong    float hOffset;
2000c1bc742181ded4930842b46e9507372f0b1b963James Dong    float vOffset;
2010c1bc742181ded4930842b46e9507372f0b1b963James Dong    const Paint& paint;
2020c1bc742181ded4930842b46e9507372f0b1b963James Dong    const SkPath& path;
2030c1bc742181ded4930842b46e9507372f0b1b963James Dong};
2040c1bc742181ded4930842b46e9507372f0b1b963James Dong
2050c1bc742181ded4930842b46e9507372f0b1b963James Dongvoid Canvas::drawTextOnPath(const uint16_t* text, int count, int bidiFlags, const SkPath& path,
2060c1bc742181ded4930842b46e9507372f0b1b963James Dong        float hOffset, float vOffset, const Paint& paint, Typeface* typeface) {
2070c1bc742181ded4930842b46e9507372f0b1b963James Dong    Paint paintCopy(paint);
2080c1bc742181ded4930842b46e9507372f0b1b963James Dong    minikin::Layout layout;
2090c1bc742181ded4930842b46e9507372f0b1b963James Dong    MinikinUtils::doLayout(&layout, &paintCopy, bidiFlags, typeface, text, 0, count, count);
2100c1bc742181ded4930842b46e9507372f0b1b963James Dong    hOffset += MinikinUtils::hOffsetForTextAlign(&paintCopy, layout, path);
211
212    // Set align to left for drawing, as we don't want individual
213    // glyphs centered or right-aligned; the offset above takes
214    // care of all alignment.
215    paintCopy.setTextAlign(Paint::kLeft_Align);
216
217    DrawTextOnPathFunctor f(layout, this, hOffset, vOffset, paintCopy, path);
218    MinikinUtils::forFontRun(layout, &paintCopy, f);
219}
220
221} // namespace android
222