• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright 2015 Google Inc.
3  *
4  * Use of this source code is governed by a BSD-style license that can be
5  * found in the LICENSE file.
6  */
7 
8 #include "gm/gm.h"
9 #include "include/core/SkBlendMode.h"
10 #include "include/core/SkCanvas.h"
11 #include "include/core/SkColor.h"
12 #include "include/core/SkColorFilter.h"
13 #include "include/core/SkFilterQuality.h"
14 #include "include/core/SkFont.h"
15 #include "include/core/SkFontTypes.h"
16 #include "include/core/SkImage.h"
17 #include "include/core/SkImageInfo.h"
18 #include "include/core/SkPaint.h"
19 #include "include/core/SkPath.h"
20 #include "include/core/SkPathMeasure.h"
21 #include "include/core/SkPoint.h"
22 #include "include/core/SkRSXform.h"
23 #include "include/core/SkRect.h"
24 #include "include/core/SkRefCnt.h"
25 #include "include/core/SkScalar.h"
26 #include "include/core/SkShader.h"
27 #include "include/core/SkSize.h"
28 #include "include/core/SkString.h"
29 #include "include/core/SkSurface.h"
30 #include "include/core/SkTextBlob.h"
31 #include "include/core/SkTileMode.h"
32 #include "include/core/SkTypeface.h"
33 #include "include/core/SkTypes.h"
34 #include "include/core/SkVertices.h"
35 #include "include/effects/SkGradientShader.h"
36 #include "include/private/SkTemplates.h"
37 #include "src/core/SkAutoMalloc.h"
38 #include "src/core/SkFontPriv.h"
39 #include "tools/Resources.h"
40 #include "tools/ToolUtils.h"
41 
42 #include <initializer_list>
43 
44 class DrawAtlasGM : public skiagm::GM {
MakeAtlas(SkCanvas * caller,const SkRect & target)45     static sk_sp<SkImage> MakeAtlas(SkCanvas* caller, const SkRect& target) {
46         SkImageInfo info = SkImageInfo::MakeN32Premul(100, 100);
47         auto        surface(ToolUtils::makeSurface(caller, info));
48         SkCanvas* canvas = surface->getCanvas();
49         // draw red everywhere, but we don't expect to see it in the draw, testing the notion
50         // that drawAtlas draws a subset-region of the atlas.
51         canvas->clear(SK_ColorRED);
52 
53         SkPaint paint;
54         paint.setBlendMode(SkBlendMode::kClear);
55         SkRect r(target);
56         r.inset(-1, -1);
57         // zero out a place (with a 1-pixel border) to land our drawing.
58         canvas->drawRect(r, paint);
59         paint.setBlendMode(SkBlendMode::kSrcOver);
60         paint.setColor(SK_ColorBLUE);
61         paint.setAntiAlias(true);
62         canvas->drawOval(target, paint);
63         return surface->makeImageSnapshot();
64     }
65 
66 public:
DrawAtlasGM()67     DrawAtlasGM() {}
68 
69 protected:
70 
onShortName()71     SkString onShortName() override {
72         return SkString("draw-atlas");
73     }
74 
onISize()75     SkISize onISize() override {
76         return SkISize::Make(640, 480);
77     }
78 
onDraw(SkCanvas * canvas)79     void onDraw(SkCanvas* canvas) override {
80         const SkRect target = { 50, 50, 80, 90 };
81         auto atlas = MakeAtlas(canvas, target);
82 
83         const struct {
84             SkScalar fScale;
85             SkScalar fDegrees;
86             SkScalar fTx;
87             SkScalar fTy;
88 
89             void apply(SkRSXform* xform) const {
90                 const SkScalar rad = SkDegreesToRadians(fDegrees);
91                 xform->fSCos = fScale * SkScalarCos(rad);
92                 xform->fSSin = fScale * SkScalarSin(rad);
93                 xform->fTx   = fTx;
94                 xform->fTy   = fTy;
95             }
96         } rec[] = {
97             { 1, 0, 10, 10 },       // just translate
98             { 2, 0, 110, 10 },      // scale + translate
99             { 1, 30, 210, 10 },     // rotate + translate
100             { 2, -30, 310, 30 },    // scale + rotate + translate
101         };
102 
103         const int N = SK_ARRAY_COUNT(rec);
104         SkRSXform xform[N];
105         SkRect tex[N];
106         SkColor colors[N];
107 
108         for (int i = 0; i < N; ++i) {
109             rec[i].apply(&xform[i]);
110             tex[i] = target;
111             colors[i] = 0x80FF0000 + (i * 40 * 256);
112         }
113 
114         SkPaint paint;
115         paint.setFilterQuality(kLow_SkFilterQuality);
116         paint.setAntiAlias(true);
117 
118         canvas->drawAtlas(atlas.get(), xform, tex, N, nullptr, &paint);
119         canvas->translate(0, 100);
120         canvas->drawAtlas(atlas.get(), xform, tex, colors, N, SkBlendMode::kSrcIn, nullptr, &paint);
121     }
122 
123 private:
124     typedef GM INHERITED;
125 };
DEF_GM(return new DrawAtlasGM;)126 DEF_GM( return new DrawAtlasGM; )
127 
128 ///////////////////////////////////////////////////////////////////////////////////////////////////
129 
130 static void draw_text_on_path(SkCanvas* canvas, const void* text, size_t length,
131                               const SkPoint xy[], const SkPath& path, const SkFont& font, const SkPaint& paint,
132                               float baseline_offset) {
133     SkPathMeasure meas(path, false);
134 
135     int count = font.countText(text, length, SkTextEncoding::kUTF8);
136     size_t size = count * (sizeof(SkRSXform) + sizeof(SkScalar));
137     SkAutoSMalloc<512> storage(size);
138     SkRSXform* xform = (SkRSXform*)storage.get();
139     SkScalar* widths = (SkScalar*)(xform + count);
140 
141     // Compute a conservative bounds so we can cull the draw
142     const SkRect fontb = SkFontPriv::GetFontBounds(font);
143     const SkScalar max = SkTMax(SkTMax(SkScalarAbs(fontb.fLeft), SkScalarAbs(fontb.fRight)),
144                                 SkTMax(SkScalarAbs(fontb.fTop), SkScalarAbs(fontb.fBottom)));
145     const SkRect bounds = path.getBounds().makeOutset(max, max);
146 
147     SkAutoTArray<SkGlyphID> glyphs(count);
148     font.textToGlyphs(text, length, SkTextEncoding::kUTF8, glyphs.get(), count);
149     font.getWidths(glyphs.get(), count, widths);
150 
151     for (int i = 0; i < count; ++i) {
152         // we want to position each character on the center of its advance
153         const SkScalar offset = SkScalarHalf(widths[i]);
154         SkPoint pos;
155         SkVector tan;
156         if (!meas.getPosTan(xy[i].x() + offset, &pos, &tan)) {
157             pos = xy[i];
158             tan.set(1, 0);
159         }
160         pos += SkVector::Make(-tan.fY, tan.fX) * baseline_offset;
161 
162         xform[i].fSCos = tan.x();
163         xform[i].fSSin = tan.y();
164         xform[i].fTx   = pos.x() - tan.y() * xy[i].y() - tan.x() * offset;
165         xform[i].fTy   = pos.y() + tan.x() * xy[i].y() - tan.y() * offset;
166     }
167 
168     canvas->drawTextBlob(SkTextBlob::MakeFromRSXform(glyphs.get(), count * sizeof(SkGlyphID),
169                                          &xform[0], font, SkTextEncoding::kGlyphID),
170                          0, 0, paint);
171 
172     if (true) {
173         SkPaint p;
174         p.setStyle(SkPaint::kStroke_Style);
175         canvas->drawRect(bounds, p);
176     }
177 }
178 
make_shader()179 static sk_sp<SkShader> make_shader() {
180     SkPoint pts[2] = {{0, 0}, {220, 0}};
181     SkColor colors[2] = {SK_ColorRED, SK_ColorBLUE};
182     return SkGradientShader::MakeLinear(pts, colors, nullptr, 2, SkTileMode::kMirror);
183 }
184 
drawTextPath(SkCanvas * canvas,bool doStroke)185 static void drawTextPath(SkCanvas* canvas, bool doStroke) {
186     const char text0[] = "ABCDFGHJKLMNOPQRSTUVWXYZ";
187     const int N = sizeof(text0) - 1;
188     SkPoint pos[N];
189 
190     SkFont font;
191     font.setSize(100);
192 
193     SkPaint paint;
194     paint.setShader(make_shader());
195     paint.setAntiAlias(true);
196     if (doStroke) {
197         paint.setStyle(SkPaint::kStroke_Style);
198         paint.setStrokeWidth(2.25f);
199         paint.setStrokeJoin(SkPaint::kRound_Join);
200     }
201 
202     SkScalar x = 0;
203     for (int i = 0; i < N; ++i) {
204         pos[i].set(x, 0);
205         x += font.measureText(&text0[i], 1, SkTextEncoding::kUTF8, nullptr, &paint);
206     }
207 
208     SkPath path;
209     const float baseline_offset = -5;
210 
211     const SkPath::Direction dirs[] = {
212         SkPath::kCW_Direction, SkPath::kCCW_Direction,
213     };
214     for (auto d : dirs) {
215         path.reset();
216         path.addOval(SkRect::MakeXYWH(160, 160, 540, 540), d);
217         draw_text_on_path(canvas, text0, N, pos, path, font, paint, baseline_offset);
218     }
219 
220     paint.reset();
221     paint.setStyle(SkPaint::kStroke_Style);
222     canvas->drawPath(path, paint);
223 }
224 
225 DEF_SIMPLE_GM(drawTextRSXform, canvas, 430, 860) {
226     canvas->scale(0.5f, 0.5f);
227     const bool doStroke[] = { false, true };
228     for (auto st : doStroke) {
229         drawTextPath(canvas, st);
230         canvas->translate(0, 860);
231     }
232 }
233 
234 // Exercise xform blob and its bounds
235 DEF_SIMPLE_GM(blob_rsxform, canvas, 500, 100) {
236     SkFont font;
237     font.setTypeface(ToolUtils::create_portable_typeface());
238     font.setSize(50);
239 
240     const char text[] = "CrazyXform";
241     constexpr size_t len = sizeof(text) - 1;
242 
243     SkRSXform xforms[len];
244     SkScalar scale = 1;
245     SkScalar x = 0, y = 0;
246     for (size_t i = 0; i < len; ++i) {
247         scale = SkScalarSin(i * SK_ScalarPI / (len-1)) * 0.75f + 0.5f;
248         xforms[i] = SkRSXform::Make(scale, 0, x, y);
249         x += 50 * scale;
250     }
251 
252     auto blob = SkTextBlob::MakeFromRSXform(text, len, xforms, font);
253 
254     SkPoint offset = { 20, 70 };
255     SkPaint paint;
256     paint.setColor(0xFFCCCCCC);
257     canvas->drawRect(blob->bounds().makeOffset(offset.fX, offset.fY), paint);
258     paint.setColor(SK_ColorBLACK);
259     canvas->drawTextBlob(blob, offset.fX, offset.fY, paint);
260 }
261 
make_vertices(sk_sp<SkImage> image,const SkRect & r,SkColor color)262 static sk_sp<SkVertices> make_vertices(sk_sp<SkImage> image, const SkRect& r,
263                                        SkColor color) {
264     SkPoint pos[4];
265     r.toQuad(pos);
266     SkColor colors[4] = { color, color, color, color };
267     return SkVertices::MakeCopy(SkVertices::kTriangleFan_VertexMode, 4,
268                                 pos, pos, colors);
269 }
270 
271 /*
272  *  drawAtlas and drawVertices have several things in common:
273  *  - can create compound "shaders", combining texture and colors
274  *      - these are combined via an explicit blendmode
275  *  - like drawImage, they only respect parts of the paint
276  *      - colorfilter, imagefilter, blendmode, alpha
277  *
278  *  This GM produces a series of pairs of images (atlas | vertices).
279  *  Each pair should look the same, and each set shows a different combination
280  *  of alpha | colorFilter | mode
281  */
282 DEF_SIMPLE_GM(compare_atlas_vertices, canvas, 560, 585) {
283     const SkRect tex = SkRect::MakeWH(128, 128);
284     const SkRSXform xform = SkRSXform::Make(1, 0, 0, 0);
285     const SkColor color = 0x884488CC;
286 
287     auto image = GetResourceAsImage("images/mandrill_128.png");
288     auto verts = make_vertices(image, tex, color);
289     const sk_sp<SkColorFilter> filters[] = {
290         nullptr,
291         SkColorFilters::Blend(0xFF00FF88, SkBlendMode::kModulate),
292     };
293     const SkBlendMode modes[] = {
294         SkBlendMode::kSrcOver,
295         SkBlendMode::kPlus,
296     };
297 
298     canvas->translate(10, 10);
299     SkPaint paint;
300     for (SkBlendMode mode : modes) {
301         for (float alpha : { 1.0f, 0.5f }) {
302             paint.setAlphaf(alpha);
303             canvas->save();
304             for (auto cf : filters) {
305                 paint.setColorFilter(cf);
306                 canvas->drawAtlas(image, &xform, &tex, &color, 1,
307                                   mode, &tex, &paint);
308                 canvas->translate(128, 0);
309                 paint.setShader(image->makeShader());
310                 canvas->drawVertices(verts, mode, paint);
311                 paint.setShader(nullptr);
312                 canvas->translate(145, 0);
313             }
314             canvas->restore();
315             canvas->translate(0, 145);
316         }
317     }
318 }
319