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