blob: 5094eb5aea2d1daa8dd9d494fe192a511d6337d6 [file] [log] [blame]
Mike Kleinea3f0142019-03-20 11:12:10 -05001/*
2 * Copyright 2014 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
Kevin Lubickce5ec012022-04-22 09:09:07 -04008#include "tools/ToolUtils.h"
9
Kevin Lubickbe579dc2023-10-05 11:11:20 -040010#include "include/core/SkAlphaType.h"
Mike Kleinc0bd9f92019-04-23 12:05:21 -050011#include "include/core/SkBitmap.h"
12#include "include/core/SkBlendMode.h"
13#include "include/core/SkCanvas.h"
14#include "include/core/SkColorPriv.h"
Robert Phillips655489c2022-10-06 12:36:59 -040015#include "include/core/SkColorSpace.h"
Brian Osman211d63b2023-09-01 20:39:58 +000016#include "include/core/SkColorType.h"
Kevin Lubickbe579dc2023-10-05 11:11:20 -040017#include "include/core/SkFont.h"
18#include "include/core/SkFontTypes.h"
Mike Kleinc0bd9f92019-04-23 12:05:21 -050019#include "include/core/SkImage.h"
Kevin Lubickbe579dc2023-10-05 11:11:20 -040020#include "include/core/SkImageInfo.h"
Mike Kleinc0bd9f92019-04-23 12:05:21 -050021#include "include/core/SkMatrix.h"
22#include "include/core/SkPaint.h"
Kevin Lubickbe579dc2023-10-05 11:11:20 -040023#include "include/core/SkPath.h"
Mike Reede9d783c2020-08-17 14:14:13 -040024#include "include/core/SkPathBuilder.h"
Kevin Lubickbe579dc2023-10-05 11:11:20 -040025#include "include/core/SkPathTypes.h"
Chris Daltonaa060012021-10-18 16:15:05 -060026#include "include/core/SkPicture.h"
Kevin Lubickbe579dc2023-10-05 11:11:20 -040027#include "include/core/SkPixelRef.h" // IWYU pragma: keep
Mike Kleinc0bd9f92019-04-23 12:05:21 -050028#include "include/core/SkPixmap.h"
29#include "include/core/SkPoint3.h"
Kevin Lubick9b028372023-10-05 15:04:54 -040030#include "include/core/SkRefCnt.h"
Kevin Lubickbe579dc2023-10-05 11:11:20 -040031#include "include/core/SkSamplingOptions.h"
32#include "include/core/SkStream.h"
Mike Kleinc0bd9f92019-04-23 12:05:21 -050033#include "include/core/SkSurface.h"
34#include "include/core/SkTextBlob.h"
Kevin Lubickbe579dc2023-10-05 11:11:20 -040035#include "include/core/SkTileMode.h"
36#include "include/core/SkTypeface.h"
Robert Phillips861a11a2023-06-07 10:49:14 -040037#include "include/effects/SkGradientShader.h"
Mike Kleinc0bd9f92019-04-23 12:05:21 -050038#include "include/private/SkColorData.h"
Kevin Lubickbe579dc2023-10-05 11:11:20 -040039#include "include/private/base/SkCPUTypes.h"
40#include "include/private/base/SkTemplates.h"
Mike Kleinc0bd9f92019-04-23 12:05:21 -050041#include "src/core/SkFontPriv.h"
Kevin Lubickbe579dc2023-10-05 11:11:20 -040042#include "tools/SkMetaData.h"
Mike Kleinea3f0142019-03-20 11:12:10 -050043
44#include <cmath>
45#include <cstring>
Mike Kleinea3f0142019-03-20 11:12:10 -050046
Kevin Lubick27e36b02022-10-04 11:38:11 -040047#ifdef SK_BUILD_FOR_WIN
48#include "include/ports/SkTypeface_win.h"
49#endif
50
Herb Derby3b3bcf02023-01-17 15:12:15 -050051using namespace skia_private;
52
Mike Kleinea3f0142019-03-20 11:12:10 -050053namespace ToolUtils {
54
55const char* alphatype_name(SkAlphaType at) {
56 switch (at) {
Mike Klein98168782019-04-09 13:45:02 -050057 case kUnknown_SkAlphaType: return "Unknown";
58 case kOpaque_SkAlphaType: return "Opaque";
59 case kPremul_SkAlphaType: return "Premul";
Mike Kleinea3f0142019-03-20 11:12:10 -050060 case kUnpremul_SkAlphaType: return "Unpremul";
61 }
Robert Phillips655489c2022-10-06 12:36:59 -040062 SkUNREACHABLE;
Mike Kleinea3f0142019-03-20 11:12:10 -050063}
64
65const char* colortype_name(SkColorType ct) {
66 switch (ct) {
Robert Phillipsea1b30b2019-09-19 16:05:48 -040067 case kUnknown_SkColorType: return "Unknown";
68 case kAlpha_8_SkColorType: return "Alpha_8";
69 case kA16_unorm_SkColorType: return "Alpha_16";
70 case kA16_float_SkColorType: return "A16_float";
71 case kRGB_565_SkColorType: return "RGB_565";
72 case kARGB_4444_SkColorType: return "ARGB_4444";
73 case kRGBA_8888_SkColorType: return "RGBA_8888";
Brian Osman9f1e06a2021-08-10 14:39:18 -040074 case kSRGBA_8888_SkColorType: return "SRGBA_8888";
Robert Phillipsea1b30b2019-09-19 16:05:48 -040075 case kRGB_888x_SkColorType: return "RGB_888x";
76 case kBGRA_8888_SkColorType: return "BGRA_8888";
77 case kRGBA_1010102_SkColorType: return "RGBA_1010102";
Robert Phillipsa50ce3c2020-04-28 22:30:33 +000078 case kBGRA_1010102_SkColorType: return "BGRA_1010102";
Robert Phillips9a30ee02020-04-29 08:58:39 -040079 case kRGB_101010x_SkColorType: return "RGB_101010x";
Mike Kleinf7eb0542020-02-11 12:19:08 -060080 case kBGR_101010x_SkColorType: return "BGR_101010x";
Aaron Clarkefe37efc2023-02-16 14:40:00 -080081 case kBGR_101010x_XR_SkColorType: return "BGR_101010x_XR";
Brian Osman211d63b2023-09-01 20:39:58 +000082 case kRGBA_10x6_SkColorType: return "RGBA_10x6";
Robert Phillipsea1b30b2019-09-19 16:05:48 -040083 case kGray_8_SkColorType: return "Gray_8";
84 case kRGBA_F16Norm_SkColorType: return "RGBA_F16Norm";
85 case kRGBA_F16_SkColorType: return "RGBA_F16";
86 case kRGBA_F32_SkColorType: return "RGBA_F32";
87 case kR8G8_unorm_SkColorType: return "R8G8_unorm";
88 case kR16G16_unorm_SkColorType: return "R16G16_unorm";
89 case kR16G16_float_SkColorType: return "R16G16_float";
90 case kR16G16B16A16_unorm_SkColorType: return "R16G16B16A16_unorm";
Brian Osmana7a23242022-02-08 10:34:38 -050091 case kR8_unorm_SkColorType: return "R8_unorm";
Mike Kleinea3f0142019-03-20 11:12:10 -050092 }
Robert Phillips655489c2022-10-06 12:36:59 -040093 SkUNREACHABLE;
Mike Kleinea3f0142019-03-20 11:12:10 -050094}
95
Mike Klein98168782019-04-09 13:45:02 -050096const char* colortype_depth(SkColorType ct) {
97 switch (ct) {
Robert Phillipsea1b30b2019-09-19 16:05:48 -040098 case kUnknown_SkColorType: return "Unknown";
99 case kAlpha_8_SkColorType: return "A8";
100 case kA16_unorm_SkColorType: return "A16";
101 case kA16_float_SkColorType: return "AF16";
102 case kRGB_565_SkColorType: return "565";
103 case kARGB_4444_SkColorType: return "4444";
104 case kRGBA_8888_SkColorType: return "8888";
Brian Osman9f1e06a2021-08-10 14:39:18 -0400105 case kSRGBA_8888_SkColorType: return "8888";
Robert Phillipsea1b30b2019-09-19 16:05:48 -0400106 case kRGB_888x_SkColorType: return "888";
107 case kBGRA_8888_SkColorType: return "8888";
108 case kRGBA_1010102_SkColorType: return "1010102";
Robert Phillipsa50ce3c2020-04-28 22:30:33 +0000109 case kBGRA_1010102_SkColorType: return "1010102";
Robert Phillips9a30ee02020-04-29 08:58:39 -0400110 case kRGB_101010x_SkColorType: return "101010";
Mike Kleinf7eb0542020-02-11 12:19:08 -0600111 case kBGR_101010x_SkColorType: return "101010";
Aaron Clarkefe37efc2023-02-16 14:40:00 -0800112 case kBGR_101010x_XR_SkColorType: return "101010";
Brian Osman211d63b2023-09-01 20:39:58 +0000113 case kRGBA_10x6_SkColorType: return "10101010";
Robert Phillipsea1b30b2019-09-19 16:05:48 -0400114 case kGray_8_SkColorType: return "G8";
Robert Phillips655489c2022-10-06 12:36:59 -0400115 case kRGBA_F16Norm_SkColorType: return "F16Norm";
Robert Phillipsea1b30b2019-09-19 16:05:48 -0400116 case kRGBA_F16_SkColorType: return "F16";
117 case kRGBA_F32_SkColorType: return "F32";
118 case kR8G8_unorm_SkColorType: return "88";
119 case kR16G16_unorm_SkColorType: return "1616";
120 case kR16G16_float_SkColorType: return "F16F16";
121 case kR16G16B16A16_unorm_SkColorType: return "16161616";
Robert Phillips655489c2022-10-06 12:36:59 -0400122 case kR8_unorm_SkColorType: return "R8";
Mike Klein98168782019-04-09 13:45:02 -0500123 }
Robert Phillips655489c2022-10-06 12:36:59 -0400124 SkUNREACHABLE;
Mike Klein98168782019-04-09 13:45:02 -0500125}
126
Michael Ludwig23003182019-08-05 11:25:23 -0400127const char* tilemode_name(SkTileMode mode) {
128 switch (mode) {
129 case SkTileMode::kClamp: return "clamp";
130 case SkTileMode::kRepeat: return "repeat";
131 case SkTileMode::kMirror: return "mirror";
132 case SkTileMode::kDecal: return "decal";
133 }
Robert Phillips655489c2022-10-06 12:36:59 -0400134 SkUNREACHABLE;
Michael Ludwig23003182019-08-05 11:25:23 -0400135}
Mike Klein98168782019-04-09 13:45:02 -0500136
Mike Kleinea3f0142019-03-20 11:12:10 -0500137SkColor color_to_565(SkColor color) {
138 // Not a good idea to use this function for greyscale colors...
139 // it will add an obvious purple or green tint.
140 SkASSERT(SkColorGetR(color) != SkColorGetG(color) || SkColorGetR(color) != SkColorGetB(color) ||
141 SkColorGetG(color) != SkColorGetB(color));
142
143 SkPMColor pmColor = SkPreMultiplyColor(color);
144 U16CPU color16 = SkPixel32ToPixel16(pmColor);
145 return SkPixel16ToColor(color16);
146}
147
Mike Kleinea3f0142019-03-20 11:12:10 -0500148sk_sp<SkShader> create_checkerboard_shader(SkColor c1, SkColor c2, int size) {
149 SkBitmap bm;
150 bm.allocPixels(SkImageInfo::MakeS32(2 * size, 2 * size, kPremul_SkAlphaType));
151 bm.eraseColor(c1);
152 bm.eraseArea(SkIRect::MakeLTRB(0, 0, size, size), c2);
153 bm.eraseArea(SkIRect::MakeLTRB(size, size, 2 * size, 2 * size), c2);
Mike Reed82abece2020-12-12 09:51:11 -0500154 return bm.makeShader(SkTileMode::kRepeat, SkTileMode::kRepeat, SkSamplingOptions());
Mike Kleinea3f0142019-03-20 11:12:10 -0500155}
156
157SkBitmap create_checkerboard_bitmap(int w, int h, SkColor c1, SkColor c2, int checkSize) {
158 SkBitmap bitmap;
159 bitmap.allocPixels(SkImageInfo::MakeS32(w, h, kPremul_SkAlphaType));
160 SkCanvas canvas(bitmap);
161
162 ToolUtils::draw_checkerboard(&canvas, c1, c2, checkSize);
163 return bitmap;
164}
165
Mike Reeddb873dd2020-12-04 12:22:10 -0500166sk_sp<SkImage> create_checkerboard_image(int w, int h, SkColor c1, SkColor c2, int checkSize) {
Kevin Lubick5c93acf2023-05-09 12:11:43 -0400167 auto surf = SkSurfaces::Raster(SkImageInfo::MakeN32Premul(w, h));
Mike Reeddb873dd2020-12-04 12:22:10 -0500168 ToolUtils::draw_checkerboard(surf->getCanvas(), c1, c2, checkSize);
169 return surf->makeImageSnapshot();
170}
171
Mike Kleinea3f0142019-03-20 11:12:10 -0500172void draw_checkerboard(SkCanvas* canvas, SkColor c1, SkColor c2, int size) {
173 SkPaint paint;
174 paint.setShader(create_checkerboard_shader(c1, c2, size));
175 paint.setBlendMode(SkBlendMode::kSrc);
176 canvas->drawPaint(paint);
177}
178
Robert Phillips655489c2022-10-06 12:36:59 -0400179int make_pixmaps(SkColorType ct,
180 SkAlphaType at,
181 bool withMips,
182 const SkColor4f colors[6],
183 SkPixmap pixmaps[6],
184 std::unique_ptr<char[]>* mem) {
185
186 int levelSize = 32;
187 int numMipLevels = withMips ? 6 : 1;
188 size_t size = 0;
189 SkImageInfo ii[6];
190 size_t rowBytes[6];
191 for (int level = 0; level < numMipLevels; ++level) {
192 ii[level] = SkImageInfo::Make(levelSize, levelSize, ct, at);
193 rowBytes[level] = ii[level].minRowBytes();
194 // Make sure we test row bytes that aren't tight.
195 if (!(level % 2)) {
196 rowBytes[level] += (level + 1)*SkColorTypeBytesPerPixel(ii[level].colorType());
197 }
198 size += rowBytes[level]*ii[level].height();
199 levelSize /= 2;
200 }
201 mem->reset(new char[size]);
202 char* addr = mem->get();
203 for (int level = 0; level < numMipLevels; ++level) {
204 pixmaps[level].reset(ii[level], addr, rowBytes[level]);
205 addr += rowBytes[level]*ii[level].height();
206 pixmaps[level].erase(colors[level]);
207 }
208 return numMipLevels;
209}
210
Mike Kleinea3f0142019-03-20 11:12:10 -0500211void add_to_text_blob_w_len(SkTextBlobBuilder* builder,
212 const char* text,
213 size_t len,
214 SkTextEncoding encoding,
215 const SkFont& font,
216 SkScalar x,
217 SkScalar y) {
218 int count = font.countText(text, len, encoding);
Ben Wagner5da8e552019-12-06 16:24:00 -0500219 if (count < 1) {
220 return;
221 }
Mike Kleinea3f0142019-03-20 11:12:10 -0500222 auto run = builder->allocRun(font, count, x, y);
223 font.textToGlyphs(text, len, encoding, run.glyphs, count);
224}
225
226void add_to_text_blob(SkTextBlobBuilder* builder,
227 const char* text,
228 const SkFont& font,
229 SkScalar x,
230 SkScalar y) {
Ben Wagner51e15a62019-05-07 15:38:46 -0400231 add_to_text_blob_w_len(builder, text, strlen(text), SkTextEncoding::kUTF8, font, x, y);
Mike Kleinea3f0142019-03-20 11:12:10 -0500232}
233
234void get_text_path(const SkFont& font,
235 const void* text,
236 size_t length,
237 SkTextEncoding encoding,
238 SkPath* dst,
239 const SkPoint pos[]) {
240 SkAutoToGlyphs atg(font, text, length, encoding);
241 const int count = atg.count();
Herb Derby3b3bcf02023-01-17 15:12:15 -0500242 AutoTArray<SkPoint> computedPos;
Mike Kleinea3f0142019-03-20 11:12:10 -0500243 if (pos == nullptr) {
244 computedPos.reset(count);
245 font.getPos(atg.glyphs(), count, &computedPos[0]);
246 pos = computedPos.get();
247 }
248
249 struct Rec {
250 SkPath* fDst;
251 const SkPoint* fPos;
252 } rec = {dst, pos};
253 font.getPaths(atg.glyphs(),
254 atg.count(),
255 [](const SkPath* src, const SkMatrix& mx, void* ctx) {
256 Rec* rec = (Rec*)ctx;
257 if (src) {
258 SkMatrix tmp(mx);
259 tmp.postTranslate(rec->fPos->fX, rec->fPos->fY);
260 rec->fDst->addPath(*src, tmp);
261 }
262 rec->fPos += 1;
263 },
264 &rec);
265}
266
267SkPath make_star(const SkRect& bounds, int numPts, int step) {
268 SkASSERT(numPts != step);
Mike Reede9d783c2020-08-17 14:14:13 -0400269 SkPathBuilder builder;
270 builder.setFillType(SkPathFillType::kEvenOdd);
271 builder.moveTo(0, -1);
Mike Kleinea3f0142019-03-20 11:12:10 -0500272 for (int i = 1; i < numPts; ++i) {
273 int idx = i * step % numPts;
274 SkScalar theta = idx * 2 * SK_ScalarPI / numPts + SK_ScalarPI / 2;
275 SkScalar x = SkScalarCos(theta);
276 SkScalar y = -SkScalarSin(theta);
Mike Reede9d783c2020-08-17 14:14:13 -0400277 builder.lineTo(x, y);
Mike Kleinea3f0142019-03-20 11:12:10 -0500278 }
Mike Reede9d783c2020-08-17 14:14:13 -0400279 SkPath path = builder.detach();
Mike Reed2ac6ce82021-01-15 12:26:22 -0500280 path.transform(SkMatrix::RectToRect(path.getBounds(), bounds));
Mike Kleinea3f0142019-03-20 11:12:10 -0500281 return path;
282}
283
284static inline void norm_to_rgb(SkBitmap* bm, int x, int y, const SkVector3& norm) {
285 SkASSERT(SkScalarNearlyEqual(norm.length(), 1.0f));
286 unsigned char r = static_cast<unsigned char>((0.5f * norm.fX + 0.5f) * 255);
287 unsigned char g = static_cast<unsigned char>((-0.5f * norm.fY + 0.5f) * 255);
288 unsigned char b = static_cast<unsigned char>((0.5f * norm.fZ + 0.5f) * 255);
289 *bm->getAddr32(x, y) = SkPackARGB32(0xFF, r, g, b);
290}
291
292void create_hemi_normal_map(SkBitmap* bm, const SkIRect& dst) {
293 const SkPoint center =
294 SkPoint::Make(dst.fLeft + (dst.width() / 2.0f), dst.fTop + (dst.height() / 2.0f));
295 const SkPoint halfSize = SkPoint::Make(dst.width() / 2.0f, dst.height() / 2.0f);
296
297 SkVector3 norm;
298
299 for (int y = dst.fTop; y < dst.fBottom; ++y) {
300 for (int x = dst.fLeft; x < dst.fRight; ++x) {
301 norm.fX = (x + 0.5f - center.fX) / halfSize.fX;
302 norm.fY = (y + 0.5f - center.fY) / halfSize.fY;
303
304 SkScalar tmp = norm.fX * norm.fX + norm.fY * norm.fY;
305 if (tmp >= 1.0f) {
306 norm.set(0.0f, 0.0f, 1.0f);
307 } else {
308 norm.fZ = sqrtf(1.0f - tmp);
309 }
310
311 norm_to_rgb(bm, x, y, norm);
312 }
313 }
314}
315
316void create_frustum_normal_map(SkBitmap* bm, const SkIRect& dst) {
317 const SkPoint center =
318 SkPoint::Make(dst.fLeft + (dst.width() / 2.0f), dst.fTop + (dst.height() / 2.0f));
319
320 SkIRect inner = dst;
321 inner.inset(dst.width() / 4, dst.height() / 4);
322
323 SkPoint3 norm;
324 const SkPoint3 left = SkPoint3::Make(-SK_ScalarRoot2Over2, 0.0f, SK_ScalarRoot2Over2);
325 const SkPoint3 up = SkPoint3::Make(0.0f, -SK_ScalarRoot2Over2, SK_ScalarRoot2Over2);
326 const SkPoint3 right = SkPoint3::Make(SK_ScalarRoot2Over2, 0.0f, SK_ScalarRoot2Over2);
327 const SkPoint3 down = SkPoint3::Make(0.0f, SK_ScalarRoot2Over2, SK_ScalarRoot2Over2);
328
329 for (int y = dst.fTop; y < dst.fBottom; ++y) {
330 for (int x = dst.fLeft; x < dst.fRight; ++x) {
331 if (inner.contains(x, y)) {
332 norm.set(0.0f, 0.0f, 1.0f);
333 } else {
334 SkScalar locX = x + 0.5f - center.fX;
335 SkScalar locY = y + 0.5f - center.fY;
336
337 if (locX >= 0.0f) {
338 if (locY > 0.0f) {
339 norm = locX >= locY ? right : down; // LR corner
340 } else {
341 norm = locX > -locY ? right : up; // UR corner
342 }
343 } else {
344 if (locY > 0.0f) {
345 norm = -locX > locY ? left : down; // LL corner
346 } else {
347 norm = locX > locY ? up : left; // UL corner
348 }
349 }
350 }
351
352 norm_to_rgb(bm, x, y, norm);
353 }
354 }
355}
356
357void create_tetra_normal_map(SkBitmap* bm, const SkIRect& dst) {
358 const SkPoint center =
359 SkPoint::Make(dst.fLeft + (dst.width() / 2.0f), dst.fTop + (dst.height() / 2.0f));
360
361 static const SkScalar k1OverRoot3 = 0.5773502692f;
362
363 SkPoint3 norm;
364 const SkPoint3 leftUp = SkPoint3::Make(-k1OverRoot3, -k1OverRoot3, k1OverRoot3);
365 const SkPoint3 rightUp = SkPoint3::Make(k1OverRoot3, -k1OverRoot3, k1OverRoot3);
366 const SkPoint3 down = SkPoint3::Make(0.0f, SK_ScalarRoot2Over2, SK_ScalarRoot2Over2);
367
368 for (int y = dst.fTop; y < dst.fBottom; ++y) {
369 for (int x = dst.fLeft; x < dst.fRight; ++x) {
370 SkScalar locX = x + 0.5f - center.fX;
371 SkScalar locY = y + 0.5f - center.fY;
372
373 if (locX >= 0.0f) {
374 if (locY > 0.0f) {
375 norm = locX >= locY ? rightUp : down; // LR corner
376 } else {
377 norm = rightUp;
378 }
379 } else {
380 if (locY > 0.0f) {
381 norm = -locX > locY ? leftUp : down; // LL corner
382 } else {
383 norm = leftUp;
384 }
385 }
386
387 norm_to_rgb(bm, x, y, norm);
388 }
389 }
390}
391
Mike Kleinea3f0142019-03-20 11:12:10 -0500392bool copy_to(SkBitmap* dst, SkColorType dstColorType, const SkBitmap& src) {
393 SkPixmap srcPM;
394 if (!src.peekPixels(&srcPM)) {
395 return false;
396 }
397
398 SkBitmap tmpDst;
399 SkImageInfo dstInfo = srcPM.info().makeColorType(dstColorType);
400 if (!tmpDst.setInfo(dstInfo)) {
401 return false;
402 }
403
404 if (!tmpDst.tryAllocPixels()) {
405 return false;
406 }
407
408 SkPixmap dstPM;
409 if (!tmpDst.peekPixels(&dstPM)) {
410 return false;
411 }
412
413 if (!srcPM.readPixels(dstPM)) {
414 return false;
415 }
416
417 dst->swap(tmpDst);
418 return true;
419}
420
421void copy_to_g8(SkBitmap* dst, const SkBitmap& src) {
422 SkASSERT(kBGRA_8888_SkColorType == src.colorType() ||
423 kRGBA_8888_SkColorType == src.colorType());
424
425 SkImageInfo grayInfo = src.info().makeColorType(kGray_8_SkColorType);
426 dst->allocPixels(grayInfo);
427 uint8_t* dst8 = (uint8_t*)dst->getPixels();
428 const uint32_t* src32 = (const uint32_t*)src.getPixels();
429
430 const int w = src.width();
431 const int h = src.height();
432 const bool isBGRA = (kBGRA_8888_SkColorType == src.colorType());
433 for (int y = 0; y < h; ++y) {
434 if (isBGRA) {
435 // BGRA
436 for (int x = 0; x < w; ++x) {
437 uint32_t s = src32[x];
438 dst8[x] = SkComputeLuminance((s >> 16) & 0xFF, (s >> 8) & 0xFF, s & 0xFF);
439 }
440 } else {
441 // RGBA
442 for (int x = 0; x < w; ++x) {
443 uint32_t s = src32[x];
444 dst8[x] = SkComputeLuminance(s & 0xFF, (s >> 8) & 0xFF, (s >> 16) & 0xFF);
445 }
446 }
447 src32 = (const uint32_t*)((const char*)src32 + src.rowBytes());
448 dst8 += dst->rowBytes();
449 }
450}
451
452//////////////////////////////////////////////////////////////////////////////////////////////
453
454bool equal_pixels(const SkPixmap& a, const SkPixmap& b) {
Kevin Lubick38e85e82024-01-17 08:14:52 -0500455 if (a.width() != b.width() || a.height() != b.height()) {
456 SkDebugf("[ToolUtils::equal_pixels] Dimensions do not match (%d x %d) != (%d x %d)\n",
457 a.width(), a.height(), b.width(), b.height());
458 }
459
460 if (a.colorType() != b.colorType()) {
461 SkDebugf("[ToolUtils::equal_pixels] colorType does not match %d != %d\n",
462 (int) a.colorType(), (int) b.colorType());
Mike Kleinea3f0142019-03-20 11:12:10 -0500463 return false;
464 }
465
466 for (int y = 0; y < a.height(); ++y) {
467 const char* aptr = (const char*)a.addr(0, y);
468 const char* bptr = (const char*)b.addr(0, y);
John Stilesc1c3c6d2020-08-15 23:22:53 -0400469 if (0 != memcmp(aptr, bptr, a.width() * a.info().bytesPerPixel())) {
Kevin Lubick38e85e82024-01-17 08:14:52 -0500470 SkDebugf("[ToolUtils::equal_pixels] row %d does not match byte for byte\n", y);
Mike Kleinea3f0142019-03-20 11:12:10 -0500471 return false;
472 }
Mike Kleinea3f0142019-03-20 11:12:10 -0500473 }
474 return true;
475}
476
477bool equal_pixels(const SkBitmap& bm0, const SkBitmap& bm1) {
478 SkPixmap pm0, pm1;
479 return bm0.peekPixels(&pm0) && bm1.peekPixels(&pm1) && equal_pixels(pm0, pm1);
480}
481
482bool equal_pixels(const SkImage* a, const SkImage* b) {
483 // ensure that peekPixels will succeed
484 auto imga = a->makeRasterImage();
485 auto imgb = b->makeRasterImage();
486
487 SkPixmap pm0, pm1;
488 return imga->peekPixels(&pm0) && imgb->peekPixels(&pm1) && equal_pixels(pm0, pm1);
489}
490
491sk_sp<SkSurface> makeSurface(SkCanvas* canvas,
492 const SkImageInfo& info,
493 const SkSurfaceProps* props) {
494 auto surf = canvas->makeSurface(info, props);
495 if (!surf) {
Kevin Lubick5c93acf2023-05-09 12:11:43 -0400496 surf = SkSurfaces::Raster(info, props);
Mike Kleinea3f0142019-03-20 11:12:10 -0500497 }
498 return surf;
499}
Mike Klein19cc0f62019-03-22 15:30:07 -0500500
Dominik Röttschesebdd78b2022-08-11 18:44:40 +0300501VariationSliders::VariationSliders(SkTypeface* typeface,
502 SkFontArguments::VariationPosition variationPosition) {
Dominik Röttschesf27608b2022-07-08 17:35:18 +0300503 if (!typeface) {
504 return;
505 }
506
507 int numAxes = typeface->getVariationDesignParameters(nullptr, 0);
508 if (numAxes < 0) {
509 return;
510 }
511
512 std::unique_ptr<SkFontParameters::Variation::Axis[]> copiedAxes =
513 std::make_unique<SkFontParameters::Variation::Axis[]>(numAxes);
514
515 numAxes = typeface->getVariationDesignParameters(copiedAxes.get(), numAxes);
516 if (numAxes < 0) {
517 return;
518 }
519
Dominik Röttschesebdd78b2022-08-11 18:44:40 +0300520 auto argVariationPositionOrDefault = [&variationPosition](SkFourByteTag tag,
521 SkScalar defaultValue) -> SkScalar {
522 for (int i = 0; i < variationPosition.coordinateCount; ++i) {
523 if (variationPosition.coordinates[i].axis == tag) {
524 return variationPosition.coordinates[i].value;
525 }
526 }
527 return defaultValue;
528 };
529
Dominik Röttschesf27608b2022-07-08 17:35:18 +0300530 fAxisSliders.resize(numAxes);
Dominik Röttschesebdd78b2022-08-11 18:44:40 +0300531 fCoords = std::make_unique<SkFontArguments::VariationPosition::Coordinate[]>(numAxes);
Dominik Röttschesf27608b2022-07-08 17:35:18 +0300532 for (int i = 0; i < numAxes; ++i) {
533 fAxisSliders[i].axis = copiedAxes[i];
Dominik Röttschesebdd78b2022-08-11 18:44:40 +0300534 fAxisSliders[i].current =
535 argVariationPositionOrDefault(copiedAxes[i].tag, copiedAxes[i].def);
Dominik Röttschesf27608b2022-07-08 17:35:18 +0300536 fAxisSliders[i].name = tagToString(fAxisSliders[i].axis.tag);
Dominik Röttschesebdd78b2022-08-11 18:44:40 +0300537 fCoords[i] = { fAxisSliders[i].axis.tag, fAxisSliders[i].current };
Dominik Röttschesf27608b2022-07-08 17:35:18 +0300538 }
Dominik Röttschesf27608b2022-07-08 17:35:18 +0300539}
540
541/* static */
542SkString VariationSliders::tagToString(SkFourByteTag tag) {
543 char tagAsString[5];
544 tagAsString[4] = 0;
545 tagAsString[0] = (char)(uint8_t)(tag >> 24);
546 tagAsString[1] = (char)(uint8_t)(tag >> 16);
547 tagAsString[2] = (char)(uint8_t)(tag >> 8);
548 tagAsString[3] = (char)(uint8_t)(tag >> 0);
549 return SkString(tagAsString);
550}
551
552bool VariationSliders::writeControls(SkMetaData* controls) {
553 for (size_t i = 0; i < fAxisSliders.size(); ++i) {
554 SkScalar axisVars[kAxisVarsSize];
555
556 axisVars[0] = fAxisSliders[i].current;
557 axisVars[1] = fAxisSliders[i].axis.min;
558 axisVars[2] = fAxisSliders[i].axis.max;
559 controls->setScalars(fAxisSliders[i].name.c_str(), kAxisVarsSize, axisVars);
560 }
561 return true;
562}
563
564void VariationSliders::readControls(const SkMetaData& controls, bool* changed) {
565 for (size_t i = 0; i < fAxisSliders.size(); ++i) {
566 SkScalar axisVars[kAxisVarsSize] = {0};
567 int resultAxisVarsSize = 0;
568 SkASSERT_RELEASE(controls.findScalars(
569 tagToString(fAxisSliders[i].axis.tag).c_str(), &resultAxisVarsSize, axisVars));
570 SkASSERT_RELEASE(resultAxisVarsSize == kAxisVarsSize);
571 if (changed) {
572 *changed |= fAxisSliders[i].current != axisVars[0];
573 }
574 fAxisSliders[i].current = axisVars[0];
575 fCoords[i] = { fAxisSliders[i].axis.tag, fAxisSliders[i].current };
576 }
577}
578
579SkSpan<const SkFontArguments::VariationPosition::Coordinate> VariationSliders::getCoordinates() {
580 return SkSpan<const SkFontArguments::VariationPosition::Coordinate>{fCoords.get(),
581 fAxisSliders.size()};
582}
583
Robert Phillips861a11a2023-06-07 10:49:14 -0400584////////////////////////////////////////////////////////////////////////////////////////////////////
585HilbertGenerator::HilbertGenerator(float desiredSize, float desiredLineWidth, int desiredDepth)
586 : fDesiredSize(desiredSize)
587 , fDesiredDepth(desiredDepth)
588 , fSegmentLength(fDesiredSize / ((0x1 << fDesiredDepth) - 1.0f))
589 , fDesiredLineWidth(desiredLineWidth)
590 , fActualBounds(SkRect::MakeEmpty())
591 , fCurPos(SkPoint::Make(0.0f, 0.0f))
592 , fCurDir(0)
593 , fExpectedLen(fSegmentLength * ((0x1 << (2*fDesiredDepth)) - 1.0f))
594 , fCurLen(0.0f) {
595}
596
597void HilbertGenerator::draw(SkCanvas* canvas) {
598 this->recursiveDraw(canvas, /* curDepth= */ 0, /* turnLeft= */ true);
599
600 SkScalarNearlyEqual(fExpectedLen, fCurLen, 0.01f);
601 SkScalarNearlyEqual(fDesiredSize, fActualBounds.width(), 0.01f);
602 SkScalarNearlyEqual(fDesiredSize, fActualBounds.height(), 0.01f);
603}
604
605void HilbertGenerator::turn90(bool turnLeft) {
606 fCurDir += turnLeft ? 90 : -90;
607 if (fCurDir >= 360) {
608 fCurDir = 0;
609 } else if (fCurDir < 0) {
610 fCurDir = 270;
611 }
612
613 SkASSERT(fCurDir == 0 || fCurDir == 90 || fCurDir == 180 || fCurDir == 270);
614}
615
616void HilbertGenerator::line(SkCanvas* canvas) {
617
618 SkPoint before = fCurPos;
619
620 SkRect r;
621 switch (fCurDir) {
622 case 0:
623 r.fLeft = fCurPos.fX;
624 r.fTop = fCurPos.fY - fDesiredLineWidth / 2.0f;
625 r.fRight = fCurPos.fX + fSegmentLength;
626 r.fBottom = fCurPos.fY + fDesiredLineWidth / 2.0f;
627 fCurPos.fX += fSegmentLength;
628 break;
629 case 90:
630 r.fLeft = fCurPos.fX - fDesiredLineWidth / 2.0f;
631 r.fTop = fCurPos.fY - fSegmentLength;
632 r.fRight = fCurPos.fX + fDesiredLineWidth / 2.0f;
633 r.fBottom = fCurPos.fY;
634 fCurPos.fY -= fSegmentLength;
635 break;
636 case 180:
637 r.fLeft = fCurPos.fX - fSegmentLength;
638 r.fTop = fCurPos.fY - fDesiredLineWidth / 2.0f;
639 r.fRight = fCurPos.fX;
640 r.fBottom = fCurPos.fY + fDesiredLineWidth / 2.0f;
641 fCurPos.fX -= fSegmentLength;
642 break;
643 case 270:
644 r.fLeft = fCurPos.fX - fDesiredLineWidth / 2.0f;
645 r.fTop = fCurPos.fY;
646 r.fRight = fCurPos.fX + fDesiredLineWidth / 2.0f;
647 r.fBottom = fCurPos.fY + fSegmentLength;
648 fCurPos.fY += fSegmentLength;
649 break;
650 default:
651 return;
652 }
653
654 SkPoint pts[2] = { before, fCurPos };
655
656 SkColor4f colors[2] = {
657 this->getColor(fCurLen),
658 this->getColor(fCurLen + fSegmentLength),
659 };
660
661 fCurLen += fSegmentLength;
662 if (fActualBounds.isEmpty()) {
663 fActualBounds = r;
664 } else {
665 fActualBounds.join(r);
666 }
667
668 SkPaint paint;
669 paint.setShader(SkGradientShader::MakeLinear(pts, colors, /* colorSpace= */ nullptr,
670 /* pos= */ nullptr, 2, SkTileMode::kClamp));
671 canvas->drawRect(r, paint);
672}
673
674void HilbertGenerator::recursiveDraw(SkCanvas* canvas, int curDepth, bool turnLeft) {
675 if (curDepth >= fDesiredDepth) {
676 return;
677 }
678
679 this->turn90(turnLeft);
680 this->recursiveDraw(canvas, curDepth + 1, !turnLeft);
681 this->line(canvas);
682 this->turn90(!turnLeft);
683 this->recursiveDraw(canvas, curDepth + 1, turnLeft);
684 this->line(canvas);
685 this->recursiveDraw(canvas, curDepth + 1, turnLeft);
686 this->turn90(!turnLeft);
687 this->line(canvas);
688 this->recursiveDraw(canvas, curDepth + 1, !turnLeft);
689 this->turn90(turnLeft);
690}
691
692SkColor4f HilbertGenerator::getColor(float curLen) {
693 static const SkColor4f kColors[] = {
694 SkColors::kBlack,
695 SkColors::kBlue,
696 SkColors::kCyan,
697 SkColors::kGreen,
698 SkColors::kYellow,
699 SkColors::kRed,
700 SkColors::kWhite,
701 };
702
703 static const float kStops[] = {
704 0.0f,
705 1.0f/6.0f,
706 2.0f/6.0f,
707 0.5f,
708 4.0f/6.0f,
709 5.0f/6.0f,
710 1.0f,
711 };
712 static_assert(std::size(kColors) == std::size(kStops));
713
714 float t = curLen / fExpectedLen;
715 if (t <= 0.0f) {
716 return kColors[0];
717 } else if (t >= 1.0f) {
718 return kColors[std::size(kColors)-1];
719 }
720
721 for (unsigned int i = 0; i < std::size(kColors)-1; ++i) {
722 if (kStops[i] <= t && t <= kStops[i+1]) {
723 t = (t - kStops[i]) / (kStops[i+1] - kStops[i]);
724 SkASSERT(0.0f <= t && t <= 1.0f);
725 return { kColors[i].fR * (1 - t) + kColors[i+1].fR * t,
726 kColors[i].fG * (1 - t) + kColors[i+1].fG * t,
727 kColors[i].fB * (1 - t) + kColors[i+1].fB * t,
728 kColors[i].fA * (1 - t) + kColors[i+1].fA * t };
729
730 }
731 }
732
733 return SkColors::kBlack;
734}
735
Kevin Lubick9b028372023-10-05 15:04:54 -0400736void ExtractPathsFromSKP(const char filepath[], std::function<PathSniffCallback> callback) {
737 SkFILEStream stream(filepath);
738 if (!stream.isValid()) {
739 SkDebugf("ExtractPaths: invalid input file at \"%s\"\n", filepath);
740 return;
741 }
742
743 class PathSniffer : public SkCanvas {
744 public:
745 PathSniffer(std::function<PathSniffCallback> callback)
746 : SkCanvas(4096, 4096, nullptr)
747 , fPathSniffCallback(callback) {}
748 private:
749 void onDrawPath(const SkPath& path, const SkPaint& paint) override {
750 fPathSniffCallback(this->getTotalMatrix(), path, paint);
751 }
752 std::function<PathSniffCallback> fPathSniffCallback;
753 };
754
755 sk_sp<SkPicture> skp = SkPicture::MakeFromStream(&stream);
756 if (!skp) {
757 SkDebugf("ExtractPaths: couldn't load skp at \"%s\"\n", filepath);
758 return;
759 }
760 PathSniffer pathSniffer(callback);
761 skp->playback(&pathSniffer);
762}
763
Mike Kleinea3f0142019-03-20 11:12:10 -0500764} // namespace ToolUtils