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