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