|  | /* | 
|  | * Copyright 2016 Google Inc. | 
|  | * | 
|  | * Use of this source code is governed by a BSD-style license that can be | 
|  | * found in the LICENSE file. | 
|  | */ | 
|  |  | 
|  | #include "bench/Benchmark.h" | 
|  |  | 
|  | #include "include/core/SkBitmap.h" | 
|  | #include "include/core/SkData.h" | 
|  | #include "include/core/SkExecutor.h" | 
|  | #include "include/core/SkImage.h" | 
|  | #include "include/core/SkPixmap.h" | 
|  | #include "include/core/SkStream.h" | 
|  | #include "include/effects/SkGradientShader.h" | 
|  | #include "include/private/base/SkTo.h" | 
|  | #include "src/base/SkRandom.h" | 
|  | #include "src/core/SkAutoPixmapStorage.h" | 
|  | #include "src/pdf/SkPDFUnion.h" | 
|  | #include "src/utils/SkFloatToDecimal.h" | 
|  | #include "tools/DecodeUtils.h" | 
|  | #include "tools/Resources.h" | 
|  | #include "tools/fonts/FontToolUtils.h" | 
|  |  | 
|  | namespace { | 
|  | struct WStreamWriteTextBenchmark : public Benchmark { | 
|  | std::unique_ptr<SkWStream> fWStream; | 
|  | WStreamWriteTextBenchmark() : fWStream(new SkNullWStream) {} | 
|  | const char* onGetName() override { return "WStreamWriteText"; } | 
|  | bool isSuitableFor(Backend backend) override { | 
|  | return backend == Backend::kNonRendering; | 
|  | } | 
|  | void onDraw(int loops, SkCanvas*) override { | 
|  | while (loops-- > 0) { | 
|  | for (int i = 1000; i-- > 0;) { | 
|  | fWStream->writeText("HELLO SKIA!\n"); | 
|  | } | 
|  | } | 
|  | } | 
|  | }; | 
|  | }  // namespace | 
|  |  | 
|  | DEF_BENCH(return new WStreamWriteTextBenchmark;) | 
|  |  | 
|  | // Test speed of SkFloatToDecimal for typical floats that | 
|  | // might be found in a PDF document. | 
|  | struct PDFScalarBench : public Benchmark { | 
|  | PDFScalarBench(const char* n, float (*f)(SkRandom*)) : fName(n), fNextFloat(f) {} | 
|  | const char* fName; | 
|  | float (*fNextFloat)(SkRandom*); | 
|  | bool isSuitableFor(Backend b) override { | 
|  | return b == Backend::kNonRendering; | 
|  | } | 
|  | const char* onGetName() override { return fName; } | 
|  | void onDraw(int loops, SkCanvas*) override { | 
|  | SkRandom random; | 
|  | char dst[kMaximumSkFloatToDecimalLength]; | 
|  | while (loops-- > 0) { | 
|  | auto f = fNextFloat(&random); | 
|  | (void)SkFloatToDecimal(f, dst); | 
|  | } | 
|  | } | 
|  | }; | 
|  |  | 
|  | float next_common(SkRandom* random) { | 
|  | return random->nextRangeF(-500.0f, 1500.0f); | 
|  | } | 
|  | float next_any(SkRandom* random) { | 
|  | union { uint32_t u; float f; }; | 
|  | u = random->nextU(); | 
|  | static_assert(sizeof(float) == sizeof(uint32_t), ""); | 
|  | return f; | 
|  | } | 
|  |  | 
|  | DEF_BENCH(return new PDFScalarBench("PDFScalar_common", next_common);) | 
|  | DEF_BENCH(return new PDFScalarBench("PDFScalar_random", next_any);) | 
|  |  | 
|  | #ifdef SK_SUPPORT_PDF | 
|  |  | 
|  | #include "src/pdf/SkPDFBitmap.h" | 
|  | #include "src/pdf/SkPDFDocumentPriv.h" | 
|  | #include "src/pdf/SkPDFShader.h" | 
|  | #include "src/pdf/SkPDFUtils.h" | 
|  |  | 
|  | namespace { | 
|  | class PDFImageBench : public Benchmark { | 
|  | public: | 
|  | PDFImageBench() {} | 
|  | ~PDFImageBench() override {} | 
|  |  | 
|  | protected: | 
|  | const char* onGetName() override { return "PDFImage"; } | 
|  | bool isSuitableFor(Backend backend) override { | 
|  | return backend == Backend::kNonRendering; | 
|  | } | 
|  | void onDelayedSetup() override { | 
|  | sk_sp<SkImage> img(ToolUtils::GetResourceAsImage("images/color_wheel.png")); | 
|  | if (img) { | 
|  | // force decoding, throw away reference to encoded data. | 
|  | SkAutoPixmapStorage pixmap; | 
|  | pixmap.alloc(SkImageInfo::MakeN32Premul(img->dimensions())); | 
|  | if (img->readPixels(nullptr, pixmap, 0, 0)) { | 
|  | fImage = SkImages::RasterFromPixmapCopy(pixmap); | 
|  | } | 
|  | } | 
|  | } | 
|  | void onDraw(int loops, SkCanvas*) override { | 
|  | if (!fImage) { | 
|  | return; | 
|  | } | 
|  | while (loops-- > 0) { | 
|  | SkNullWStream nullStream; | 
|  | SkPDFDocument doc(&nullStream, SkPDF::Metadata()); | 
|  | doc.beginPage(256, 256); | 
|  | (void)SkPDFSerializeImage(fImage.get(), &doc); | 
|  | } | 
|  | } | 
|  |  | 
|  | private: | 
|  | sk_sp<SkImage> fImage; | 
|  | }; | 
|  |  | 
|  | class PDFJpegImageBench : public Benchmark { | 
|  | public: | 
|  | PDFJpegImageBench() {} | 
|  | ~PDFJpegImageBench() override {} | 
|  |  | 
|  | protected: | 
|  | const char* onGetName() override { return "PDFJpegImage"; } | 
|  | bool isSuitableFor(Backend backend) override { | 
|  | return backend == Backend::kNonRendering; | 
|  | } | 
|  | void onDelayedSetup() override { | 
|  | sk_sp<SkImage> img(ToolUtils::GetResourceAsImage("images/mandrill_512_q075.jpg")); | 
|  | if (!img) { return; } | 
|  | sk_sp<SkData> encoded = img->refEncodedData(); | 
|  | SkASSERT(encoded); | 
|  | if (!encoded) { return; } | 
|  | fImage = img; | 
|  | } | 
|  | void onDraw(int loops, SkCanvas*) override { | 
|  | if (!fImage) { | 
|  | SkDEBUGFAIL(""); | 
|  | return; | 
|  | } | 
|  | while (loops-- > 0) { | 
|  | SkNullWStream nullStream; | 
|  | SkPDFDocument doc(&nullStream, SkPDF::Metadata()); | 
|  | doc.beginPage(256, 256); | 
|  | (void)SkPDFSerializeImage(fImage.get(), &doc); | 
|  | } | 
|  | } | 
|  |  | 
|  | private: | 
|  | sk_sp<SkImage> fImage; | 
|  | }; | 
|  |  | 
|  | /** Test calling DEFLATE on a 78k PDF command stream. Used for measuring | 
|  | alternate zlib settings, usage, and library versions. */ | 
|  | class PDFCompressionBench : public Benchmark { | 
|  | public: | 
|  | PDFCompressionBench() {} | 
|  | ~PDFCompressionBench() override {} | 
|  |  | 
|  | protected: | 
|  | const char* onGetName() override { return "PDFCompression"; } | 
|  | bool isSuitableFor(Backend backend) override { | 
|  | return backend == Backend::kNonRendering; | 
|  | } | 
|  | void onDelayedSetup() override { | 
|  | fAsset = GetResourceAsStream("pdf_command_stream.txt"); | 
|  | } | 
|  | void onDraw(int loops, SkCanvas*) override { | 
|  | SkASSERT(fAsset); | 
|  | if (!fAsset) { return; } | 
|  | while (loops-- > 0) { | 
|  | SkNullWStream wStream; | 
|  | SkPDFDocument doc(&wStream, SkPDF::Metadata()); | 
|  | doc.beginPage(256, 256); | 
|  | (void)SkPDFStreamOut(nullptr, fAsset->duplicate(), | 
|  | &doc, SkPDFSteamCompressionEnabled::Yes); | 
|  | } | 
|  | } | 
|  |  | 
|  | private: | 
|  | std::unique_ptr<SkStreamAsset> fAsset; | 
|  | }; | 
|  |  | 
|  | struct PDFColorComponentBench : public Benchmark { | 
|  | bool isSuitableFor(Backend b) override { | 
|  | return b == Backend::kNonRendering; | 
|  | } | 
|  | const char* onGetName() override { return "PDFColorComponent"; } | 
|  | void onDraw(int loops, SkCanvas*) override { | 
|  | char dst[5]; | 
|  | while (loops-- > 0) { | 
|  | for (int i = 0; i < 256; ++i) { | 
|  | (void)SkPDFUtils::ColorToDecimal(SkToU8(i), dst); | 
|  | } | 
|  | } | 
|  | } | 
|  | }; | 
|  |  | 
|  | struct PDFShaderBench : public Benchmark { | 
|  | sk_sp<SkShader> fShader; | 
|  | const char* onGetName() final { return "PDFShader"; } | 
|  | bool isSuitableFor(Backend b) final { return b == Backend::kNonRendering; } | 
|  | void onDelayedSetup() final { | 
|  | const SkPoint pts[2] = {{0.0f, 0.0f}, {100.0f, 100.0f}}; | 
|  | const SkColor colors[] = { | 
|  | SK_ColorRED, SK_ColorGREEN, SK_ColorBLUE, | 
|  | SK_ColorWHITE, SK_ColorBLACK, | 
|  | }; | 
|  | fShader = SkGradientShader::MakeLinear( | 
|  | pts, colors, nullptr, std::size(colors), | 
|  | SkTileMode::kClamp); | 
|  | } | 
|  | void onDraw(int loops, SkCanvas*) final { | 
|  | SkASSERT(fShader); | 
|  | while (loops-- > 0) { | 
|  | SkNullWStream nullStream; | 
|  | SkPDFDocument doc(&nullStream, SkPDF::Metadata()); | 
|  | doc.beginPage(256, 256); | 
|  | (void) SkPDFMakeShader(&doc, fShader.get(), SkMatrix::I(), | 
|  | {0, 0, 400, 400}, SkColors::kBlack); | 
|  | } | 
|  | } | 
|  | }; | 
|  |  | 
|  | struct WritePDFTextBenchmark : public Benchmark { | 
|  | std::unique_ptr<SkWStream> fWStream; | 
|  | WritePDFTextBenchmark() : fWStream(new SkNullWStream) {} | 
|  | const char* onGetName() override { return "WritePDFText"; } | 
|  | bool isSuitableFor(Backend backend) override { | 
|  | return backend == Backend::kNonRendering; | 
|  | } | 
|  | void onDraw(int loops, SkCanvas*) override { | 
|  | static const char kHello[] = "HELLO SKIA!\n"; | 
|  | static const char kBinary[] = "\001\002\003\004\005\006"; | 
|  | while (loops-- > 0) { | 
|  | for (int i = 1000; i-- > 0;) { | 
|  | SkPDFWriteTextString(fWStream.get(), kHello, strlen(kHello)); | 
|  | SkPDFWriteByteString(fWStream.get(), kBinary, strlen(kBinary)); | 
|  | } | 
|  | } | 
|  | } | 
|  | }; | 
|  |  | 
|  | // Test for regression chromium:947381 | 
|  | // with    5c83ae81aa :   2364.99 microsec | 
|  | // without 5c83ae81aa : 302821.78 microsec | 
|  | struct PDFClipPathBenchmark : public Benchmark { | 
|  | SkPath fPath; | 
|  | void onDelayedSetup() override { | 
|  | SkBitmap bitmap; | 
|  | bitmap.allocN32Pixels(256, 256); | 
|  | bitmap.eraseColor(SK_ColorWHITE); | 
|  | { | 
|  | SkCanvas tmp(bitmap); | 
|  | SkPaint paint; | 
|  | paint.setAntiAlias(false); | 
|  | paint.setStyle(SkPaint::kStroke_Style); | 
|  | paint.setStrokeWidth(10); | 
|  | for (int r : {20, 40, 60, 80, 100, 120}) { | 
|  | tmp.drawCircle(128, 128, (float)r, paint); | 
|  | } | 
|  | } | 
|  | fPath.reset(); | 
|  | for (int y = 0; y < 256; ++y) { | 
|  | SkColor current = bitmap.getColor(0, y); | 
|  | int start = 0; | 
|  | for (int x = 0; x < 256; ++x) { | 
|  | SkColor color = bitmap.getColor(x, y); | 
|  | if (color == current) { | 
|  | continue; | 
|  | } | 
|  | if (color == SK_ColorBLACK) { | 
|  | start = x; | 
|  | } else { | 
|  | fPath.addRect(SkRect::Make(SkIRect{start, y, x, y + 1})); | 
|  | } | 
|  | current = color; | 
|  | } | 
|  | if (current == SK_ColorBLACK) { | 
|  | fPath.addRect(SkRect::Make(SkIRect{start, y, 256, y + 1})); | 
|  | } | 
|  | } | 
|  | } | 
|  | const char* onGetName() override { return "PDFClipPath"; } | 
|  | bool isSuitableFor(Backend backend) override { | 
|  | return backend == Backend::kNonRendering; | 
|  | } | 
|  | void onDraw(int loops, SkCanvas*) override { | 
|  | while (loops-- > 0) { | 
|  | SkNullWStream wStream; | 
|  | SkPDFDocument doc(&wStream, SkPDF::Metadata()); | 
|  | SkCanvas* canvas = doc.beginPage(256, 256); | 
|  | canvas->clipPath(fPath); | 
|  | canvas->translate(4.0f/3, 4.0f/3); | 
|  | canvas->clipPath(fPath); | 
|  | canvas->clear(SK_ColorRED); | 
|  | doc.endPage(); | 
|  | } | 
|  | } | 
|  | }; | 
|  |  | 
|  | }  // namespace | 
|  | DEF_BENCH(return new PDFImageBench;) | 
|  | DEF_BENCH(return new PDFJpegImageBench;) | 
|  | DEF_BENCH(return new PDFCompressionBench;) | 
|  | DEF_BENCH(return new PDFColorComponentBench;) | 
|  | DEF_BENCH(return new PDFShaderBench;) | 
|  | DEF_BENCH(return new WritePDFTextBenchmark;) | 
|  | DEF_BENCH(return new PDFClipPathBenchmark;) | 
|  |  | 
|  | #ifdef SK_PDF_ENABLE_SLOW_TESTS | 
|  | #include "include/core/SkExecutor.h" | 
|  | namespace { | 
|  | void big_pdf_test(SkDocument* doc, const SkBitmap& background) { | 
|  | static const char* kText[] = { | 
|  | "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do", | 
|  | "eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad", | 
|  | "minim veniam, quis nostrud exercitation ullamco laboris nisi ut", | 
|  | "aliquip ex ea commodo consequat. Duis aute irure dolor in", | 
|  | "reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla", | 
|  | "pariatur. Excepteur sint occaecat cupidatat non proident, sunt in", | 
|  | "culpa qui officia deserunt mollit anim id est laborum.", | 
|  | "", | 
|  | "Sed ut perspiciatis, unde omnis iste natus error sit voluptatem", | 
|  | "accusantium doloremque laudantium, totam rem aperiam eaque ipsa, quae", | 
|  | "ab illo inventore veritatis et quasi architecto beatae vitae dicta", | 
|  | "sunt, explicabo. Nemo enim ipsam voluptatem, quia voluptas sit,", | 
|  | "aspernatur aut odit aut fugit, sed quia consequuntur magni dolores", | 
|  | "eos, qui ratione voluptatem sequi nesciunt, neque porro quisquam est,", | 
|  | "qui dolorem ipsum, quia dolor sit amet consectetur adipiscing velit,", | 
|  | "sed quia non numquam do eius modi tempora incididunt, ut labore et", | 
|  | "dolore magnam aliquam quaerat voluptatem. Ut enim ad minima veniam,", | 
|  | "quis nostrum exercitationem ullam corporis suscipit laboriosam, nisi", | 
|  | "ut aliquid ex ea commodi consequatur? Quis autem vel eum iure", | 
|  | "reprehenderit, qui in ea voluptate velit esse, quam nihil molestiae", | 
|  | "consequatur, vel illum, qui dolorem eum fugiat, quo voluptas nulla", | 
|  | "pariatur?", | 
|  | "", | 
|  | "At vero eos et accusamus et iusto odio dignissimos ducimus, qui", | 
|  | "blanditiis praesentium voluptatum deleniti atque corrupti, quos", | 
|  | "dolores et quas molestias excepturi sint, obcaecati cupiditate non", | 
|  | "provident, similique sunt in culpa, qui officia deserunt mollitia", | 
|  | "animi, id est laborum et dolorum fuga. Et harum quidem rerum facilis", | 
|  | "est et expedita distinctio. Nam libero tempore, cum soluta nobis est", | 
|  | "eligendi optio, cumque nihil impedit, quo minus id, quod maxime", | 
|  | "placeat, facere possimus, omnis voluptas assumenda est, omnis dolor", | 
|  | "repellendus. Temporibus autem quibusdam et aut officiis debitis aut", | 
|  | "rerum necessitatibus saepe eveniet, ut et voluptates repudiandae sint", | 
|  | "et molestiae non recusandae. Itaque earum rerum hic tenetur a sapiente", | 
|  | "delectus, ut aut reiciendis voluptatibus maiores alias consequatur aut", | 
|  | "perferendis doloribus asperiores repellat", | 
|  | "", | 
|  | "Sed ut perspiciatis, unde omnis iste natus error sit voluptatem", | 
|  | "accusantium doloremque laudantium, totam rem aperiam eaque ipsa, quae", | 
|  | "ab illo inventore veritatis et quasi architecto beatae vitae dicta", | 
|  | "sunt, explicabo. Nemo enim ipsam voluptatem, quia voluptas sit,", | 
|  | "aspernatur aut odit aut fugit, sed quia consequuntur magni dolores", | 
|  | "eos, qui ratione voluptatem sequi nesciunt, neque porro quisquam est,", | 
|  | "qui dolorem ipsum, quia dolor sit amet consectetur adipiscing velit,", | 
|  | "sed quia non numquam do eius modi tempora incididunt, ut labore et", | 
|  | "dolore magnam aliquam quaerat voluptatem. Ut enim ad minima veniam,", | 
|  | "quis nostrum exercitationem ullam corporis suscipit laboriosam, nisi", | 
|  | "ut aliquid ex ea commodi consequatur? Quis autem vel eum iure", | 
|  | "reprehenderit, qui in ea voluptate velit esse, quam nihil molestiae", | 
|  | "consequatur, vel illum, qui dolorem eum fugiat, quo voluptas nulla", | 
|  | "pariatur?", | 
|  | "", | 
|  | }; | 
|  | SkCanvas* canvas = nullptr; | 
|  | float x = 36; | 
|  | float y = 36; | 
|  | constexpr size_t kLineCount = std::size(kText); | 
|  | constexpr int kLoopCount = 200; | 
|  | SkFont font = ToolUtils::DefaultFont(); | 
|  | SkPaint paint; | 
|  | for (int loop = 0; loop < kLoopCount; ++loop) { | 
|  | for (size_t line = 0; line < kLineCount; ++line) { | 
|  | y += font.getSpacing(); | 
|  | if (!canvas || y > 792 - 36) { | 
|  | y = 36 + font.getSpacing(); | 
|  | canvas = doc->beginPage(612, 792); | 
|  | background.notifyPixelsChanged(); | 
|  | canvas->drawBitmap(background, 0, 0); | 
|  | } | 
|  | canvas->drawString(kText[line], x, y, font, paint); | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | SkBitmap make_background() { | 
|  | SkBitmap background; | 
|  | SkBitmap bitmap; | 
|  | bitmap.allocN32Pixels(32, 32); | 
|  | bitmap.eraseColor(SK_ColorWHITE); | 
|  | SkCanvas tmp(bitmap); | 
|  | SkPaint gray; | 
|  | gray.setColor(SkColorSetARGB(0xFF, 0xEE, 0xEE, 0xEE)); | 
|  | tmp.drawRect({0,0,16,16}, gray); | 
|  | tmp.drawRect({16,16,32,32}, gray); | 
|  | SkPaint shader; | 
|  | shader.setShader( | 
|  | SkShader::MakeBitmapShader( | 
|  | bitmap, SkShader::kRepeat_TileMode, SkShader::kRepeat_TileMode)); | 
|  | background.allocN32Pixels(612, 792); | 
|  | SkCanvas tmp2(background); | 
|  | tmp2.drawPaint(shader); | 
|  | return background; | 
|  | } | 
|  |  | 
|  | struct PDFBigDocBench : public Benchmark { | 
|  | bool fFast; | 
|  | SkBitmap fBackground; | 
|  | std::unique_ptr<SkExecutor> fExecutor; | 
|  | PDFBigDocBench(bool fast) : fFast(fast) {} | 
|  | void onDelayedSetup() override { | 
|  | fBackground = make_background(); | 
|  | fExecutor = fFast ? SkExecutor::MakeFIFOThreadPool() : nullptr; | 
|  | } | 
|  | const char* onGetName() override { | 
|  | static const char kNameFast[] = "PDFBigDocBench_fast"; | 
|  | static const char kNameSlow[] = "PDFBigDocBench_slow"; | 
|  | return fFast ? kNameFast : kNameSlow; | 
|  | } | 
|  | bool isSuitableFor(Backend backend) override { return backend == Backend::kNonRendering; } | 
|  | void onDraw(int loops, SkCanvas*) override { | 
|  | while (loops-- > 0) { | 
|  | #ifdef SK_PDF_TEST_BIGDOCBENCH_OUTPUT | 
|  | SkFILEWStream wStream("/tmp/big_pdf.pdf"); | 
|  | #else | 
|  | SkNullWStream wStream; | 
|  | #endif | 
|  | SkPDF::Metadata metadata; | 
|  | metadata.fExecutor = fExecutor.get(); | 
|  | auto doc = SkPDF::MakeDocument(&wStream, metadata); | 
|  | big_pdf_test(doc.get(), fBackground); | 
|  | } | 
|  | } | 
|  | }; | 
|  | }  // namespace | 
|  | DEF_BENCH(return new PDFBigDocBench(false);) | 
|  | DEF_BENCH(return new PDFBigDocBench(true);) | 
|  | #endif | 
|  |  | 
|  | #endif // SK_SUPPORT_PDF |