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