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