| /* | |
| * 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 "Benchmark.h" | |
| #include "Resources.h" | |
| #include "SkAutoPixmapStorage.h" | |
| #include "SkData.h" | |
| #include "SkExecutor.h" | |
| #include "SkFloatToDecimal.h" | |
| #include "SkGradientShader.h" | |
| #include "SkImage.h" | |
| #include "SkPDFUnion.h" | |
| #include "SkPixmap.h" | |
| #include "SkRandom.h" | |
| #include "SkStream.h" | |
| #include "SkTo.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 == kNonRendering_Backend; | |
| } | |
| 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 == kNonRendering_Backend; | |
| } | |
| 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 "SkPDFBitmap.h" | |
| #include "SkPDFDocumentPriv.h" | |
| #include "SkPDFShader.h" | |
| #include "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 == kNonRendering_Backend; | |
| } | |
| void onDelayedSetup() override { | |
| sk_sp<SkImage> img(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(pixmap, 0, 0)) { | |
| fImage = SkImage::MakeRasterCopy(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 == kNonRendering_Backend; | |
| } | |
| void onDelayedSetup() override { | |
| sk_sp<SkImage> img(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 == kNonRendering_Backend; | |
| } | |
| 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, true); | |
| } | |
| } | |
| private: | |
| std::unique_ptr<SkStreamAsset> fAsset; | |
| }; | |
| struct PDFColorComponentBench : public Benchmark { | |
| bool isSuitableFor(Backend b) override { | |
| return b == kNonRendering_Backend; | |
| } | |
| 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 == kNonRendering_Backend; } | |
| 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, SK_ARRAY_COUNT(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}, SK_ColorBLACK); | |
| } | |
| } | |
| }; | |
| 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 == kNonRendering_Backend; | |
| } | |
| 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;) { | |
| SkPDFWriteString(fWStream.get(), kHello, strlen(kHello)); | |
| SkPDFWriteString(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 == kNonRendering_Backend; | |
| } | |
| 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 "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 = SK_ARRAY_COUNT(kText); | |
| constexpr int kLoopCount = 200; | |
| SkFont font; | |
| 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 == kNonRendering_Backend; } | |
| 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 |