• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 "MinikinUtils.h"
20 #include "Paint.h"
21 #include "Properties.h"
22 #include "RecordingCanvas.h"
23 #include "RenderNode.h"
24 #include "Typeface.h"
25 #include "pipeline/skia/SkiaRecordingCanvas.h"
26 
27 #include <SkDrawFilter.h>
28 
29 namespace android {
30 
create_recording_canvas(int width,int height,uirenderer::RenderNode * renderNode)31 Canvas* 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 
drawStroke(SkScalar left,SkScalar right,SkScalar top,SkScalar thickness,const SkPaint & paint,Canvas * canvas)38 static 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 
drawTextDecorations(float x,float y,float length,const SkPaint & paint)45 void 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     }
55     if (flags & (SkPaint::kUnderlineText_ReserveFlag | SkPaint::kStrikeThruText_ReserveFlag)) {
56         const SkScalar left = x;
57         const SkScalar right = x + length;
58         if (flags & SkPaint::kUnderlineText_ReserveFlag) {
59             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             }
69             const SkScalar top = y + position;
70             drawStroke(left, right, top, thickness, paint, this);
71         }
72         if (flags & SkPaint::kStrikeThruText_ReserveFlag) {
73             const float textSize = paint.getTextSize();
74             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);
78         }
79     }
80 }
81 
simplifyPaint(int color,SkPaint * paint)82 static 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 
92 class DrawTextFunctor {
93 public:
DrawTextFunctor(const minikin::Layout & layout,Canvas * canvas,const SkPaint & paint,float x,float y,minikin::MinikinRect & bounds,float totalAdvance)94     DrawTextFunctor(const minikin::Layout& layout, Canvas* canvas, const SkPaint& paint, float x,
95                     float y, minikin::MinikinRect& bounds, float totalAdvance)
96             : layout(layout)
97             , canvas(canvas)
98             , paint(paint)
99             , x(x)
100             , y(y)
101             , bounds(bounds)
102             , totalAdvance(totalAdvance) {}
103 
operator ()(size_t start,size_t end)104     void operator()(size_t start, size_t end) {
105         auto glyphFunc = [&](uint16_t* text, float* positions) {
106             if (canvas->drawTextAbsolutePos()) {
107                 for (size_t i = start, textIndex = 0, posIndex = 0; i < end; i++) {
108                     text[textIndex++] = layout.getGlyphId(i);
109                     positions[posIndex++] = x + layout.getX(i);
110                     positions[posIndex++] = y + layout.getY(i);
111                 }
112             } else {
113                 for (size_t i = start, textIndex = 0, posIndex = 0; i < end; i++) {
114                     text[textIndex++] = layout.getGlyphId(i);
115                     positions[posIndex++] = layout.getX(i);
116                     positions[posIndex++] = layout.getY(i);
117                 }
118             }
119         };
120 
121         size_t glyphCount = end - start;
122 
123         if (CC_UNLIKELY(canvas->isHighContrastText() && paint.getAlpha() != 0)) {
124             // high contrast draw path
125             int color = paint.getColor();
126             int channelSum = SkColorGetR(color) + SkColorGetG(color) + SkColorGetB(color);
127             bool darken = channelSum < (128 * 3);
128 
129             // outline
130             SkPaint outlinePaint(paint);
131             simplifyPaint(darken ? SK_ColorWHITE : SK_ColorBLACK, &outlinePaint);
132             outlinePaint.setStyle(SkPaint::kStrokeAndFill_Style);
133             canvas->drawGlyphs(glyphFunc, glyphCount, outlinePaint, x, y, bounds.mLeft, bounds.mTop,
134                                bounds.mRight, bounds.mBottom, totalAdvance);
135 
136             // inner
137             SkPaint innerPaint(paint);
138             simplifyPaint(darken ? SK_ColorBLACK : SK_ColorWHITE, &innerPaint);
139             innerPaint.setStyle(SkPaint::kFill_Style);
140             canvas->drawGlyphs(glyphFunc, glyphCount, innerPaint, x, y, bounds.mLeft, bounds.mTop,
141                                bounds.mRight, bounds.mBottom, totalAdvance);
142         } else {
143             // standard draw path
144             canvas->drawGlyphs(glyphFunc, glyphCount, paint, x, y, bounds.mLeft, bounds.mTop,
145                                bounds.mRight, bounds.mBottom, totalAdvance);
146         }
147     }
148 
149 private:
150     const minikin::Layout& layout;
151     Canvas* canvas;
152     const SkPaint& paint;
153     float x;
154     float y;
155     minikin::MinikinRect& bounds;
156     float totalAdvance;
157 };
158 
drawText(const uint16_t * text,int start,int count,int contextCount,float x,float y,minikin::Bidi bidiFlags,const Paint & origPaint,const Typeface * typeface,minikin::MeasuredText * mt)159 void Canvas::drawText(const uint16_t* text, int start, int count, int contextCount, float x,
160                       float y, minikin::Bidi bidiFlags, const Paint& origPaint,
161                       const Typeface* typeface, minikin::MeasuredText* mt) {
162     // minikin may modify the original paint
163     Paint paint(origPaint);
164 
165     minikin::Layout layout =
166             MinikinUtils::doLayout(&paint, bidiFlags, typeface, text, start, count, contextCount,
167                                    mt);
168 
169     x += MinikinUtils::xOffsetForTextAlign(&paint, layout);
170 
171     minikin::MinikinRect bounds;
172     layout.getBounds(&bounds);
173     if (!drawTextAbsolutePos()) {
174         bounds.offset(x, y);
175     }
176 
177     // Set align to left for drawing, as we don't want individual
178     // glyphs centered or right-aligned; the offset above takes
179     // care of all alignment.
180     paint.setTextAlign(Paint::kLeft_Align);
181 
182     DrawTextFunctor f(layout, this, paint, x, y, bounds, layout.getAdvance());
183     MinikinUtils::forFontRun(layout, &paint, f);
184 }
185 
186 class DrawTextOnPathFunctor {
187 public:
DrawTextOnPathFunctor(const minikin::Layout & layout,Canvas * canvas,float hOffset,float vOffset,const Paint & paint,const SkPath & path)188     DrawTextOnPathFunctor(const minikin::Layout& layout, Canvas* canvas, float hOffset,
189                           float vOffset, const Paint& paint, const SkPath& path)
190             : layout(layout)
191             , canvas(canvas)
192             , hOffset(hOffset)
193             , vOffset(vOffset)
194             , paint(paint)
195             , path(path) {}
196 
operator ()(size_t start,size_t end)197     void operator()(size_t start, size_t end) {
198         canvas->drawLayoutOnPath(layout, hOffset, vOffset, paint, path, start, end);
199     }
200 
201 private:
202     const minikin::Layout& layout;
203     Canvas* canvas;
204     float hOffset;
205     float vOffset;
206     const Paint& paint;
207     const SkPath& path;
208 };
209 
drawTextOnPath(const uint16_t * text,int count,minikin::Bidi bidiFlags,const SkPath & path,float hOffset,float vOffset,const Paint & paint,const Typeface * typeface)210 void Canvas::drawTextOnPath(const uint16_t* text, int count, minikin::Bidi bidiFlags,
211                             const SkPath& path, float hOffset, float vOffset, const Paint& paint,
212                             const Typeface* typeface) {
213     Paint paintCopy(paint);
214     minikin::Layout layout =
215             MinikinUtils::doLayout(&paintCopy, bidiFlags, typeface, text, 0, count, count, nullptr);
216     hOffset += MinikinUtils::hOffsetForTextAlign(&paintCopy, layout, path);
217 
218     // Set align to left for drawing, as we don't want individual
219     // glyphs centered or right-aligned; the offset above takes
220     // care of all alignment.
221     paintCopy.setTextAlign(Paint::kLeft_Align);
222 
223     DrawTextOnPathFunctor f(layout, this, hOffset, vOffset, paintCopy, path);
224     MinikinUtils::forFontRun(layout, &paintCopy, f);
225 }
226 
227 int Canvas::sApiLevel = 1;
228 
setCompatibilityVersion(int apiLevel)229 void Canvas::setCompatibilityVersion(int apiLevel) {
230     sApiLevel = apiLevel;
231 }
232 
233 }  // namespace android
234