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/SkFont.h"
14 #include "include/core/SkFontMgr.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.setAntiAlias(true);
116 SkSamplingOptions sampling(SkFilterMode::kLinear);
117
118 canvas->drawAtlas(atlas.get(), xform, tex, nullptr, N, SkBlendMode::kDst,
119 sampling, nullptr, &paint);
120 canvas->translate(0, 100);
121 canvas->drawAtlas(atlas.get(), xform, tex, colors, N, SkBlendMode::kSrcIn,
122 sampling, nullptr, &paint);
123 }
124
125 private:
126 using INHERITED = GM;
127 };
DEF_GM(return new DrawAtlasGM;)128 DEF_GM( return new DrawAtlasGM; )
129
130 ///////////////////////////////////////////////////////////////////////////////////////////////////
131
132 static void draw_text_on_path(SkCanvas* canvas, const void* text, size_t length,
133 const SkPoint xy[], const SkPath& path, const SkFont& font, const SkPaint& paint,
134 float baseline_offset) {
135 SkPathMeasure meas(path, false);
136
137 int count = font.countText(text, length, SkTextEncoding::kUTF8);
138 size_t size = count * (sizeof(SkRSXform) + sizeof(SkScalar));
139 SkAutoSMalloc<512> storage(size);
140 SkRSXform* xform = (SkRSXform*)storage.get();
141 SkScalar* widths = (SkScalar*)(xform + count);
142
143 // Compute a conservative bounds so we can cull the draw
144 const SkRect fontb = SkFontPriv::GetFontBounds(font);
145 const SkScalar max = std::max(std::max(SkScalarAbs(fontb.fLeft), SkScalarAbs(fontb.fRight)),
146 std::max(SkScalarAbs(fontb.fTop), SkScalarAbs(fontb.fBottom)));
147 const SkRect bounds = path.getBounds().makeOutset(max, max);
148
149 SkAutoTArray<SkGlyphID> glyphs(count);
150 font.textToGlyphs(text, length, SkTextEncoding::kUTF8, glyphs.get(), count);
151 font.getWidths(glyphs.get(), count, widths);
152
153 for (int i = 0; i < count; ++i) {
154 // we want to position each character on the center of its advance
155 const SkScalar offset = SkScalarHalf(widths[i]);
156 SkPoint pos;
157 SkVector tan;
158 if (!meas.getPosTan(xy[i].x() + offset, &pos, &tan)) {
159 pos = xy[i];
160 tan.set(1, 0);
161 }
162 pos += SkVector::Make(-tan.fY, tan.fX) * baseline_offset;
163
164 xform[i].fSCos = tan.x();
165 xform[i].fSSin = tan.y();
166 xform[i].fTx = pos.x() - tan.y() * xy[i].y() - tan.x() * offset;
167 xform[i].fTy = pos.y() + tan.x() * xy[i].y() - tan.y() * offset;
168 }
169
170 canvas->drawTextBlob(SkTextBlob::MakeFromRSXform(glyphs.get(), count * sizeof(SkGlyphID),
171 &xform[0], font, SkTextEncoding::kGlyphID),
172 0, 0, paint);
173
174 if (true) {
175 SkPaint p;
176 p.setStyle(SkPaint::kStroke_Style);
177 canvas->drawRect(bounds, p);
178 }
179 }
180
make_shader()181 static sk_sp<SkShader> make_shader() {
182 SkPoint pts[2] = {{0, 0}, {220, 0}};
183 SkColor colors[2] = {SK_ColorRED, SK_ColorBLUE};
184 return SkGradientShader::MakeLinear(pts, colors, nullptr, 2, SkTileMode::kMirror);
185 }
186
drawTextPath(SkCanvas * canvas,bool doStroke)187 static void drawTextPath(SkCanvas* canvas, bool doStroke) {
188 const char text0[] = "ABCDFGHJKLMNOPQRSTUVWXYZ";
189 const int N = sizeof(text0) - 1;
190 SkPoint pos[N];
191
192 SkFont font;
193 font.setSize(100);
194
195 SkPaint paint;
196 paint.setShader(make_shader());
197 paint.setAntiAlias(true);
198 if (doStroke) {
199 paint.setStyle(SkPaint::kStroke_Style);
200 paint.setStrokeWidth(2.25f);
201 paint.setStrokeJoin(SkPaint::kRound_Join);
202 }
203
204 SkScalar x = 0;
205 for (int i = 0; i < N; ++i) {
206 pos[i].set(x, 0);
207 x += font.measureText(&text0[i], 1, SkTextEncoding::kUTF8, nullptr, &paint);
208 }
209
210 SkPath path;
211 const float baseline_offset = -5;
212
213 const SkPathDirection dirs[] = {
214 SkPathDirection::kCW, SkPathDirection::kCCW,
215 };
216 for (auto d : dirs) {
217 path.reset();
218 path.addOval(SkRect::MakeXYWH(160, 160, 540, 540), d);
219 draw_text_on_path(canvas, text0, N, pos, path, font, paint, baseline_offset);
220 }
221
222 paint.reset();
223 paint.setStyle(SkPaint::kStroke_Style);
224 canvas->drawPath(path, paint);
225 }
226
227 DEF_SIMPLE_GM(drawTextRSXform, canvas, 430, 860) {
228 canvas->scale(0.5f, 0.5f);
229 const bool doStroke[] = { false, true };
230 for (auto st : doStroke) {
231 drawTextPath(canvas, st);
232 canvas->translate(0, 860);
233 }
234 }
235
236 // Exercise xform blob and its bounds
237 DEF_SIMPLE_GM(blob_rsxform, canvas, 500, 100) {
238 SkFont font;
239 font.setTypeface(ToolUtils::create_portable_typeface());
240 font.setSize(50);
241
242 const char text[] = "CrazyXform";
243 constexpr size_t len = sizeof(text) - 1;
244
245 SkRSXform xforms[len];
246 SkScalar scale = 1;
247 SkScalar x = 0, y = 0;
248 for (size_t i = 0; i < len; ++i) {
249 scale = SkScalarSin(i * SK_ScalarPI / (len-1)) * 0.75f + 0.5f;
250 xforms[i] = SkRSXform::Make(scale, 0, x, y);
251 x += 50 * scale;
252 }
253
254 auto blob = SkTextBlob::MakeFromRSXform(text, len, xforms, font);
255
256 SkPoint offset = { 20, 70 };
257 SkPaint paint;
258 paint.setColor(0xFFCCCCCC);
259 canvas->drawRect(blob->bounds().makeOffset(offset.fX, offset.fY), paint);
260 paint.setColor(SK_ColorBLACK);
261 canvas->drawTextBlob(blob, offset.fX, offset.fY, paint);
262 }
263
264 // Exercise xform blob and its tight bounds
265 DEF_SIMPLE_GM(blob_rsxform_distortable, canvas, 500, 100) {
266 sk_sp<SkTypeface> typeface;
267 std::unique_ptr<SkStreamAsset> distortable(GetResourceAsStream("fonts/Distortable.ttf"));
268 if (distortable) {
269 sk_sp<SkFontMgr> fm = SkFontMgr::RefDefault();
270 const SkFontArguments::VariationPosition::Coordinate position[] = {
271 { SkSetFourByteTag('w','g','h','t'), 1.618033988749895f }
272 };
273 SkFontArguments params;
274 params.setVariationDesignPosition({position, SK_ARRAY_COUNT(position)});
275 typeface = fm->makeFromStream(std::move(distortable), params);
276 }
277
278 SkFont font(typeface, 50);
279
280 const char text[] = "abcabcabc";
281 constexpr size_t len = sizeof(text) - 1;
282
283 SkRSXform xforms[len];
284 SkScalar scale = 1;
285 SkScalar x = 0, y = 0;
286 for (size_t i = 0; i < len; ++i) {
287 scale = SkScalarSin(i * SK_ScalarPI / (len-1)) * 0.75f + 0.5f;
288 xforms[i] = SkRSXform::Make(scale, 0, x, y);
289 x += 50 * scale;
290 }
291
292 auto blob = SkTextBlob::MakeFromRSXform(text, len, xforms, font);
293
294 SkPoint offset = { 20, 70 };
295 SkPaint paint;
296 paint.setColor(0xFFCCCCCC);
297 canvas->drawRect(blob->bounds().makeOffset(offset.fX, offset.fY), paint);
298 paint.setColor(SK_ColorBLACK);
299 canvas->drawTextBlob(blob, offset.fX, offset.fY, paint);
300 }
301
make_vertices(sk_sp<SkImage> image,const SkRect & r,SkColor color)302 static sk_sp<SkVertices> make_vertices(sk_sp<SkImage> image, const SkRect& r,
303 SkColor color) {
304 SkPoint pos[4];
305 r.toQuad(pos);
306 SkColor colors[4] = { color, color, color, color };
307 return SkVertices::MakeCopy(SkVertices::kTriangleFan_VertexMode, 4,
308 pos, pos, colors);
309 }
310
311 /*
312 * drawAtlas and drawVertices have several things in common:
313 * - can create compound "shaders", combining texture and colors
314 * - these are combined via an explicit blendmode
315 * - like drawImage, they only respect parts of the paint
316 * - colorfilter, imagefilter, blendmode, alpha
317 *
318 * This GM produces a series of pairs of images (atlas | vertices).
319 * Each pair should look the same, and each set shows a different combination
320 * of alpha | colorFilter | mode
321 */
322 DEF_SIMPLE_GM(compare_atlas_vertices, canvas, 560, 585) {
323 const SkRect tex = SkRect::MakeWH(128, 128);
324 const SkRSXform xform = SkRSXform::Make(1, 0, 0, 0);
325 const SkColor color = 0x884488CC;
326
327 auto image = GetResourceAsImage("images/mandrill_128.png");
328 auto verts = make_vertices(image, tex, color);
329 const sk_sp<SkColorFilter> filters[] = {
330 nullptr,
331 SkColorFilters::Blend(0xFF00FF88, SkBlendMode::kModulate),
332 };
333 const SkBlendMode modes[] = {
334 SkBlendMode::kSrcOver,
335 SkBlendMode::kPlus,
336 };
337
338 canvas->translate(10, 10);
339 SkPaint paint;
340 for (SkBlendMode mode : modes) {
341 for (float alpha : { 1.0f, 0.5f }) {
342 paint.setAlphaf(alpha);
343 canvas->save();
344 for (const sk_sp<SkColorFilter>& cf : filters) {
345 paint.setColorFilter(cf);
346 canvas->drawAtlas(image.get(), &xform, &tex, &color, 1, mode,
347 SkSamplingOptions(), &tex, &paint);
348 canvas->translate(128, 0);
349 paint.setShader(image->makeShader(SkSamplingOptions()));
350 canvas->drawVertices(verts, mode, paint);
351 paint.setShader(nullptr);
352 canvas->translate(145, 0);
353 }
354 canvas->restore();
355 canvas->translate(0, 145);
356 }
357 }
358 }
359