Canvas.cpp revision 79abbf22d4f672208327546661e694d837f564a9
125bf8f5d5678e747933ae9e4051daa99ddfcfaa3Hugo Benichi/*
225bf8f5d5678e747933ae9e4051daa99ddfcfaa3Hugo Benichi * Copyright (C) 2015 The Android Open Source Project
325bf8f5d5678e747933ae9e4051daa99ddfcfaa3Hugo Benichi *
425bf8f5d5678e747933ae9e4051daa99ddfcfaa3Hugo Benichi * Licensed under the Apache License, Version 2.0 (the "License");
525bf8f5d5678e747933ae9e4051daa99ddfcfaa3Hugo Benichi * you may not use this file except in compliance with the License.
625bf8f5d5678e747933ae9e4051daa99ddfcfaa3Hugo Benichi * You may obtain a copy of the License at
725bf8f5d5678e747933ae9e4051daa99ddfcfaa3Hugo Benichi *
825bf8f5d5678e747933ae9e4051daa99ddfcfaa3Hugo Benichi *      http://www.apache.org/licenses/LICENSE-2.0
925bf8f5d5678e747933ae9e4051daa99ddfcfaa3Hugo Benichi *
1025bf8f5d5678e747933ae9e4051daa99ddfcfaa3Hugo Benichi * Unless required by applicable law or agreed to in writing, software
1125bf8f5d5678e747933ae9e4051daa99ddfcfaa3Hugo Benichi * distributed under the License is distributed on an "AS IS" BASIS,
1225bf8f5d5678e747933ae9e4051daa99ddfcfaa3Hugo Benichi * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
1325bf8f5d5678e747933ae9e4051daa99ddfcfaa3Hugo Benichi * See the License for the specific language governing permissions and
1425bf8f5d5678e747933ae9e4051daa99ddfcfaa3Hugo Benichi * limitations under the License.
1525bf8f5d5678e747933ae9e4051daa99ddfcfaa3Hugo Benichi */
1625bf8f5d5678e747933ae9e4051daa99ddfcfaa3Hugo Benichi
1725bf8f5d5678e747933ae9e4051daa99ddfcfaa3Hugo Benichi#include "Canvas.h"
1825bf8f5d5678e747933ae9e4051daa99ddfcfaa3Hugo Benichi
1925bf8f5d5678e747933ae9e4051daa99ddfcfaa3Hugo Benichi#include "DisplayListCanvas.h"
2025bf8f5d5678e747933ae9e4051daa99ddfcfaa3Hugo Benichi#include "RecordingCanvas.h"
2125bf8f5d5678e747933ae9e4051daa99ddfcfaa3Hugo Benichi#include "MinikinUtils.h"
225df9d729f6c78215f287701d7e136dfc922e2bd3Hugo Benichi#include "Paint.h"
235df9d729f6c78215f287701d7e136dfc922e2bd3Hugo Benichi#include "Typeface.h"
245df9d729f6c78215f287701d7e136dfc922e2bd3Hugo Benichi
2525bf8f5d5678e747933ae9e4051daa99ddfcfaa3Hugo Benichi#include <SkDrawFilter.h>
2625bf8f5d5678e747933ae9e4051daa99ddfcfaa3Hugo Benichi
2725bf8f5d5678e747933ae9e4051daa99ddfcfaa3Hugo Benichinamespace android {
2825bf8f5d5678e747933ae9e4051daa99ddfcfaa3Hugo Benichi
2925bf8f5d5678e747933ae9e4051daa99ddfcfaa3Hugo BenichiCanvas* Canvas::create_recording_canvas(int width, int height) {
3025bf8f5d5678e747933ae9e4051daa99ddfcfaa3Hugo Benichi#if HWUI_NEW_OPS
3125bf8f5d5678e747933ae9e4051daa99ddfcfaa3Hugo Benichi    return new uirenderer::RecordingCanvas(width, height);
3225bf8f5d5678e747933ae9e4051daa99ddfcfaa3Hugo Benichi#else
3325bf8f5d5678e747933ae9e4051daa99ddfcfaa3Hugo Benichi    return new uirenderer::DisplayListCanvas(width, height);
3425bf8f5d5678e747933ae9e4051daa99ddfcfaa3Hugo Benichi#endif
3525bf8f5d5678e747933ae9e4051daa99ddfcfaa3Hugo Benichi}
3625bf8f5d5678e747933ae9e4051daa99ddfcfaa3Hugo Benichi
3725bf8f5d5678e747933ae9e4051daa99ddfcfaa3Hugo Benichivoid Canvas::drawTextDecorations(float x, float y, float length, const SkPaint& paint) {
3825bf8f5d5678e747933ae9e4051daa99ddfcfaa3Hugo Benichi    uint32_t flags;
3925bf8f5d5678e747933ae9e4051daa99ddfcfaa3Hugo Benichi    SkDrawFilter* drawFilter = getDrawFilter();
4025bf8f5d5678e747933ae9e4051daa99ddfcfaa3Hugo Benichi    if (drawFilter) {
4125bf8f5d5678e747933ae9e4051daa99ddfcfaa3Hugo Benichi        SkPaint paintCopy(paint);
4225bf8f5d5678e747933ae9e4051daa99ddfcfaa3Hugo Benichi        drawFilter->filter(&paintCopy, SkDrawFilter::kText_Type);
4325bf8f5d5678e747933ae9e4051daa99ddfcfaa3Hugo Benichi        flags = paintCopy.getFlags();
4425bf8f5d5678e747933ae9e4051daa99ddfcfaa3Hugo Benichi    } else {
4525bf8f5d5678e747933ae9e4051daa99ddfcfaa3Hugo Benichi        flags = paint.getFlags();
4625bf8f5d5678e747933ae9e4051daa99ddfcfaa3Hugo Benichi    }
4725bf8f5d5678e747933ae9e4051daa99ddfcfaa3Hugo Benichi    if (flags & (SkPaint::kUnderlineText_Flag | SkPaint::kStrikeThruText_Flag)) {
4825bf8f5d5678e747933ae9e4051daa99ddfcfaa3Hugo Benichi        // Same values used by Skia
4925bf8f5d5678e747933ae9e4051daa99ddfcfaa3Hugo Benichi        const float kStdStrikeThru_Offset   = (-6.0f / 21.0f);
5025bf8f5d5678e747933ae9e4051daa99ddfcfaa3Hugo Benichi        const float kStdUnderline_Offset    = (1.0f / 9.0f);
5125bf8f5d5678e747933ae9e4051daa99ddfcfaa3Hugo Benichi        const float kStdUnderline_Thickness = (1.0f / 18.0f);
5225bf8f5d5678e747933ae9e4051daa99ddfcfaa3Hugo Benichi
5325bf8f5d5678e747933ae9e4051daa99ddfcfaa3Hugo Benichi        SkScalar left = x;
5425bf8f5d5678e747933ae9e4051daa99ddfcfaa3Hugo Benichi        SkScalar right = x + length;
5525bf8f5d5678e747933ae9e4051daa99ddfcfaa3Hugo Benichi        float textSize = paint.getTextSize();
5625bf8f5d5678e747933ae9e4051daa99ddfcfaa3Hugo Benichi        float strokeWidth = fmax(textSize * kStdUnderline_Thickness, 1.0f);
5725bf8f5d5678e747933ae9e4051daa99ddfcfaa3Hugo Benichi        if (flags & SkPaint::kUnderlineText_Flag) {
5825bf8f5d5678e747933ae9e4051daa99ddfcfaa3Hugo Benichi            SkScalar top = y + textSize * kStdUnderline_Offset - 0.5f * strokeWidth;
5925bf8f5d5678e747933ae9e4051daa99ddfcfaa3Hugo Benichi            SkScalar bottom = y + textSize * kStdUnderline_Offset + 0.5f * strokeWidth;
6025bf8f5d5678e747933ae9e4051daa99ddfcfaa3Hugo Benichi            drawRect(left, top, right, bottom, paint);
6125bf8f5d5678e747933ae9e4051daa99ddfcfaa3Hugo Benichi        }
6225bf8f5d5678e747933ae9e4051daa99ddfcfaa3Hugo Benichi        if (flags & SkPaint::kStrikeThruText_Flag) {
6325bf8f5d5678e747933ae9e4051daa99ddfcfaa3Hugo Benichi            SkScalar top = y + textSize * kStdStrikeThru_Offset - 0.5f * strokeWidth;
6425bf8f5d5678e747933ae9e4051daa99ddfcfaa3Hugo Benichi            SkScalar bottom = y + textSize * kStdStrikeThru_Offset + 0.5f * strokeWidth;
6525bf8f5d5678e747933ae9e4051daa99ddfcfaa3Hugo Benichi            drawRect(left, top, right, bottom, paint);
6625bf8f5d5678e747933ae9e4051daa99ddfcfaa3Hugo Benichi        }
6725bf8f5d5678e747933ae9e4051daa99ddfcfaa3Hugo Benichi    }
6825bf8f5d5678e747933ae9e4051daa99ddfcfaa3Hugo Benichi}
6925bf8f5d5678e747933ae9e4051daa99ddfcfaa3Hugo Benichi
7025bf8f5d5678e747933ae9e4051daa99ddfcfaa3Hugo Benichistatic void simplifyPaint(int color, SkPaint* paint) {
7125bf8f5d5678e747933ae9e4051daa99ddfcfaa3Hugo Benichi    paint->setColor(color);
7225bf8f5d5678e747933ae9e4051daa99ddfcfaa3Hugo Benichi    paint->setShader(nullptr);
7325bf8f5d5678e747933ae9e4051daa99ddfcfaa3Hugo Benichi    paint->setColorFilter(nullptr);
7425bf8f5d5678e747933ae9e4051daa99ddfcfaa3Hugo Benichi    paint->setLooper(nullptr);
7561cbccc2bf7983b50e7a7f1fdb1858caeab6fd96Hugo Benichi    paint->setStrokeWidth(4 + 0.04 * paint->getTextSize());
7625bf8f5d5678e747933ae9e4051daa99ddfcfaa3Hugo Benichi    paint->setStrokeJoin(SkPaint::kRound_Join);
7725bf8f5d5678e747933ae9e4051daa99ddfcfaa3Hugo Benichi    paint->setLooper(nullptr);
7825bf8f5d5678e747933ae9e4051daa99ddfcfaa3Hugo Benichi}
7961cbccc2bf7983b50e7a7f1fdb1858caeab6fd96Hugo Benichi
8025bf8f5d5678e747933ae9e4051daa99ddfcfaa3Hugo Benichiclass DrawTextFunctor {
8125bf8f5d5678e747933ae9e4051daa99ddfcfaa3Hugo Benichipublic:
8225bf8f5d5678e747933ae9e4051daa99ddfcfaa3Hugo Benichi    DrawTextFunctor(const Layout& layout, Canvas* canvas, uint16_t* glyphs, float* pos,
8361cbccc2bf7983b50e7a7f1fdb1858caeab6fd96Hugo Benichi            const SkPaint& paint, float x, float y, MinikinRect& bounds, float totalAdvance)
8425bf8f5d5678e747933ae9e4051daa99ddfcfaa3Hugo Benichi        : layout(layout)
855df9d729f6c78215f287701d7e136dfc922e2bd3Hugo Benichi        , canvas(canvas)
865df9d729f6c78215f287701d7e136dfc922e2bd3Hugo Benichi        , glyphs(glyphs)
875df9d729f6c78215f287701d7e136dfc922e2bd3Hugo Benichi        , pos(pos)
885df9d729f6c78215f287701d7e136dfc922e2bd3Hugo Benichi        , paint(paint)
895df9d729f6c78215f287701d7e136dfc922e2bd3Hugo Benichi        , x(x)
905df9d729f6c78215f287701d7e136dfc922e2bd3Hugo Benichi        , y(y)
915df9d729f6c78215f287701d7e136dfc922e2bd3Hugo Benichi        , bounds(bounds)
925df9d729f6c78215f287701d7e136dfc922e2bd3Hugo Benichi        , totalAdvance(totalAdvance) {
935df9d729f6c78215f287701d7e136dfc922e2bd3Hugo Benichi    }
945df9d729f6c78215f287701d7e136dfc922e2bd3Hugo Benichi
955df9d729f6c78215f287701d7e136dfc922e2bd3Hugo Benichi    void operator()(size_t start, size_t end) {
965df9d729f6c78215f287701d7e136dfc922e2bd3Hugo Benichi        if (canvas->drawTextAbsolutePos()) {
9725bf8f5d5678e747933ae9e4051daa99ddfcfaa3Hugo Benichi            for (size_t i = start; i < end; i++) {
98                glyphs[i] = layout.getGlyphId(i);
99                pos[2 * i] = x + layout.getX(i);
100                pos[2 * i + 1] = y + layout.getY(i);
101            }
102        } else {
103            for (size_t i = start; i < end; i++) {
104                glyphs[i] = layout.getGlyphId(i);
105                pos[2 * i] = layout.getX(i);
106                pos[2 * i + 1] = layout.getY(i);
107            }
108        }
109
110        size_t glyphCount = end - start;
111
112        if (CC_UNLIKELY(canvas->isHighContrastText() && paint.getAlpha() != 0)) {
113            // high contrast draw path
114            int color = paint.getColor();
115            int channelSum = SkColorGetR(color) + SkColorGetG(color) + SkColorGetB(color);
116            bool darken = channelSum < (128 * 3);
117
118            // outline
119            SkPaint outlinePaint(paint);
120            simplifyPaint(darken ? SK_ColorWHITE : SK_ColorBLACK, &outlinePaint);
121            outlinePaint.setStyle(SkPaint::kStrokeAndFill_Style);
122            canvas->drawGlyphs(glyphs + start, pos + (2 * start), glyphCount, outlinePaint, x, y,
123                    bounds.mLeft, bounds.mTop, bounds.mRight, bounds.mBottom, totalAdvance);
124
125            // inner
126            SkPaint innerPaint(paint);
127            simplifyPaint(darken ? SK_ColorBLACK : SK_ColorWHITE, &innerPaint);
128            innerPaint.setStyle(SkPaint::kFill_Style);
129            canvas->drawGlyphs(glyphs + start, pos + (2 * start), glyphCount, innerPaint, x, y,
130                    bounds.mLeft, bounds.mTop, bounds.mRight, bounds.mBottom, totalAdvance);
131        } else {
132            // standard draw path
133            canvas->drawGlyphs(glyphs + start, pos + (2 * start), glyphCount, paint, x, y,
134                    bounds.mLeft, bounds.mTop, bounds.mRight, bounds.mBottom, totalAdvance);
135        }
136    }
137private:
138    const Layout& layout;
139    Canvas* canvas;
140    uint16_t* glyphs;
141    float* pos;
142    const SkPaint& paint;
143    float x;
144    float y;
145    MinikinRect& bounds;
146    float totalAdvance;
147};
148
149void Canvas::drawText(const uint16_t* text, int start, int count, int contextCount,
150        float x, float y, int bidiFlags, const Paint& origPaint, Typeface* typeface) {
151    // minikin may modify the original paint
152    Paint paint(origPaint);
153
154    Layout layout;
155    MinikinUtils::doLayout(&layout, &paint, bidiFlags, typeface, text, start, count, contextCount);
156
157    size_t nGlyphs = layout.nGlyphs();
158    std::unique_ptr<uint16_t[]> glyphs(new uint16_t[nGlyphs]);
159    std::unique_ptr<float[]> pos(new float[nGlyphs * 2]);
160
161    x += MinikinUtils::xOffsetForTextAlign(&paint, layout);
162
163    MinikinRect bounds;
164    layout.getBounds(&bounds);
165    if (!drawTextAbsolutePos()) {
166        bounds.offset(x, y);
167    }
168
169    // Set align to left for drawing, as we don't want individual
170    // glyphs centered or right-aligned; the offset above takes
171    // care of all alignment.
172    paint.setTextAlign(Paint::kLeft_Align);
173
174    DrawTextFunctor f(layout, this, glyphs.get(), pos.get(),
175            paint, x, y, bounds, layout.getAdvance());
176    MinikinUtils::forFontRun(layout, &paint, f);
177}
178
179class DrawTextOnPathFunctor {
180public:
181    DrawTextOnPathFunctor(const Layout& layout, Canvas* canvas, float hOffset,
182            float vOffset, const Paint& paint, const SkPath& path)
183        : layout(layout)
184        , canvas(canvas)
185        , hOffset(hOffset)
186        , vOffset(vOffset)
187        , paint(paint)
188        , path(path) {
189    }
190
191    void operator()(size_t start, size_t end) {
192        uint16_t glyphs[1];
193        for (size_t i = start; i < end; i++) {
194            glyphs[0] = layout.getGlyphId(i);
195            float x = hOffset + layout.getX(i);
196            float y = vOffset + layout.getY(i);
197            canvas->drawGlyphsOnPath(glyphs, 1, path, x, y, paint);
198        }
199    }
200private:
201    const Layout& layout;
202    Canvas* canvas;
203    float hOffset;
204    float vOffset;
205    const Paint& paint;
206    const SkPath& path;
207};
208
209void Canvas::drawTextOnPath(const uint16_t* text, int count, int bidiFlags, const SkPath& path,
210        float hOffset, float vOffset, const Paint& paint, Typeface* typeface) {
211    Paint paintCopy(paint);
212    Layout layout;
213    MinikinUtils::doLayout(&layout, &paintCopy, bidiFlags, typeface, text, 0, count, count);
214    hOffset += MinikinUtils::hOffsetForTextAlign(&paintCopy, layout, path);
215
216    // Set align to left for drawing, as we don't want individual
217    // glyphs centered or right-aligned; the offset above takes
218    // care of all alignment.
219    paintCopy.setTextAlign(Paint::kLeft_Align);
220
221    DrawTextOnPathFunctor f(layout, this, hOffset, vOffset, paintCopy, path);
222    MinikinUtils::forFontRun(layout, &paintCopy, f);
223}
224
225} // namespace android
226