Canvas.cpp revision bad99183916ba2bac6659efc8a28273e344ba511
1dccca44ffda4836b56a21da95a046c9708ffd49csergeyv/*
2dccca44ffda4836b56a21da95a046c9708ffd49csergeyv * Copyright (C) 2015 The Android Open Source Project
3dccca44ffda4836b56a21da95a046c9708ffd49csergeyv *
4dccca44ffda4836b56a21da95a046c9708ffd49csergeyv * Licensed under the Apache License, Version 2.0 (the "License");
5dccca44ffda4836b56a21da95a046c9708ffd49csergeyv * you may not use this file except in compliance with the License.
6dccca44ffda4836b56a21da95a046c9708ffd49csergeyv * You may obtain a copy of the License at
7dccca44ffda4836b56a21da95a046c9708ffd49csergeyv *
8dccca44ffda4836b56a21da95a046c9708ffd49csergeyv *      http://www.apache.org/licenses/LICENSE-2.0
9dccca44ffda4836b56a21da95a046c9708ffd49csergeyv *
10dccca44ffda4836b56a21da95a046c9708ffd49csergeyv * Unless required by applicable law or agreed to in writing, software
11dccca44ffda4836b56a21da95a046c9708ffd49csergeyv * distributed under the License is distributed on an "AS IS" BASIS,
12dccca44ffda4836b56a21da95a046c9708ffd49csergeyv * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13dccca44ffda4836b56a21da95a046c9708ffd49csergeyv * See the License for the specific language governing permissions and
14dccca44ffda4836b56a21da95a046c9708ffd49csergeyv * limitations under the License.
15dccca44ffda4836b56a21da95a046c9708ffd49csergeyv */
16dccca44ffda4836b56a21da95a046c9708ffd49csergeyv
17dccca44ffda4836b56a21da95a046c9708ffd49csergeyv#include "Canvas.h"
18dccca44ffda4836b56a21da95a046c9708ffd49csergeyv
19dccca44ffda4836b56a21da95a046c9708ffd49csergeyv#include "DisplayListCanvas.h"
20dccca44ffda4836b56a21da95a046c9708ffd49csergeyv#include "RecordingCanvas.h"
21dccca44ffda4836b56a21da95a046c9708ffd49csergeyv#include "MinikinUtils.h"
22dccca44ffda4836b56a21da95a046c9708ffd49csergeyv#include "Paint.h"
23bad99183916ba2bac6659efc8a28273e344ba511sergeyv#include "Typeface.h"
24dccca44ffda4836b56a21da95a046c9708ffd49csergeyv
25dccca44ffda4836b56a21da95a046c9708ffd49csergeyv#include <SkDrawFilter.h>
26dccca44ffda4836b56a21da95a046c9708ffd49csergeyv
27dccca44ffda4836b56a21da95a046c9708ffd49csergeyvnamespace android {
28dccca44ffda4836b56a21da95a046c9708ffd49csergeyv
29dccca44ffda4836b56a21da95a046c9708ffd49csergeyvCanvas* Canvas::create_recording_canvas(int width, int height) {
30dccca44ffda4836b56a21da95a046c9708ffd49csergeyv#if HWUI_NEW_OPS
31dccca44ffda4836b56a21da95a046c9708ffd49csergeyv    return new uirenderer::RecordingCanvas(width, height);
32dccca44ffda4836b56a21da95a046c9708ffd49csergeyv#else
33dccca44ffda4836b56a21da95a046c9708ffd49csergeyv    return new uirenderer::DisplayListCanvas(width, height);
34dccca44ffda4836b56a21da95a046c9708ffd49csergeyv#endif
35dccca44ffda4836b56a21da95a046c9708ffd49csergeyv}
36dccca44ffda4836b56a21da95a046c9708ffd49csergeyv
37dccca44ffda4836b56a21da95a046c9708ffd49csergeyvvoid Canvas::drawTextDecorations(float x, float y, float length, const SkPaint& paint) {
38dccca44ffda4836b56a21da95a046c9708ffd49csergeyv    uint32_t flags;
39dccca44ffda4836b56a21da95a046c9708ffd49csergeyv    SkDrawFilter* drawFilter = getDrawFilter();
40dccca44ffda4836b56a21da95a046c9708ffd49csergeyv    if (drawFilter) {
41dccca44ffda4836b56a21da95a046c9708ffd49csergeyv        SkPaint paintCopy(paint);
42dccca44ffda4836b56a21da95a046c9708ffd49csergeyv        drawFilter->filter(&paintCopy, SkDrawFilter::kText_Type);
43dccca44ffda4836b56a21da95a046c9708ffd49csergeyv        flags = paintCopy.getFlags();
44dccca44ffda4836b56a21da95a046c9708ffd49csergeyv    } else {
45dccca44ffda4836b56a21da95a046c9708ffd49csergeyv        flags = paint.getFlags();
46dccca44ffda4836b56a21da95a046c9708ffd49csergeyv    }
47dccca44ffda4836b56a21da95a046c9708ffd49csergeyv    if (flags & (SkPaint::kUnderlineText_Flag | SkPaint::kStrikeThruText_Flag)) {
48dccca44ffda4836b56a21da95a046c9708ffd49csergeyv        // Same values used by Skia
49dccca44ffda4836b56a21da95a046c9708ffd49csergeyv        const float kStdStrikeThru_Offset   = (-6.0f / 21.0f);
50dccca44ffda4836b56a21da95a046c9708ffd49csergeyv        const float kStdUnderline_Offset    = (1.0f / 9.0f);
51dccca44ffda4836b56a21da95a046c9708ffd49csergeyv        const float kStdUnderline_Thickness = (1.0f / 18.0f);
52dccca44ffda4836b56a21da95a046c9708ffd49csergeyv
53dccca44ffda4836b56a21da95a046c9708ffd49csergeyv        SkScalar left = x;
54dccca44ffda4836b56a21da95a046c9708ffd49csergeyv        SkScalar right = x + length;
55dccca44ffda4836b56a21da95a046c9708ffd49csergeyv        float textSize = paint.getTextSize();
56dccca44ffda4836b56a21da95a046c9708ffd49csergeyv        float strokeWidth = fmax(textSize * kStdUnderline_Thickness, 1.0f);
57dccca44ffda4836b56a21da95a046c9708ffd49csergeyv        if (flags & SkPaint::kUnderlineText_Flag) {
58dccca44ffda4836b56a21da95a046c9708ffd49csergeyv            SkScalar top = y + textSize * kStdUnderline_Offset - 0.5f * strokeWidth;
59dccca44ffda4836b56a21da95a046c9708ffd49csergeyv            SkScalar bottom = y + textSize * kStdUnderline_Offset + 0.5f * strokeWidth;
60dccca44ffda4836b56a21da95a046c9708ffd49csergeyv            drawRect(left, top, right, bottom, paint);
61dccca44ffda4836b56a21da95a046c9708ffd49csergeyv        }
62dccca44ffda4836b56a21da95a046c9708ffd49csergeyv        if (flags & SkPaint::kStrikeThruText_Flag) {
63dccca44ffda4836b56a21da95a046c9708ffd49csergeyv            SkScalar top = y + textSize * kStdStrikeThru_Offset - 0.5f * strokeWidth;
64dccca44ffda4836b56a21da95a046c9708ffd49csergeyv            SkScalar bottom = y + textSize * kStdStrikeThru_Offset + 0.5f * strokeWidth;
65dccca44ffda4836b56a21da95a046c9708ffd49csergeyv            drawRect(left, top, right, bottom, paint);
66dccca44ffda4836b56a21da95a046c9708ffd49csergeyv        }
67dccca44ffda4836b56a21da95a046c9708ffd49csergeyv    }
68dccca44ffda4836b56a21da95a046c9708ffd49csergeyv}
69dccca44ffda4836b56a21da95a046c9708ffd49csergeyv
70dccca44ffda4836b56a21da95a046c9708ffd49csergeyvstatic void simplifyPaint(int color, SkPaint* paint) {
71dccca44ffda4836b56a21da95a046c9708ffd49csergeyv    paint->setColor(color);
72dccca44ffda4836b56a21da95a046c9708ffd49csergeyv    paint->setShader(nullptr);
73dccca44ffda4836b56a21da95a046c9708ffd49csergeyv    paint->setColorFilter(nullptr);
74dccca44ffda4836b56a21da95a046c9708ffd49csergeyv    paint->setLooper(nullptr);
75dccca44ffda4836b56a21da95a046c9708ffd49csergeyv    paint->setStrokeWidth(4 + 0.04 * paint->getTextSize());
76dccca44ffda4836b56a21da95a046c9708ffd49csergeyv    paint->setStrokeJoin(SkPaint::kRound_Join);
77dccca44ffda4836b56a21da95a046c9708ffd49csergeyv    paint->setLooper(nullptr);
78dccca44ffda4836b56a21da95a046c9708ffd49csergeyv}
79dccca44ffda4836b56a21da95a046c9708ffd49csergeyv
80dccca44ffda4836b56a21da95a046c9708ffd49csergeyvclass DrawTextFunctor {
81dccca44ffda4836b56a21da95a046c9708ffd49csergeyvpublic:
82dccca44ffda4836b56a21da95a046c9708ffd49csergeyv    DrawTextFunctor(const Layout& layout, Canvas* canvas, uint16_t* glyphs, float* pos,
83dccca44ffda4836b56a21da95a046c9708ffd49csergeyv            const SkPaint& paint, float x, float y, MinikinRect& bounds, float totalAdvance)
84dccca44ffda4836b56a21da95a046c9708ffd49csergeyv        : layout(layout)
85dccca44ffda4836b56a21da95a046c9708ffd49csergeyv        , canvas(canvas)
86dccca44ffda4836b56a21da95a046c9708ffd49csergeyv        , glyphs(glyphs)
87dccca44ffda4836b56a21da95a046c9708ffd49csergeyv        , pos(pos)
88dccca44ffda4836b56a21da95a046c9708ffd49csergeyv        , paint(paint)
89dccca44ffda4836b56a21da95a046c9708ffd49csergeyv        , x(x)
90dccca44ffda4836b56a21da95a046c9708ffd49csergeyv        , y(y)
91dccca44ffda4836b56a21da95a046c9708ffd49csergeyv        , bounds(bounds)
92dccca44ffda4836b56a21da95a046c9708ffd49csergeyv        , totalAdvance(totalAdvance) {
93dccca44ffda4836b56a21da95a046c9708ffd49csergeyv    }
94dccca44ffda4836b56a21da95a046c9708ffd49csergeyv
95dccca44ffda4836b56a21da95a046c9708ffd49csergeyv    void operator()(size_t start, size_t end) {
96dccca44ffda4836b56a21da95a046c9708ffd49csergeyv        if (canvas->drawTextAbsolutePos()) {
97dccca44ffda4836b56a21da95a046c9708ffd49csergeyv            for (size_t i = start; i < end; i++) {
98dccca44ffda4836b56a21da95a046c9708ffd49csergeyv                glyphs[i] = layout.getGlyphId(i);
99dccca44ffda4836b56a21da95a046c9708ffd49csergeyv                pos[2 * i] = x + layout.getX(i);
100dccca44ffda4836b56a21da95a046c9708ffd49csergeyv                pos[2 * i + 1] = y + layout.getY(i);
101dccca44ffda4836b56a21da95a046c9708ffd49csergeyv            }
102dccca44ffda4836b56a21da95a046c9708ffd49csergeyv        } else {
103dccca44ffda4836b56a21da95a046c9708ffd49csergeyv            for (size_t i = start; i < end; i++) {
104dccca44ffda4836b56a21da95a046c9708ffd49csergeyv                glyphs[i] = layout.getGlyphId(i);
105dccca44ffda4836b56a21da95a046c9708ffd49csergeyv                pos[2 * i] = layout.getX(i);
106dccca44ffda4836b56a21da95a046c9708ffd49csergeyv                pos[2 * i + 1] = layout.getY(i);
107dccca44ffda4836b56a21da95a046c9708ffd49csergeyv            }
108dccca44ffda4836b56a21da95a046c9708ffd49csergeyv        }
109dccca44ffda4836b56a21da95a046c9708ffd49csergeyv
110dccca44ffda4836b56a21da95a046c9708ffd49csergeyv        size_t glyphCount = end - start;
111dccca44ffda4836b56a21da95a046c9708ffd49csergeyv
112dccca44ffda4836b56a21da95a046c9708ffd49csergeyv        if (CC_UNLIKELY(canvas->isHighContrastText() && paint.getAlpha() != 0)) {
113dccca44ffda4836b56a21da95a046c9708ffd49csergeyv            // high contrast draw path
114dccca44ffda4836b56a21da95a046c9708ffd49csergeyv            int color = paint.getColor();
115dccca44ffda4836b56a21da95a046c9708ffd49csergeyv            int channelSum = SkColorGetR(color) + SkColorGetG(color) + SkColorGetB(color);
116dccca44ffda4836b56a21da95a046c9708ffd49csergeyv            bool darken = channelSum < (128 * 3);
117dccca44ffda4836b56a21da95a046c9708ffd49csergeyv
118dccca44ffda4836b56a21da95a046c9708ffd49csergeyv            // outline
119dccca44ffda4836b56a21da95a046c9708ffd49csergeyv            SkPaint outlinePaint(paint);
120dccca44ffda4836b56a21da95a046c9708ffd49csergeyv            simplifyPaint(darken ? SK_ColorWHITE : SK_ColorBLACK, &outlinePaint);
121dccca44ffda4836b56a21da95a046c9708ffd49csergeyv            outlinePaint.setStyle(SkPaint::kStrokeAndFill_Style);
122dccca44ffda4836b56a21da95a046c9708ffd49csergeyv            canvas->drawGlyphs(glyphs + start, pos + (2 * start), glyphCount, outlinePaint, x, y,
123dccca44ffda4836b56a21da95a046c9708ffd49csergeyv                    bounds.mLeft, bounds.mTop, bounds.mRight, bounds.mBottom, totalAdvance);
124dccca44ffda4836b56a21da95a046c9708ffd49csergeyv
125dccca44ffda4836b56a21da95a046c9708ffd49csergeyv            // inner
126dccca44ffda4836b56a21da95a046c9708ffd49csergeyv            SkPaint innerPaint(paint);
127dccca44ffda4836b56a21da95a046c9708ffd49csergeyv            simplifyPaint(darken ? SK_ColorBLACK : SK_ColorWHITE, &innerPaint);
128dccca44ffda4836b56a21da95a046c9708ffd49csergeyv            innerPaint.setStyle(SkPaint::kFill_Style);
129dccca44ffda4836b56a21da95a046c9708ffd49csergeyv            canvas->drawGlyphs(glyphs + start, pos + (2 * start), glyphCount, innerPaint, x, y,
130dccca44ffda4836b56a21da95a046c9708ffd49csergeyv                    bounds.mLeft, bounds.mTop, bounds.mRight, bounds.mBottom, totalAdvance);
131dccca44ffda4836b56a21da95a046c9708ffd49csergeyv        } else {
132dccca44ffda4836b56a21da95a046c9708ffd49csergeyv            // standard draw path
133dccca44ffda4836b56a21da95a046c9708ffd49csergeyv            canvas->drawGlyphs(glyphs + start, pos + (2 * start), glyphCount, paint, x, y,
134dccca44ffda4836b56a21da95a046c9708ffd49csergeyv                    bounds.mLeft, bounds.mTop, bounds.mRight, bounds.mBottom, totalAdvance);
135dccca44ffda4836b56a21da95a046c9708ffd49csergeyv        }
136dccca44ffda4836b56a21da95a046c9708ffd49csergeyv    }
137dccca44ffda4836b56a21da95a046c9708ffd49csergeyvprivate:
138dccca44ffda4836b56a21da95a046c9708ffd49csergeyv    const Layout& layout;
139dccca44ffda4836b56a21da95a046c9708ffd49csergeyv    Canvas* canvas;
140dccca44ffda4836b56a21da95a046c9708ffd49csergeyv    uint16_t* glyphs;
141dccca44ffda4836b56a21da95a046c9708ffd49csergeyv    float* pos;
142dccca44ffda4836b56a21da95a046c9708ffd49csergeyv    const SkPaint& paint;
143dccca44ffda4836b56a21da95a046c9708ffd49csergeyv    float x;
144dccca44ffda4836b56a21da95a046c9708ffd49csergeyv    float y;
145dccca44ffda4836b56a21da95a046c9708ffd49csergeyv    MinikinRect& bounds;
146dccca44ffda4836b56a21da95a046c9708ffd49csergeyv    float totalAdvance;
147dccca44ffda4836b56a21da95a046c9708ffd49csergeyv};
148dccca44ffda4836b56a21da95a046c9708ffd49csergeyv
149dccca44ffda4836b56a21da95a046c9708ffd49csergeyvvoid Canvas::drawText(const uint16_t* text, int start, int count, int contextCount,
150bad99183916ba2bac6659efc8a28273e344ba511sergeyv        float x, float y, int bidiFlags, const Paint& origPaint, Typeface* typeface) {
151dccca44ffda4836b56a21da95a046c9708ffd49csergeyv    // minikin may modify the original paint
152dccca44ffda4836b56a21da95a046c9708ffd49csergeyv    Paint paint(origPaint);
153dccca44ffda4836b56a21da95a046c9708ffd49csergeyv
154dccca44ffda4836b56a21da95a046c9708ffd49csergeyv    Layout layout;
155dccca44ffda4836b56a21da95a046c9708ffd49csergeyv    MinikinUtils::doLayout(&layout, &paint, bidiFlags, typeface, text, start, count, contextCount);
156dccca44ffda4836b56a21da95a046c9708ffd49csergeyv
157dccca44ffda4836b56a21da95a046c9708ffd49csergeyv    size_t nGlyphs = layout.nGlyphs();
158dccca44ffda4836b56a21da95a046c9708ffd49csergeyv    std::unique_ptr<uint16_t[]> glyphs(new uint16_t[nGlyphs]);
159dccca44ffda4836b56a21da95a046c9708ffd49csergeyv    std::unique_ptr<float[]> pos(new float[nGlyphs * 2]);
160dccca44ffda4836b56a21da95a046c9708ffd49csergeyv
161dccca44ffda4836b56a21da95a046c9708ffd49csergeyv    x += MinikinUtils::xOffsetForTextAlign(&paint, layout);
162dccca44ffda4836b56a21da95a046c9708ffd49csergeyv
163dccca44ffda4836b56a21da95a046c9708ffd49csergeyv    MinikinRect bounds;
164dccca44ffda4836b56a21da95a046c9708ffd49csergeyv    layout.getBounds(&bounds);
165dccca44ffda4836b56a21da95a046c9708ffd49csergeyv    if (!drawTextAbsolutePos()) {
166dccca44ffda4836b56a21da95a046c9708ffd49csergeyv        bounds.offset(x, y);
167dccca44ffda4836b56a21da95a046c9708ffd49csergeyv    }
168dccca44ffda4836b56a21da95a046c9708ffd49csergeyv
169dccca44ffda4836b56a21da95a046c9708ffd49csergeyv    DrawTextFunctor f(layout, this, glyphs.get(), pos.get(),
170dccca44ffda4836b56a21da95a046c9708ffd49csergeyv            paint, x, y, bounds, layout.getAdvance());
171dccca44ffda4836b56a21da95a046c9708ffd49csergeyv    MinikinUtils::forFontRun(layout, &paint, f);
172dccca44ffda4836b56a21da95a046c9708ffd49csergeyv}
173dccca44ffda4836b56a21da95a046c9708ffd49csergeyv
174dccca44ffda4836b56a21da95a046c9708ffd49csergeyvclass DrawTextOnPathFunctor {
175dccca44ffda4836b56a21da95a046c9708ffd49csergeyvpublic:
176dccca44ffda4836b56a21da95a046c9708ffd49csergeyv    DrawTextOnPathFunctor(const Layout& layout, Canvas* canvas, float hOffset,
177dccca44ffda4836b56a21da95a046c9708ffd49csergeyv            float vOffset, const Paint& paint, const SkPath& path)
178dccca44ffda4836b56a21da95a046c9708ffd49csergeyv        : layout(layout)
179dccca44ffda4836b56a21da95a046c9708ffd49csergeyv        , canvas(canvas)
180dccca44ffda4836b56a21da95a046c9708ffd49csergeyv        , hOffset(hOffset)
181dccca44ffda4836b56a21da95a046c9708ffd49csergeyv        , vOffset(vOffset)
182dccca44ffda4836b56a21da95a046c9708ffd49csergeyv        , paint(paint)
183dccca44ffda4836b56a21da95a046c9708ffd49csergeyv        , path(path) {
184dccca44ffda4836b56a21da95a046c9708ffd49csergeyv    }
185dccca44ffda4836b56a21da95a046c9708ffd49csergeyv
186dccca44ffda4836b56a21da95a046c9708ffd49csergeyv    void operator()(size_t start, size_t end) {
187dccca44ffda4836b56a21da95a046c9708ffd49csergeyv        uint16_t glyphs[1];
188dccca44ffda4836b56a21da95a046c9708ffd49csergeyv        for (size_t i = start; i < end; i++) {
189dccca44ffda4836b56a21da95a046c9708ffd49csergeyv            glyphs[0] = layout.getGlyphId(i);
190dccca44ffda4836b56a21da95a046c9708ffd49csergeyv            float x = hOffset + layout.getX(i);
191dccca44ffda4836b56a21da95a046c9708ffd49csergeyv            float y = vOffset + layout.getY(i);
192dccca44ffda4836b56a21da95a046c9708ffd49csergeyv            canvas->drawGlyphsOnPath(glyphs, 1, path, x, y, paint);
193dccca44ffda4836b56a21da95a046c9708ffd49csergeyv        }
194dccca44ffda4836b56a21da95a046c9708ffd49csergeyv    }
195dccca44ffda4836b56a21da95a046c9708ffd49csergeyvprivate:
196dccca44ffda4836b56a21da95a046c9708ffd49csergeyv    const Layout& layout;
197dccca44ffda4836b56a21da95a046c9708ffd49csergeyv    Canvas* canvas;
198dccca44ffda4836b56a21da95a046c9708ffd49csergeyv    float hOffset;
199dccca44ffda4836b56a21da95a046c9708ffd49csergeyv    float vOffset;
200dccca44ffda4836b56a21da95a046c9708ffd49csergeyv    const Paint& paint;
201dccca44ffda4836b56a21da95a046c9708ffd49csergeyv    const SkPath& path;
202dccca44ffda4836b56a21da95a046c9708ffd49csergeyv};
203dccca44ffda4836b56a21da95a046c9708ffd49csergeyv
204dccca44ffda4836b56a21da95a046c9708ffd49csergeyvvoid Canvas::drawTextOnPath(const uint16_t* text, int count, int bidiFlags, const SkPath& path,
205bad99183916ba2bac6659efc8a28273e344ba511sergeyv        float hOffset, float vOffset, const Paint& paint, Typeface* typeface) {
206dccca44ffda4836b56a21da95a046c9708ffd49csergeyv    Paint paintCopy(paint);
207dccca44ffda4836b56a21da95a046c9708ffd49csergeyv    Layout layout;
208dccca44ffda4836b56a21da95a046c9708ffd49csergeyv    MinikinUtils::doLayout(&layout, &paintCopy, bidiFlags, typeface, text, 0, count, count);
209dccca44ffda4836b56a21da95a046c9708ffd49csergeyv    hOffset += MinikinUtils::hOffsetForTextAlign(&paintCopy, layout, path);
210dccca44ffda4836b56a21da95a046c9708ffd49csergeyv
211dccca44ffda4836b56a21da95a046c9708ffd49csergeyv    // Set align to left for drawing, as we don't want individual
212dccca44ffda4836b56a21da95a046c9708ffd49csergeyv    // glyphs centered or right-aligned; the offset above takes
213dccca44ffda4836b56a21da95a046c9708ffd49csergeyv    // care of all alignment.
214dccca44ffda4836b56a21da95a046c9708ffd49csergeyv    paintCopy.setTextAlign(Paint::kLeft_Align);
215dccca44ffda4836b56a21da95a046c9708ffd49csergeyv
216dccca44ffda4836b56a21da95a046c9708ffd49csergeyv    DrawTextOnPathFunctor f(layout, this, hOffset, vOffset, paintCopy, path);
217dccca44ffda4836b56a21da95a046c9708ffd49csergeyv    MinikinUtils::forFontRun(layout, &paintCopy, f);
218dccca44ffda4836b56a21da95a046c9708ffd49csergeyv}
219dccca44ffda4836b56a21da95a046c9708ffd49csergeyv
220dccca44ffda4836b56a21da95a046c9708ffd49csergeyv} // namespace android
221