blob: 5ac2be2f14669401d5f984a401d4c78750431a7f [file] [log] [blame]
reed71c3c762015-06-24 10:29:17 -07001/*
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
Mike Kleinc0bd9f92019-04-23 12:05:21 -05008#include "gm/gm.h"
Ben Wagner7fde8e12019-05-01 17:28:53 -04009#include "include/core/SkBlendMode.h"
Mike Kleinc0bd9f92019-04-23 12:05:21 -050010#include "include/core/SkCanvas.h"
Ben Wagner7fde8e12019-05-01 17:28:53 -040011#include "include/core/SkColor.h"
12#include "include/core/SkColorFilter.h"
13#include "include/core/SkFilterQuality.h"
14#include "include/core/SkFont.h"
Ben Wagnerfcfd0af2020-07-09 10:51:27 -040015#include "include/core/SkFontMgr.h"
Ben Wagner7fde8e12019-05-01 17:28:53 -040016#include "include/core/SkFontTypes.h"
17#include "include/core/SkImage.h"
18#include "include/core/SkImageInfo.h"
19#include "include/core/SkPaint.h"
20#include "include/core/SkPath.h"
21#include "include/core/SkPathMeasure.h"
22#include "include/core/SkPoint.h"
Mike Kleinc0bd9f92019-04-23 12:05:21 -050023#include "include/core/SkRSXform.h"
Ben Wagner7fde8e12019-05-01 17:28:53 -040024#include "include/core/SkRect.h"
25#include "include/core/SkRefCnt.h"
26#include "include/core/SkScalar.h"
27#include "include/core/SkShader.h"
28#include "include/core/SkSize.h"
29#include "include/core/SkString.h"
Mike Kleinc0bd9f92019-04-23 12:05:21 -050030#include "include/core/SkSurface.h"
31#include "include/core/SkTextBlob.h"
Ben Wagner7fde8e12019-05-01 17:28:53 -040032#include "include/core/SkTileMode.h"
33#include "include/core/SkTypeface.h"
34#include "include/core/SkTypes.h"
35#include "include/core/SkVertices.h"
36#include "include/effects/SkGradientShader.h"
37#include "include/private/SkTemplates.h"
Mike Kleinc0bd9f92019-04-23 12:05:21 -050038#include "src/core/SkAutoMalloc.h"
Ben Wagner7fde8e12019-05-01 17:28:53 -040039#include "src/core/SkFontPriv.h"
40#include "tools/Resources.h"
Mike Kleinc0bd9f92019-04-23 12:05:21 -050041#include "tools/ToolUtils.h"
reed71c3c762015-06-24 10:29:17 -070042
Ben Wagner7fde8e12019-05-01 17:28:53 -040043#include <initializer_list>
44
reed71c3c762015-06-24 10:29:17 -070045class DrawAtlasGM : public skiagm::GM {
reed9ce9d672016-03-17 10:51:11 -070046 static sk_sp<SkImage> MakeAtlas(SkCanvas* caller, const SkRect& target) {
reed71c3c762015-06-24 10:29:17 -070047 SkImageInfo info = SkImageInfo::MakeN32Premul(100, 100);
Mike Kleinea3f0142019-03-20 11:12:10 -050048 auto surface(ToolUtils::makeSurface(caller, info));
reed71c3c762015-06-24 10:29:17 -070049 SkCanvas* canvas = surface->getCanvas();
50 // draw red everywhere, but we don't expect to see it in the draw, testing the notion
51 // that drawAtlas draws a subset-region of the atlas.
52 canvas->clear(SK_ColorRED);
53
54 SkPaint paint;
reed374772b2016-10-05 17:33:02 -070055 paint.setBlendMode(SkBlendMode::kClear);
reed71c3c762015-06-24 10:29:17 -070056 SkRect r(target);
57 r.inset(-1, -1);
58 // zero out a place (with a 1-pixel border) to land our drawing.
59 canvas->drawRect(r, paint);
reed374772b2016-10-05 17:33:02 -070060 paint.setBlendMode(SkBlendMode::kSrcOver);
reed71c3c762015-06-24 10:29:17 -070061 paint.setColor(SK_ColorBLUE);
62 paint.setAntiAlias(true);
63 canvas->drawOval(target, paint);
reed9ce9d672016-03-17 10:51:11 -070064 return surface->makeImageSnapshot();
reed71c3c762015-06-24 10:29:17 -070065 }
66
reed71c3c762015-06-24 10:29:17 -070067public:
68 DrawAtlasGM() {}
halcanary9d524f22016-03-29 09:03:52 -070069
reed71c3c762015-06-24 10:29:17 -070070protected:
halcanary9d524f22016-03-29 09:03:52 -070071
reed71c3c762015-06-24 10:29:17 -070072 SkString onShortName() override {
73 return SkString("draw-atlas");
74 }
halcanary9d524f22016-03-29 09:03:52 -070075
reed71c3c762015-06-24 10:29:17 -070076 SkISize onISize() override {
77 return SkISize::Make(640, 480);
78 }
halcanary9d524f22016-03-29 09:03:52 -070079
reed71c3c762015-06-24 10:29:17 -070080 void onDraw(SkCanvas* canvas) override {
81 const SkRect target = { 50, 50, 80, 90 };
brianosman95e8d0a2016-09-29 13:43:49 -070082 auto atlas = MakeAtlas(canvas, target);
reed71c3c762015-06-24 10:29:17 -070083
84 const struct {
85 SkScalar fScale;
86 SkScalar fDegrees;
87 SkScalar fTx;
88 SkScalar fTy;
halcanary9d524f22016-03-29 09:03:52 -070089
reed71c3c762015-06-24 10:29:17 -070090 void apply(SkRSXform* xform) const {
91 const SkScalar rad = SkDegreesToRadians(fDegrees);
92 xform->fSCos = fScale * SkScalarCos(rad);
93 xform->fSSin = fScale * SkScalarSin(rad);
94 xform->fTx = fTx;
95 xform->fTy = fTy;
96 }
97 } rec[] = {
98 { 1, 0, 10, 10 }, // just translate
99 { 2, 0, 110, 10 }, // scale + translate
100 { 1, 30, 210, 10 }, // rotate + translate
101 { 2, -30, 310, 30 }, // scale + rotate + translate
102 };
103
104 const int N = SK_ARRAY_COUNT(rec);
105 SkRSXform xform[N];
106 SkRect tex[N];
107 SkColor colors[N];
108
109 for (int i = 0; i < N; ++i) {
110 rec[i].apply(&xform[i]);
111 tex[i] = target;
112 colors[i] = 0x80FF0000 + (i * 40 * 256);
113 }
114
115 SkPaint paint;
reed71c3c762015-06-24 10:29:17 -0700116 paint.setAntiAlias(true);
Mike Reed99116302021-01-25 11:37:10 -0500117 SkSamplingOptions sampling(SkFilterMode::kLinear);
reed71c3c762015-06-24 10:29:17 -0700118
Mike Reed99116302021-01-25 11:37:10 -0500119 canvas->drawAtlas(atlas.get(), xform, tex, nullptr, N, SkBlendMode::kDst,
120 sampling, nullptr, &paint);
reed71c3c762015-06-24 10:29:17 -0700121 canvas->translate(0, 100);
Mike Reed99116302021-01-25 11:37:10 -0500122 canvas->drawAtlas(atlas.get(), xform, tex, colors, N, SkBlendMode::kSrcIn,
123 sampling, nullptr, &paint);
reed71c3c762015-06-24 10:29:17 -0700124 }
halcanary9d524f22016-03-29 09:03:52 -0700125
reed71c3c762015-06-24 10:29:17 -0700126private:
John Stiles7571f9e2020-09-02 22:42:33 -0400127 using INHERITED = GM;
reed71c3c762015-06-24 10:29:17 -0700128};
129DEF_GM( return new DrawAtlasGM; )
reed45561a02016-07-07 12:47:17 -0700130
131///////////////////////////////////////////////////////////////////////////////////////////////////
reed45561a02016-07-07 12:47:17 -0700132
Mike Reed488fbd12018-01-29 13:33:06 -0500133static void draw_text_on_path(SkCanvas* canvas, const void* text, size_t length,
Mike Reeddf3d2252018-12-20 17:10:27 -0500134 const SkPoint xy[], const SkPath& path, const SkFont& font, const SkPaint& paint,
Mike Reed1eb9af92018-10-01 12:16:59 -0400135 float baseline_offset) {
reed45561a02016-07-07 12:47:17 -0700136 SkPathMeasure meas(path, false);
137
Ben Wagner51e15a62019-05-07 15:38:46 -0400138 int count = font.countText(text, length, SkTextEncoding::kUTF8);
reed7c70d7c2016-07-12 15:06:33 -0700139 size_t size = count * (sizeof(SkRSXform) + sizeof(SkScalar));
140 SkAutoSMalloc<512> storage(size);
141 SkRSXform* xform = (SkRSXform*)storage.get();
142 SkScalar* widths = (SkScalar*)(xform + count);
143
reed7c70d7c2016-07-12 15:06:33 -0700144 // Compute a conservative bounds so we can cull the draw
Mike Reed7d1eb332018-12-04 17:35:56 -0500145 const SkRect fontb = SkFontPriv::GetFontBounds(font);
Brian Osman788b9162020-02-07 10:36:46 -0500146 const SkScalar max = std::max(std::max(SkScalarAbs(fontb.fLeft), SkScalarAbs(fontb.fRight)),
147 std::max(SkScalarAbs(fontb.fTop), SkScalarAbs(fontb.fBottom)));
reed7c70d7c2016-07-12 15:06:33 -0700148 const SkRect bounds = path.getBounds().makeOutset(max, max);
149
Mike Reeddf3d2252018-12-20 17:10:27 -0500150 SkAutoTArray<SkGlyphID> glyphs(count);
Ben Wagner51e15a62019-05-07 15:38:46 -0400151 font.textToGlyphs(text, length, SkTextEncoding::kUTF8, glyphs.get(), count);
Mike Reeddf3d2252018-12-20 17:10:27 -0500152 font.getWidths(glyphs.get(), count, widths);
Mike Reed488fbd12018-01-29 13:33:06 -0500153
Mike Reed1eb9af92018-10-01 12:16:59 -0400154 for (int i = 0; i < count; ++i) {
155 // we want to position each character on the center of its advance
156 const SkScalar offset = SkScalarHalf(widths[i]);
157 SkPoint pos;
158 SkVector tan;
159 if (!meas.getPosTan(xy[i].x() + offset, &pos, &tan)) {
160 pos = xy[i];
161 tan.set(1, 0);
Mike Reed488fbd12018-01-29 13:33:06 -0500162 }
Mike Reed1eb9af92018-10-01 12:16:59 -0400163 pos += SkVector::Make(-tan.fY, tan.fX) * baseline_offset;
Mike Reed488fbd12018-01-29 13:33:06 -0500164
Mike Reed1eb9af92018-10-01 12:16:59 -0400165 xform[i].fSCos = tan.x();
166 xform[i].fSSin = tan.y();
167 xform[i].fTx = pos.x() - tan.y() * xy[i].y() - tan.x() * offset;
168 xform[i].fTy = pos.y() + tan.x() * xy[i].y() - tan.y() * offset;
Mike Reed488fbd12018-01-29 13:33:06 -0500169 }
reed7c70d7c2016-07-12 15:06:33 -0700170
Mike Reeddf3d2252018-12-20 17:10:27 -0500171 canvas->drawTextBlob(SkTextBlob::MakeFromRSXform(glyphs.get(), count * sizeof(SkGlyphID),
Ben Wagner51e15a62019-05-07 15:38:46 -0400172 &xform[0], font, SkTextEncoding::kGlyphID),
Mike Reeddf3d2252018-12-20 17:10:27 -0500173 0, 0, paint);
Mike Reed1eb9af92018-10-01 12:16:59 -0400174
reed7c70d7c2016-07-12 15:06:33 -0700175 if (true) {
176 SkPaint p;
177 p.setStyle(SkPaint::kStroke_Style);
178 canvas->drawRect(bounds, p);
179 }
reed45561a02016-07-07 12:47:17 -0700180}
181
Mike Reed9c2916e2018-03-15 13:37:08 -0400182static sk_sp<SkShader> make_shader() {
183 SkPoint pts[2] = {{0, 0}, {220, 0}};
184 SkColor colors[2] = {SK_ColorRED, SK_ColorBLUE};
Mike Reedfae8fce2019-04-03 10:27:45 -0400185 return SkGradientShader::MakeLinear(pts, colors, nullptr, 2, SkTileMode::kMirror);
Mike Reed9c2916e2018-03-15 13:37:08 -0400186}
187
Mike Reed1eb9af92018-10-01 12:16:59 -0400188static void drawTextPath(SkCanvas* canvas, bool doStroke) {
reeddde2b1f2016-07-08 03:31:09 -0700189 const char text0[] = "ABCDFGHJKLMNOPQRSTUVWXYZ";
reeddde2b1f2016-07-08 03:31:09 -0700190 const int N = sizeof(text0) - 1;
reed45561a02016-07-07 12:47:17 -0700191 SkPoint pos[N];
reed45561a02016-07-07 12:47:17 -0700192
Mike Reeddf3d2252018-12-20 17:10:27 -0500193 SkFont font;
194 font.setSize(100);
195
reed45561a02016-07-07 12:47:17 -0700196 SkPaint paint;
Mike Reed9c2916e2018-03-15 13:37:08 -0400197 paint.setShader(make_shader());
reed45561a02016-07-07 12:47:17 -0700198 paint.setAntiAlias(true);
Mike Reedcaaf2112018-01-29 14:32:38 -0500199 if (doStroke) {
200 paint.setStyle(SkPaint::kStroke_Style);
201 paint.setStrokeWidth(2.25f);
202 paint.setStrokeJoin(SkPaint::kRound_Join);
203 }
reed7c70d7c2016-07-12 15:06:33 -0700204
205 SkScalar x = 0;
206 for (int i = 0; i < N; ++i) {
207 pos[i].set(x, 0);
Ben Wagner51e15a62019-05-07 15:38:46 -0400208 x += font.measureText(&text0[i], 1, SkTextEncoding::kUTF8, nullptr, &paint);
reed7c70d7c2016-07-12 15:06:33 -0700209 }
reed45561a02016-07-07 12:47:17 -0700210
211 SkPath path;
Mike Reed488fbd12018-01-29 13:33:06 -0500212 const float baseline_offset = -5;
reed45561a02016-07-07 12:47:17 -0700213
Mike Reed30bc5272019-11-22 18:34:02 +0000214 const SkPathDirection dirs[] = {
215 SkPathDirection::kCW, SkPathDirection::kCCW,
Mike Reed488fbd12018-01-29 13:33:06 -0500216 };
217 for (auto d : dirs) {
218 path.reset();
219 path.addOval(SkRect::MakeXYWH(160, 160, 540, 540), d);
Mike Reeddf3d2252018-12-20 17:10:27 -0500220 draw_text_on_path(canvas, text0, N, pos, path, font, paint, baseline_offset);
Mike Reed488fbd12018-01-29 13:33:06 -0500221 }
reed45561a02016-07-07 12:47:17 -0700222
Mike Reedcaaf2112018-01-29 14:32:38 -0500223 paint.reset();
reed45561a02016-07-07 12:47:17 -0700224 paint.setStyle(SkPaint::kStroke_Style);
225 canvas->drawPath(path, paint);
226}
227
Mike Reed1eb9af92018-10-01 12:16:59 -0400228DEF_SIMPLE_GM(drawTextRSXform, canvas, 430, 860) {
Mike Reed488fbd12018-01-29 13:33:06 -0500229 canvas->scale(0.5f, 0.5f);
Mike Reedcaaf2112018-01-29 14:32:38 -0500230 const bool doStroke[] = { false, true };
231 for (auto st : doStroke) {
Mike Reed1eb9af92018-10-01 12:16:59 -0400232 drawTextPath(canvas, st);
Mike Reedcaaf2112018-01-29 14:32:38 -0500233 canvas->translate(0, 860);
234 }
Mike Reed488fbd12018-01-29 13:33:06 -0500235}
236
Mike Reededce0aa2018-12-20 15:24:21 -0500237// Exercise xform blob and its bounds
238DEF_SIMPLE_GM(blob_rsxform, canvas, 500, 100) {
239 SkFont font;
Mike Kleinea3f0142019-03-20 11:12:10 -0500240 font.setTypeface(ToolUtils::create_portable_typeface());
Mike Reededce0aa2018-12-20 15:24:21 -0500241 font.setSize(50);
242
243 const char text[] = "CrazyXform";
244 constexpr size_t len = sizeof(text) - 1;
245
246 SkRSXform xforms[len];
247 SkScalar scale = 1;
248 SkScalar x = 0, y = 0;
249 for (size_t i = 0; i < len; ++i) {
250 scale = SkScalarSin(i * SK_ScalarPI / (len-1)) * 0.75f + 0.5f;
251 xforms[i] = SkRSXform::Make(scale, 0, x, y);
252 x += 50 * scale;
253 }
254
255 auto blob = SkTextBlob::MakeFromRSXform(text, len, xforms, font);
256
257 SkPoint offset = { 20, 70 };
258 SkPaint paint;
259 paint.setColor(0xFFCCCCCC);
260 canvas->drawRect(blob->bounds().makeOffset(offset.fX, offset.fY), paint);
261 paint.setColor(SK_ColorBLACK);
262 canvas->drawTextBlob(blob, offset.fX, offset.fY, paint);
263}
264
Ben Wagnerfcfd0af2020-07-09 10:51:27 -0400265// Exercise xform blob and its tight bounds
266DEF_SIMPLE_GM(blob_rsxform_distortable, canvas, 500, 100) {
267 sk_sp<SkTypeface> typeface;
268 std::unique_ptr<SkStreamAsset> distortable(GetResourceAsStream("fonts/Distortable.ttf"));
269 if (distortable) {
270 sk_sp<SkFontMgr> fm = SkFontMgr::RefDefault();
271 const SkFontArguments::VariationPosition::Coordinate position[] = {
272 { SkSetFourByteTag('w','g','h','t'), 1.618033988749895f }
273 };
274 SkFontArguments params;
275 params.setVariationDesignPosition({position, SK_ARRAY_COUNT(position)});
276 typeface = fm->makeFromStream(std::move(distortable), params);
277 }
278
279 SkFont font(typeface, 50);
280
281 const char text[] = "abcabcabc";
282 constexpr size_t len = sizeof(text) - 1;
283
284 SkRSXform xforms[len];
285 SkScalar scale = 1;
286 SkScalar x = 0, y = 0;
287 for (size_t i = 0; i < len; ++i) {
288 scale = SkScalarSin(i * SK_ScalarPI / (len-1)) * 0.75f + 0.5f;
289 xforms[i] = SkRSXform::Make(scale, 0, x, y);
290 x += 50 * scale;
291 }
292
293 auto blob = SkTextBlob::MakeFromRSXform(text, len, xforms, font);
294
295 SkPoint offset = { 20, 70 };
296 SkPaint paint;
297 paint.setColor(0xFFCCCCCC);
298 canvas->drawRect(blob->bounds().makeOffset(offset.fX, offset.fY), paint);
299 paint.setColor(SK_ColorBLACK);
300 canvas->drawTextBlob(blob, offset.fX, offset.fY, paint);
301}
302
Mike Reed93cb2522017-04-28 10:02:47 -0400303static sk_sp<SkVertices> make_vertices(sk_sp<SkImage> image, const SkRect& r,
304 SkColor color) {
305 SkPoint pos[4];
306 r.toQuad(pos);
307 SkColor colors[4] = { color, color, color, color };
308 return SkVertices::MakeCopy(SkVertices::kTriangleFan_VertexMode, 4,
309 pos, pos, colors);
310}
311
312/*
313 * drawAtlas and drawVertices have several things in common:
314 * - can create compound "shaders", combining texture and colors
315 * - these are combined via an explicit blendmode
316 * - like drawImage, they only respect parts of the paint
317 * - colorfilter, imagefilter, blendmode, alpha
318 *
319 * This GM produces a series of pairs of images (atlas | vertices).
320 * Each pair should look the same, and each set shows a different combination
321 * of alpha | colorFilter | mode
322 */
323DEF_SIMPLE_GM(compare_atlas_vertices, canvas, 560, 585) {
324 const SkRect tex = SkRect::MakeWH(128, 128);
325 const SkRSXform xform = SkRSXform::Make(1, 0, 0, 0);
326 const SkColor color = 0x884488CC;
327
Hal Canaryc465d132017-12-08 10:21:31 -0500328 auto image = GetResourceAsImage("images/mandrill_128.png");
Mike Reed93cb2522017-04-28 10:02:47 -0400329 auto verts = make_vertices(image, tex, color);
330 const sk_sp<SkColorFilter> filters[] = {
331 nullptr,
Mike Reedb286bc22019-04-08 16:23:20 -0400332 SkColorFilters::Blend(0xFF00FF88, SkBlendMode::kModulate),
Mike Reed93cb2522017-04-28 10:02:47 -0400333 };
334 const SkBlendMode modes[] = {
335 SkBlendMode::kSrcOver,
336 SkBlendMode::kPlus,
337 };
338
339 canvas->translate(10, 10);
340 SkPaint paint;
341 for (SkBlendMode mode : modes) {
Mike Reed9407e242019-02-15 16:13:57 -0500342 for (float alpha : { 1.0f, 0.5f }) {
343 paint.setAlphaf(alpha);
Mike Reed93cb2522017-04-28 10:02:47 -0400344 canvas->save();
John Stilesbd3ffa42020-07-30 20:24:57 -0400345 for (const sk_sp<SkColorFilter>& cf : filters) {
Mike Reed93cb2522017-04-28 10:02:47 -0400346 paint.setColorFilter(cf);
Mike Reed99116302021-01-25 11:37:10 -0500347 canvas->drawAtlas(image.get(), &xform, &tex, &color, 1, mode,
348 SkSamplingOptions(), &tex, &paint);
Mike Reed93cb2522017-04-28 10:02:47 -0400349 canvas->translate(128, 0);
Mike Reedb612b6c2020-12-08 21:58:35 -0500350 paint.setShader(image->makeShader(SkSamplingOptions()));
Mike Reed93cb2522017-04-28 10:02:47 -0400351 canvas->drawVertices(verts, mode, paint);
352 paint.setShader(nullptr);
353 canvas->translate(145, 0);
354 }
355 canvas->restore();
356 canvas->translate(0, 145);
357 }
358 }
359}