blob: a087035fd8e570080770614be6d0fb182faeae45 [file] [log] [blame]
sergeyvdccca442016-03-21 15:38:21 -07001/*
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
sergeyvdccca442016-03-21 15:38:21 -070019#include "RecordingCanvas.h"
Stan Ilievc0e7a902016-10-13 17:07:09 -040020#include "RenderNode.h"
sergeyvdccca442016-03-21 15:38:21 -070021#include "MinikinUtils.h"
22#include "Paint.h"
Stan Iliev500a0c32016-10-26 10:30:09 -040023#include "Properties.h"
24#include "pipeline/skia/SkiaRecordingCanvas.h"
sergeyvbad99182016-03-17 11:24:22 -070025#include "Typeface.h"
sergeyvdccca442016-03-21 15:38:21 -070026
27#include <SkDrawFilter.h>
28
29namespace android {
30
Stan Ilievc0e7a902016-10-13 17:07:09 -040031Canvas* Canvas::create_recording_canvas(int width, int height, uirenderer::RenderNode* renderNode) {
Stan Iliev500a0c32016-10-26 10:30:09 -040032 if (uirenderer::Properties::isSkiaEnabled()) {
33 return new uirenderer::skiapipeline::SkiaRecordingCanvas(renderNode, width, height);
34 }
sergeyvdccca442016-03-21 15:38:21 -070035 return new uirenderer::RecordingCanvas(width, height);
sergeyvdccca442016-03-21 15:38:21 -070036}
37
Roozbeh Pournader1378a9d2017-07-13 12:45:20 -070038static inline void drawStroke(SkScalar left, SkScalar right, SkScalar top, SkScalar thickness,
39 const SkPaint& paint, Canvas* canvas) {
40 const SkScalar strokeWidth = fmax(thickness, 1.0f);
41 const SkScalar bottom = top + strokeWidth;
42 canvas->drawRect(left, top, right, bottom, paint);
43}
44
sergeyvdccca442016-03-21 15:38:21 -070045void Canvas::drawTextDecorations(float x, float y, float length, const SkPaint& paint) {
46 uint32_t flags;
47 SkDrawFilter* drawFilter = getDrawFilter();
48 if (drawFilter) {
49 SkPaint paintCopy(paint);
50 drawFilter->filter(&paintCopy, SkDrawFilter::kText_Type);
51 flags = paintCopy.getFlags();
52 } else {
53 flags = paint.getFlags();
54 }
Mike Reedb216c212017-02-22 10:02:40 -050055 if (flags & (SkPaint::kUnderlineText_ReserveFlag | SkPaint::kStrikeThruText_ReserveFlag)) {
Roozbeh Pournaderca8a04a2017-06-06 18:30:29 -070056 const SkScalar left = x;
57 const SkScalar right = x + length;
Mike Reedb216c212017-02-22 10:02:40 -050058 if (flags & SkPaint::kUnderlineText_ReserveFlag) {
Roozbeh Pournaderca8a04a2017-06-06 18:30:29 -070059 Paint::FontMetrics metrics;
60 paint.getFontMetrics(&metrics);
61 SkScalar position;
62 if (!metrics.hasUnderlinePosition(&position)) {
63 position = paint.getTextSize() * Paint::kStdUnderline_Top;
64 }
65 SkScalar thickness;
66 if (!metrics.hasUnderlineThickness(&thickness)) {
67 thickness = paint.getTextSize() * Paint::kStdUnderline_Thickness;
68 }
Roozbeh Pournaderca8a04a2017-06-06 18:30:29 -070069 const SkScalar top = y + position;
Roozbeh Pournader1378a9d2017-07-13 12:45:20 -070070 drawStroke(left, right, top, thickness, paint, this);
sergeyvdccca442016-03-21 15:38:21 -070071 }
Mike Reedb216c212017-02-22 10:02:40 -050072 if (flags & SkPaint::kStrikeThruText_ReserveFlag) {
Roozbeh Pournaderca8a04a2017-06-06 18:30:29 -070073 const float textSize = paint.getTextSize();
Roozbeh Pournader1378a9d2017-07-13 12:45:20 -070074 const float position = textSize * Paint::kStdStrikeThru_Top;
75 const SkScalar thickness = textSize * Paint::kStdStrikeThru_Thickness;
76 const SkScalar top = y + position;
77 drawStroke(left, right, top, thickness, paint, this);
sergeyvdccca442016-03-21 15:38:21 -070078 }
79 }
80}
81
82static void simplifyPaint(int color, SkPaint* paint) {
83 paint->setColor(color);
84 paint->setShader(nullptr);
85 paint->setColorFilter(nullptr);
86 paint->setLooper(nullptr);
87 paint->setStrokeWidth(4 + 0.04 * paint->getTextSize());
88 paint->setStrokeJoin(SkPaint::kRound_Join);
89 paint->setLooper(nullptr);
90}
91
92class DrawTextFunctor {
93public:
Stan Iliev0b58d992017-03-30 18:22:27 -040094 DrawTextFunctor(const minikin::Layout& layout, Canvas* canvas,
Seigo Nonakaae1aa852016-06-09 19:42:51 +090095 const SkPaint& paint, float x, float y, minikin::MinikinRect& bounds,
96 float totalAdvance)
sergeyvdccca442016-03-21 15:38:21 -070097 : layout(layout)
98 , canvas(canvas)
sergeyvdccca442016-03-21 15:38:21 -070099 , paint(paint)
100 , x(x)
101 , y(y)
102 , bounds(bounds)
103 , totalAdvance(totalAdvance) {
104 }
105
106 void operator()(size_t start, size_t end) {
Stan Iliev0b58d992017-03-30 18:22:27 -0400107 auto glyphFunc = [&] (uint16_t* text, float* positions) {
108 if (canvas->drawTextAbsolutePos()) {
109 for (size_t i = start, textIndex = 0, posIndex = 0; i < end; i++) {
110 text[textIndex++] = layout.getGlyphId(i);
111 positions[posIndex++] = x + layout.getX(i);
112 positions[posIndex++] = y + layout.getY(i);
113 }
114 } else {
115 for (size_t i = start, textIndex = 0, posIndex = 0; i < end; i++) {
116 text[textIndex++] = layout.getGlyphId(i);
117 positions[posIndex++] = layout.getX(i);
118 positions[posIndex++] = layout.getY(i);
119 }
sergeyvdccca442016-03-21 15:38:21 -0700120 }
Stan Iliev0b58d992017-03-30 18:22:27 -0400121 };
sergeyvdccca442016-03-21 15:38:21 -0700122
123 size_t glyphCount = end - start;
124
125 if (CC_UNLIKELY(canvas->isHighContrastText() && paint.getAlpha() != 0)) {
126 // high contrast draw path
127 int color = paint.getColor();
128 int channelSum = SkColorGetR(color) + SkColorGetG(color) + SkColorGetB(color);
129 bool darken = channelSum < (128 * 3);
130
131 // outline
132 SkPaint outlinePaint(paint);
133 simplifyPaint(darken ? SK_ColorWHITE : SK_ColorBLACK, &outlinePaint);
134 outlinePaint.setStyle(SkPaint::kStrokeAndFill_Style);
Stan Iliev0b58d992017-03-30 18:22:27 -0400135 canvas->drawGlyphs(glyphFunc, glyphCount, outlinePaint, x, y, bounds.mLeft, bounds.mTop,
136 bounds.mRight, bounds.mBottom, totalAdvance);
sergeyvdccca442016-03-21 15:38:21 -0700137
138 // inner
139 SkPaint innerPaint(paint);
140 simplifyPaint(darken ? SK_ColorBLACK : SK_ColorWHITE, &innerPaint);
141 innerPaint.setStyle(SkPaint::kFill_Style);
Stan Iliev0b58d992017-03-30 18:22:27 -0400142 canvas->drawGlyphs(glyphFunc, glyphCount, innerPaint, x, y, bounds.mLeft, bounds.mTop,
143 bounds.mRight, bounds.mBottom, totalAdvance);
sergeyvdccca442016-03-21 15:38:21 -0700144 } else {
145 // standard draw path
Stan Iliev0b58d992017-03-30 18:22:27 -0400146 canvas->drawGlyphs(glyphFunc, glyphCount, paint, x, y, bounds.mLeft, bounds.mTop,
147 bounds.mRight, bounds.mBottom, totalAdvance);
sergeyvdccca442016-03-21 15:38:21 -0700148 }
149 }
150private:
Seigo Nonakaae1aa852016-06-09 19:42:51 +0900151 const minikin::Layout& layout;
sergeyvdccca442016-03-21 15:38:21 -0700152 Canvas* canvas;
sergeyvdccca442016-03-21 15:38:21 -0700153 const SkPaint& paint;
154 float x;
155 float y;
Seigo Nonakaae1aa852016-06-09 19:42:51 +0900156 minikin::MinikinRect& bounds;
sergeyvdccca442016-03-21 15:38:21 -0700157 float totalAdvance;
158};
159
160void Canvas::drawText(const uint16_t* text, int start, int count, int contextCount,
Seigo Nonaka318ca042017-08-01 16:36:18 -0700161 float x, float y, int bidiFlags, const Paint& origPaint, const Typeface* typeface) {
sergeyvdccca442016-03-21 15:38:21 -0700162 // minikin may modify the original paint
163 Paint paint(origPaint);
164
Seigo Nonakac7064142017-02-10 16:53:31 +0900165 minikin::Layout layout = MinikinUtils::doLayout(
166 &paint, bidiFlags, typeface, text, start, count, contextCount);
sergeyvdccca442016-03-21 15:38:21 -0700167
sergeyvdccca442016-03-21 15:38:21 -0700168 x += MinikinUtils::xOffsetForTextAlign(&paint, layout);
169
Seigo Nonakaae1aa852016-06-09 19:42:51 +0900170 minikin::MinikinRect bounds;
sergeyvdccca442016-03-21 15:38:21 -0700171 layout.getBounds(&bounds);
172 if (!drawTextAbsolutePos()) {
173 bounds.offset(x, y);
174 }
175
Derek Sollenberger79abbf22016-03-24 11:07:19 -0400176 // Set align to left for drawing, as we don't want individual
177 // glyphs centered or right-aligned; the offset above takes
178 // care of all alignment.
179 paint.setTextAlign(Paint::kLeft_Align);
180
Stan Iliev0b58d992017-03-30 18:22:27 -0400181 DrawTextFunctor f(layout, this, paint, x, y, bounds, layout.getAdvance());
sergeyvdccca442016-03-21 15:38:21 -0700182 MinikinUtils::forFontRun(layout, &paint, f);
183}
184
185class DrawTextOnPathFunctor {
186public:
Seigo Nonakaae1aa852016-06-09 19:42:51 +0900187 DrawTextOnPathFunctor(const minikin::Layout& layout, Canvas* canvas, float hOffset,
sergeyvdccca442016-03-21 15:38:21 -0700188 float vOffset, const Paint& paint, const SkPath& path)
189 : layout(layout)
190 , canvas(canvas)
191 , hOffset(hOffset)
192 , vOffset(vOffset)
193 , paint(paint)
194 , path(path) {
195 }
196
197 void operator()(size_t start, size_t end) {
Yuqian Liafc221492016-07-18 13:07:42 -0400198 canvas->drawLayoutOnPath(layout, hOffset, vOffset, paint, path, start, end);
sergeyvdccca442016-03-21 15:38:21 -0700199 }
200private:
Seigo Nonakaae1aa852016-06-09 19:42:51 +0900201 const minikin::Layout& layout;
sergeyvdccca442016-03-21 15:38:21 -0700202 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,
Seigo Nonaka318ca042017-08-01 16:36:18 -0700210 float hOffset, float vOffset, const Paint& paint, const Typeface* typeface) {
sergeyvdccca442016-03-21 15:38:21 -0700211 Paint paintCopy(paint);
Seigo Nonakac7064142017-02-10 16:53:31 +0900212 minikin::Layout layout = MinikinUtils::doLayout(
213 &paintCopy, bidiFlags, typeface, text, 0, count, count);
sergeyvdccca442016-03-21 15:38:21 -0700214 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