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