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 "DisplayListCanvas.h"
20 #include "RecordingCanvas.h"
21 #include "MinikinUtils.h"
22 #include "Paint.h"
23 #include "Typeface.h"
24 
25 #include <SkDrawFilter.h>
26 
27 namespace android {
28 
create_recording_canvas(int width,int height)29 Canvas* Canvas::create_recording_canvas(int width, int height) {
30 #if HWUI_NEW_OPS
31     return new uirenderer::RecordingCanvas(width, height);
32 #else
33     return new uirenderer::DisplayListCanvas(width, height);
34 #endif
35 }
36 
drawTextDecorations(float x,float y,float length,const SkPaint & paint)37 void Canvas::drawTextDecorations(float x, float y, float length, const SkPaint& paint) {
38     uint32_t flags;
39     SkDrawFilter* drawFilter = getDrawFilter();
40     if (drawFilter) {
41         SkPaint paintCopy(paint);
42         drawFilter->filter(&paintCopy, SkDrawFilter::kText_Type);
43         flags = paintCopy.getFlags();
44     } else {
45         flags = paint.getFlags();
46     }
47     if (flags & (SkPaint::kUnderlineText_Flag | SkPaint::kStrikeThruText_Flag)) {
48         // Same values used by Skia
49         const float kStdStrikeThru_Offset   = (-6.0f / 21.0f);
50         const float kStdUnderline_Offset    = (1.0f / 9.0f);
51         const float kStdUnderline_Thickness = (1.0f / 18.0f);
52 
53         SkScalar left = x;
54         SkScalar right = x + length;
55         float textSize = paint.getTextSize();
56         float strokeWidth = fmax(textSize * kStdUnderline_Thickness, 1.0f);
57         if (flags & SkPaint::kUnderlineText_Flag) {
58             SkScalar top = y + textSize * kStdUnderline_Offset - 0.5f * strokeWidth;
59             SkScalar bottom = y + textSize * kStdUnderline_Offset + 0.5f * strokeWidth;
60             drawRect(left, top, right, bottom, paint);
61         }
62         if (flags & SkPaint::kStrikeThruText_Flag) {
63             SkScalar top = y + textSize * kStdStrikeThru_Offset - 0.5f * strokeWidth;
64             SkScalar bottom = y + textSize * kStdStrikeThru_Offset + 0.5f * strokeWidth;
65             drawRect(left, top, right, bottom, paint);
66         }
67     }
68 }
69 
simplifyPaint(int color,SkPaint * paint)70 static void simplifyPaint(int color, SkPaint* paint) {
71     paint->setColor(color);
72     paint->setShader(nullptr);
73     paint->setColorFilter(nullptr);
74     paint->setLooper(nullptr);
75     paint->setStrokeWidth(4 + 0.04 * paint->getTextSize());
76     paint->setStrokeJoin(SkPaint::kRound_Join);
77     paint->setLooper(nullptr);
78 }
79 
80 class DrawTextFunctor {
81 public:
DrawTextFunctor(const Layout & layout,Canvas * canvas,uint16_t * glyphs,float * pos,const SkPaint & paint,float x,float y,MinikinRect & bounds,float totalAdvance)82     DrawTextFunctor(const Layout& layout, Canvas* canvas, uint16_t* glyphs, float* pos,
83             const SkPaint& paint, float x, float y, MinikinRect& bounds, float totalAdvance)
84         : layout(layout)
85         , canvas(canvas)
86         , glyphs(glyphs)
87         , pos(pos)
88         , paint(paint)
89         , x(x)
90         , y(y)
91         , bounds(bounds)
92         , totalAdvance(totalAdvance) {
93     }
94 
operator ()(size_t start,size_t end)95     void operator()(size_t start, size_t end) {
96         if (canvas->drawTextAbsolutePos()) {
97             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     }
137 private:
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 
drawText(const uint16_t * text,int start,int count,int contextCount,float x,float y,int bidiFlags,const Paint & origPaint,Typeface * typeface)149 void 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 
179 class DrawTextOnPathFunctor {
180 public:
DrawTextOnPathFunctor(const Layout & layout,Canvas * canvas,float hOffset,float vOffset,const Paint & paint,const SkPath & path)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 
operator ()(size_t start,size_t end)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     }
200 private:
201     const Layout& layout;
202     Canvas* canvas;
203     float hOffset;
204     float vOffset;
205     const Paint& paint;
206     const SkPath& path;
207 };
208 
drawTextOnPath(const uint16_t * text,int count,int bidiFlags,const SkPath & path,float hOffset,float vOffset,const Paint & paint,Typeface * typeface)209 void 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