1 /*
2 * Copyright 2016 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 "bench/Benchmark.h"
9
10 #include "include/core/SkBitmap.h"
11 #include "include/core/SkData.h"
12 #include "include/core/SkExecutor.h"
13 #include "include/core/SkImage.h"
14 #include "include/core/SkPath.h"
15 #include "include/core/SkPixmap.h"
16 #include "include/core/SkStream.h"
17 #include "include/effects/SkGradientShader.h"
18 #include "include/private/base/SkTo.h"
19 #include "src/base/SkRandom.h"
20 #include "src/core/SkAutoPixmapStorage.h"
21 #include "src/pdf/SkPDFUnion.h"
22 #include "src/utils/SkFloatToDecimal.h"
23 #include "tools/DecodeUtils.h"
24 #include "tools/Resources.h"
25 #include "tools/fonts/FontToolUtils.h"
26
27 namespace {
28 struct WStreamWriteTextBenchmark : public Benchmark {
29 std::unique_ptr<SkWStream> fWStream;
WStreamWriteTextBenchmark__anon900292980111::WStreamWriteTextBenchmark30 WStreamWriteTextBenchmark() : fWStream(new SkNullWStream) {}
onGetName__anon900292980111::WStreamWriteTextBenchmark31 const char* onGetName() override { return "WStreamWriteText"; }
isSuitableFor__anon900292980111::WStreamWriteTextBenchmark32 bool isSuitableFor(Backend backend) override {
33 return backend == Backend::kNonRendering;
34 }
onDraw__anon900292980111::WStreamWriteTextBenchmark35 void onDraw(int loops, SkCanvas*) override {
36 while (loops-- > 0) {
37 for (int i = 1000; i-- > 0;) {
38 fWStream->writeText("HELLO SKIA!\n");
39 }
40 }
41 }
42 };
43 } // namespace
44
45 DEF_BENCH(return new WStreamWriteTextBenchmark;)
46
47 // Test speed of SkFloatToDecimal for typical floats that
48 // might be found in a PDF document.
49 struct PDFScalarBench : public Benchmark {
PDFScalarBenchPDFScalarBench50 PDFScalarBench(const char* n, float (*f)(SkRandom*)) : fName(n), fNextFloat(f) {}
51 const char* fName;
52 float (*fNextFloat)(SkRandom*);
isSuitableForPDFScalarBench53 bool isSuitableFor(Backend b) override {
54 return b == Backend::kNonRendering;
55 }
onGetNamePDFScalarBench56 const char* onGetName() override { return fName; }
onDrawPDFScalarBench57 void onDraw(int loops, SkCanvas*) override {
58 SkRandom random;
59 char dst[kMaximumSkFloatToDecimalLength];
60 while (loops-- > 0) {
61 auto f = fNextFloat(&random);
62 (void)SkFloatToDecimal(f, dst);
63 }
64 }
65 };
66
next_common(SkRandom * random)67 float next_common(SkRandom* random) {
68 return random->nextRangeF(-500.0f, 1500.0f);
69 }
next_any(SkRandom * random)70 float next_any(SkRandom* random) {
71 union { uint32_t u; float f; };
72 u = random->nextU();
73 static_assert(sizeof(float) == sizeof(uint32_t), "");
74 return f;
75 }
76
77 DEF_BENCH(return new PDFScalarBench("PDFScalar_common", next_common);)
78 DEF_BENCH(return new PDFScalarBench("PDFScalar_random", next_any);)
79
80 #ifdef SK_SUPPORT_PDF
81
82 #include "src/pdf/SkPDFBitmap.h"
83 #include "src/pdf/SkPDFDocumentPriv.h"
84 #include "src/pdf/SkPDFShader.h"
85 #include "src/pdf/SkPDFUtils.h"
86
87 namespace {
88 class PDFImageBench : public Benchmark {
89 public:
PDFImageBench()90 PDFImageBench() {}
~PDFImageBench()91 ~PDFImageBench() override {}
92
93 protected:
onGetName()94 const char* onGetName() override { return "PDFImage"; }
isSuitableFor(Backend backend)95 bool isSuitableFor(Backend backend) override {
96 return backend == Backend::kNonRendering;
97 }
onDelayedSetup()98 void onDelayedSetup() override {
99 sk_sp<SkImage> img(ToolUtils::GetResourceAsImage("images/color_wheel.png"));
100 if (img) {
101 // force decoding, throw away reference to encoded data.
102 SkAutoPixmapStorage pixmap;
103 pixmap.alloc(SkImageInfo::MakeN32Premul(img->dimensions()));
104 if (img->readPixels(nullptr, pixmap, 0, 0)) {
105 fImage = SkImages::RasterFromPixmapCopy(pixmap);
106 }
107 }
108 }
onDraw(int loops,SkCanvas *)109 void onDraw(int loops, SkCanvas*) override {
110 if (!fImage) {
111 return;
112 }
113 while (loops-- > 0) {
114 SkNullWStream nullStream;
115 SkPDFDocument doc(&nullStream, SkPDF::Metadata());
116 doc.beginPage(256, 256);
117 (void)SkPDFSerializeImage(fImage.get(), &doc);
118 }
119 }
120
121 private:
122 sk_sp<SkImage> fImage;
123 };
124
125 class PDFJpegImageBench : public Benchmark {
126 public:
PDFJpegImageBench()127 PDFJpegImageBench() {}
~PDFJpegImageBench()128 ~PDFJpegImageBench() override {}
129
130 protected:
onGetName()131 const char* onGetName() override { return "PDFJpegImage"; }
isSuitableFor(Backend backend)132 bool isSuitableFor(Backend backend) override {
133 return backend == Backend::kNonRendering;
134 }
onDelayedSetup()135 void onDelayedSetup() override {
136 sk_sp<SkImage> img(ToolUtils::GetResourceAsImage("images/mandrill_512_q075.jpg"));
137 if (!img) { return; }
138 sk_sp<SkData> encoded = img->refEncodedData();
139 SkASSERT(encoded);
140 if (!encoded) { return; }
141 fImage = img;
142 }
onDraw(int loops,SkCanvas *)143 void onDraw(int loops, SkCanvas*) override {
144 if (!fImage) {
145 SkDEBUGFAIL("");
146 return;
147 }
148 while (loops-- > 0) {
149 SkNullWStream nullStream;
150 SkPDFDocument doc(&nullStream, SkPDF::Metadata());
151 doc.beginPage(256, 256);
152 (void)SkPDFSerializeImage(fImage.get(), &doc);
153 }
154 }
155
156 private:
157 sk_sp<SkImage> fImage;
158 };
159
160 /** Test calling DEFLATE on a 78k PDF command stream. Used for measuring
161 alternate zlib settings, usage, and library versions. */
162 class PDFCompressionBench : public Benchmark {
163 public:
PDFCompressionBench()164 PDFCompressionBench() {}
~PDFCompressionBench()165 ~PDFCompressionBench() override {}
166
167 protected:
onGetName()168 const char* onGetName() override { return "PDFCompression"; }
isSuitableFor(Backend backend)169 bool isSuitableFor(Backend backend) override {
170 return backend == Backend::kNonRendering;
171 }
onDelayedSetup()172 void onDelayedSetup() override {
173 fAsset = GetResourceAsStream("pdf_command_stream.txt");
174 }
onDraw(int loops,SkCanvas *)175 void onDraw(int loops, SkCanvas*) override {
176 SkASSERT(fAsset);
177 if (!fAsset) { return; }
178 while (loops-- > 0) {
179 SkNullWStream wStream;
180 SkPDFDocument doc(&wStream, SkPDF::Metadata());
181 doc.beginPage(256, 256);
182 (void)SkPDFStreamOut(nullptr, fAsset->duplicate(),
183 &doc, SkPDFSteamCompressionEnabled::Yes);
184 }
185 }
186
187 private:
188 std::unique_ptr<SkStreamAsset> fAsset;
189 };
190
191 struct PDFColorComponentBench : public Benchmark {
isSuitableFor__anon900292980311::PDFColorComponentBench192 bool isSuitableFor(Backend b) override {
193 return b == Backend::kNonRendering;
194 }
onGetName__anon900292980311::PDFColorComponentBench195 const char* onGetName() override { return "PDFColorComponent"; }
onDraw__anon900292980311::PDFColorComponentBench196 void onDraw(int loops, SkCanvas*) override {
197 char dst[5];
198 while (loops-- > 0) {
199 for (int i = 0; i < 256; ++i) {
200 (void)SkPDFUtils::ColorToDecimal(SkToU8(i), dst);
201 }
202 }
203 }
204 };
205
206 struct PDFShaderBench : public Benchmark {
207 sk_sp<SkShader> fShader;
onGetName__anon900292980311::PDFShaderBench208 const char* onGetName() final { return "PDFShader"; }
isSuitableFor__anon900292980311::PDFShaderBench209 bool isSuitableFor(Backend b) final { return b == Backend::kNonRendering; }
onDelayedSetup__anon900292980311::PDFShaderBench210 void onDelayedSetup() final {
211 const SkPoint pts[2] = {{0.0f, 0.0f}, {100.0f, 100.0f}};
212 const SkColor colors[] = {
213 SK_ColorRED, SK_ColorGREEN, SK_ColorBLUE,
214 SK_ColorWHITE, SK_ColorBLACK,
215 };
216 fShader = SkGradientShader::MakeLinear(
217 pts, colors, nullptr, std::size(colors),
218 SkTileMode::kClamp);
219 }
onDraw__anon900292980311::PDFShaderBench220 void onDraw(int loops, SkCanvas*) final {
221 SkASSERT(fShader);
222 while (loops-- > 0) {
223 SkNullWStream nullStream;
224 SkPDFDocument doc(&nullStream, SkPDF::Metadata());
225 doc.beginPage(256, 256);
226 (void) SkPDFMakeShader(&doc, fShader.get(), SkMatrix::I(),
227 {0, 0, 400, 400}, SkColors::kBlack);
228 }
229 }
230 };
231
232 struct WritePDFTextBenchmark : public Benchmark {
233 std::unique_ptr<SkWStream> fWStream;
WritePDFTextBenchmark__anon900292980311::WritePDFTextBenchmark234 WritePDFTextBenchmark() : fWStream(new SkNullWStream) {}
onGetName__anon900292980311::WritePDFTextBenchmark235 const char* onGetName() override { return "WritePDFText"; }
isSuitableFor__anon900292980311::WritePDFTextBenchmark236 bool isSuitableFor(Backend backend) override {
237 return backend == Backend::kNonRendering;
238 }
onDraw__anon900292980311::WritePDFTextBenchmark239 void onDraw(int loops, SkCanvas*) override {
240 static const char kHello[] = "HELLO SKIA!\n";
241 static const char kBinary[] = "\001\002\003\004\005\006";
242 while (loops-- > 0) {
243 for (int i = 1000; i-- > 0;) {
244 SkPDFWriteTextString(fWStream.get(), kHello, strlen(kHello));
245 SkPDFWriteByteString(fWStream.get(), kBinary, strlen(kBinary));
246 }
247 }
248 }
249 };
250
251 // Test for regression chromium:947381
252 // with 5c83ae81aa : 2364.99 microsec
253 // without 5c83ae81aa : 302821.78 microsec
254 struct PDFClipPathBenchmark : public Benchmark {
255 SkPath fPath;
onDelayedSetup__anon900292980311::PDFClipPathBenchmark256 void onDelayedSetup() override {
257 SkBitmap bitmap;
258 bitmap.allocN32Pixels(256, 256);
259 bitmap.eraseColor(SK_ColorWHITE);
260 {
261 SkCanvas tmp(bitmap);
262 SkPaint paint;
263 paint.setAntiAlias(false);
264 paint.setStyle(SkPaint::kStroke_Style);
265 paint.setStrokeWidth(10);
266 for (int r : {20, 40, 60, 80, 100, 120}) {
267 tmp.drawCircle(128, 128, (float)r, paint);
268 }
269 }
270 fPath.reset();
271 for (int y = 0; y < 256; ++y) {
272 SkColor current = bitmap.getColor(0, y);
273 int start = 0;
274 for (int x = 0; x < 256; ++x) {
275 SkColor color = bitmap.getColor(x, y);
276 if (color == current) {
277 continue;
278 }
279 if (color == SK_ColorBLACK) {
280 start = x;
281 } else {
282 fPath.addRect(SkRect::Make(SkIRect{start, y, x, y + 1}));
283 }
284 current = color;
285 }
286 if (current == SK_ColorBLACK) {
287 fPath.addRect(SkRect::Make(SkIRect{start, y, 256, y + 1}));
288 }
289 }
290 }
onGetName__anon900292980311::PDFClipPathBenchmark291 const char* onGetName() override { return "PDFClipPath"; }
isSuitableFor__anon900292980311::PDFClipPathBenchmark292 bool isSuitableFor(Backend backend) override {
293 return backend == Backend::kNonRendering;
294 }
onDraw__anon900292980311::PDFClipPathBenchmark295 void onDraw(int loops, SkCanvas*) override {
296 while (loops-- > 0) {
297 SkNullWStream wStream;
298 SkPDFDocument doc(&wStream, SkPDF::Metadata());
299 SkCanvas* canvas = doc.beginPage(256, 256);
300 canvas->clipPath(fPath);
301 canvas->translate(4.0f/3, 4.0f/3);
302 canvas->clipPath(fPath);
303 canvas->clear(SK_ColorRED);
304 doc.endPage();
305 }
306 }
307 };
308
309 } // namespace
310 DEF_BENCH(return new PDFImageBench;)
311 DEF_BENCH(return new PDFJpegImageBench;)
312 DEF_BENCH(return new PDFCompressionBench;)
313 DEF_BENCH(return new PDFColorComponentBench;)
314 DEF_BENCH(return new PDFShaderBench;)
315 DEF_BENCH(return new WritePDFTextBenchmark;)
316 DEF_BENCH(return new PDFClipPathBenchmark;)
317
318 #ifdef SK_PDF_ENABLE_SLOW_TESTS
319 #include "include/core/SkExecutor.h"
320 namespace {
big_pdf_test(SkDocument * doc,const SkBitmap & background)321 void big_pdf_test(SkDocument* doc, const SkBitmap& background) {
322 static const char* kText[] = {
323 "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do",
324 "eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad",
325 "minim veniam, quis nostrud exercitation ullamco laboris nisi ut",
326 "aliquip ex ea commodo consequat. Duis aute irure dolor in",
327 "reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla",
328 "pariatur. Excepteur sint occaecat cupidatat non proident, sunt in",
329 "culpa qui officia deserunt mollit anim id est laborum.",
330 "",
331 "Sed ut perspiciatis, unde omnis iste natus error sit voluptatem",
332 "accusantium doloremque laudantium, totam rem aperiam eaque ipsa, quae",
333 "ab illo inventore veritatis et quasi architecto beatae vitae dicta",
334 "sunt, explicabo. Nemo enim ipsam voluptatem, quia voluptas sit,",
335 "aspernatur aut odit aut fugit, sed quia consequuntur magni dolores",
336 "eos, qui ratione voluptatem sequi nesciunt, neque porro quisquam est,",
337 "qui dolorem ipsum, quia dolor sit amet consectetur adipiscing velit,",
338 "sed quia non numquam do eius modi tempora incididunt, ut labore et",
339 "dolore magnam aliquam quaerat voluptatem. Ut enim ad minima veniam,",
340 "quis nostrum exercitationem ullam corporis suscipit laboriosam, nisi",
341 "ut aliquid ex ea commodi consequatur? Quis autem vel eum iure",
342 "reprehenderit, qui in ea voluptate velit esse, quam nihil molestiae",
343 "consequatur, vel illum, qui dolorem eum fugiat, quo voluptas nulla",
344 "pariatur?",
345 "",
346 "At vero eos et accusamus et iusto odio dignissimos ducimus, qui",
347 "blanditiis praesentium voluptatum deleniti atque corrupti, quos",
348 "dolores et quas molestias excepturi sint, obcaecati cupiditate non",
349 "provident, similique sunt in culpa, qui officia deserunt mollitia",
350 "animi, id est laborum et dolorum fuga. Et harum quidem rerum facilis",
351 "est et expedita distinctio. Nam libero tempore, cum soluta nobis est",
352 "eligendi optio, cumque nihil impedit, quo minus id, quod maxime",
353 "placeat, facere possimus, omnis voluptas assumenda est, omnis dolor",
354 "repellendus. Temporibus autem quibusdam et aut officiis debitis aut",
355 "rerum necessitatibus saepe eveniet, ut et voluptates repudiandae sint",
356 "et molestiae non recusandae. Itaque earum rerum hic tenetur a sapiente",
357 "delectus, ut aut reiciendis voluptatibus maiores alias consequatur aut",
358 "perferendis doloribus asperiores repellat",
359 "",
360 "Sed ut perspiciatis, unde omnis iste natus error sit voluptatem",
361 "accusantium doloremque laudantium, totam rem aperiam eaque ipsa, quae",
362 "ab illo inventore veritatis et quasi architecto beatae vitae dicta",
363 "sunt, explicabo. Nemo enim ipsam voluptatem, quia voluptas sit,",
364 "aspernatur aut odit aut fugit, sed quia consequuntur magni dolores",
365 "eos, qui ratione voluptatem sequi nesciunt, neque porro quisquam est,",
366 "qui dolorem ipsum, quia dolor sit amet consectetur adipiscing velit,",
367 "sed quia non numquam do eius modi tempora incididunt, ut labore et",
368 "dolore magnam aliquam quaerat voluptatem. Ut enim ad minima veniam,",
369 "quis nostrum exercitationem ullam corporis suscipit laboriosam, nisi",
370 "ut aliquid ex ea commodi consequatur? Quis autem vel eum iure",
371 "reprehenderit, qui in ea voluptate velit esse, quam nihil molestiae",
372 "consequatur, vel illum, qui dolorem eum fugiat, quo voluptas nulla",
373 "pariatur?",
374 "",
375 };
376 SkCanvas* canvas = nullptr;
377 float x = 36;
378 float y = 36;
379 constexpr size_t kLineCount = std::size(kText);
380 constexpr int kLoopCount = 200;
381 SkFont font = ToolUtils::DefaultFont();
382 SkPaint paint;
383 for (int loop = 0; loop < kLoopCount; ++loop) {
384 for (size_t line = 0; line < kLineCount; ++line) {
385 y += font.getSpacing();
386 if (!canvas || y > 792 - 36) {
387 y = 36 + font.getSpacing();
388 canvas = doc->beginPage(612, 792);
389 background.notifyPixelsChanged();
390 canvas->drawBitmap(background, 0, 0);
391 }
392 canvas->drawString(kText[line], x, y, font, paint);
393 }
394 }
395 }
396
make_background()397 SkBitmap make_background() {
398 SkBitmap background;
399 SkBitmap bitmap;
400 bitmap.allocN32Pixels(32, 32);
401 bitmap.eraseColor(SK_ColorWHITE);
402 SkCanvas tmp(bitmap);
403 SkPaint gray;
404 gray.setColor(SkColorSetARGB(0xFF, 0xEE, 0xEE, 0xEE));
405 tmp.drawRect({0,0,16,16}, gray);
406 tmp.drawRect({16,16,32,32}, gray);
407 SkPaint shader;
408 shader.setShader(
409 SkShader::MakeBitmapShader(
410 bitmap, SkShader::kRepeat_TileMode, SkShader::kRepeat_TileMode));
411 background.allocN32Pixels(612, 792);
412 SkCanvas tmp2(background);
413 tmp2.drawPaint(shader);
414 return background;
415 }
416
417 struct PDFBigDocBench : public Benchmark {
418 bool fFast;
419 SkBitmap fBackground;
420 std::unique_ptr<SkExecutor> fExecutor;
PDFBigDocBench__anon900292980411::PDFBigDocBench421 PDFBigDocBench(bool fast) : fFast(fast) {}
onDelayedSetup__anon900292980411::PDFBigDocBench422 void onDelayedSetup() override {
423 fBackground = make_background();
424 fExecutor = fFast ? SkExecutor::MakeFIFOThreadPool() : nullptr;
425 }
onGetName__anon900292980411::PDFBigDocBench426 const char* onGetName() override {
427 static const char kNameFast[] = "PDFBigDocBench_fast";
428 static const char kNameSlow[] = "PDFBigDocBench_slow";
429 return fFast ? kNameFast : kNameSlow;
430 }
isSuitableFor__anon900292980411::PDFBigDocBench431 bool isSuitableFor(Backend backend) override { return backend == Backend::kNonRendering; }
onDraw__anon900292980411::PDFBigDocBench432 void onDraw(int loops, SkCanvas*) override {
433 while (loops-- > 0) {
434 #ifdef SK_PDF_TEST_BIGDOCBENCH_OUTPUT
435 SkFILEWStream wStream("/tmp/big_pdf.pdf");
436 #else
437 SkNullWStream wStream;
438 #endif
439 SkPDF::Metadata metadata;
440 metadata.fExecutor = fExecutor.get();
441 auto doc = SkPDF::MakeDocument(&wStream, metadata);
442 big_pdf_test(doc.get(), fBackground);
443 }
444 }
445 };
446 } // namespace
447 DEF_BENCH(return new PDFBigDocBench(false);)
448 DEF_BENCH(return new PDFBigDocBench(true);)
449 #endif
450
451 #endif // SK_SUPPORT_PDF
452