xref: /aosp_15_r20/external/skia/bench/PDFBench.cpp (revision c8dee2aa9b3f27cf6c858bd81872bdeb2c07ed17)
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