From 45420a945cfce1b83da954ab5fcb63516f646c0f Mon Sep 17 00:00:00 2001 From: halcanary Date: Thu, 2 Jun 2016 12:41:14 -0700 Subject: [PATCH] SkMultiPictureDocument & SkMultiPictureDocumentReader also, a new DM::Src. motivation: To be used to test the printing pipeline in Chromium. BUG=skia:5370 GOLD_TRYBOT_URL= https://gold.skia.org/search?issue=2023593002 Review-Url: https://codereview.chromium.org/2023593002 --- dm/DM.cpp | 15 ++++ dm/DMSrcSink.cpp | 72 +++++++++++--------- dm/DMSrcSink.h | 24 +++++++ gyp/dm.gypi | 3 +- gyp/utils.gypi | 1 + src/utils/SkMultiPictureDocument.cpp | 106 +++++++++++++++++++++++++++++ src/utils/SkMultiPictureDocument.h | 16 +++++ src/utils/SkMultiPictureDocumentPriv.h | 23 +++++++ src/utils/SkMultiPictureDocumentReader.cpp | 49 +++++++++++++ src/utils/SkMultiPictureDocumentReader.h | 45 ++++++++++++ 10 files changed, 320 insertions(+), 34 deletions(-) create mode 100644 src/utils/SkMultiPictureDocument.cpp create mode 100644 src/utils/SkMultiPictureDocument.h create mode 100644 src/utils/SkMultiPictureDocumentPriv.h create mode 100644 src/utils/SkMultiPictureDocumentReader.cpp create mode 100644 src/utils/SkMultiPictureDocumentReader.h diff --git a/dm/DM.cpp b/dm/DM.cpp index 56c3b66..c4e7d15 100644 --- a/dm/DM.cpp +++ b/dm/DM.cpp @@ -72,6 +72,8 @@ DEFINE_int32(shards, 1, "We're splitting source data into this many shards."); DEFINE_int32(shard, 0, "Which shard do I run?"); DEFINE_bool(simpleCodec, false, "Only decode images to native scale"); +DEFINE_string(mskps, "", "Directory to read mskps from, or a single mskp file."); + using namespace DM; using sk_gpu_test::GrContextFactory; using sk_gpu_test::GLTestContext; @@ -702,6 +704,19 @@ static bool gather_srcs() { } } + for (int i = 0; i < FLAGS_mskps.count(); i++) { + const char* path = FLAGS_mskps[i]; + if (sk_isdir(path)) { + SkOSFile::Iter it(path, "mskp"); + for (SkString file; it.next(&file);) { + push_src("mskp", "", + new MSKPSrc(SkOSPath::Join(path, file.c_str()))); + } + } else { + push_src("mskp", "", new MSKPSrc(path)); + } + } + SkTArray images; if (!CollectImages(FLAGS_images, &images)) { return false; diff --git a/dm/DMSrcSink.cpp b/dm/DMSrcSink.cpp index 3564160..a1be097 100644 --- a/dm/DMSrcSink.cpp +++ b/dm/DMSrcSink.cpp @@ -1007,6 +1007,41 @@ Name SKPSrc::name() const { return SkOSPath::Basename(fPath.c_str()); } /*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/ +MSKPSrc::MSKPSrc(Path path) : fPath(path) { + std::unique_ptr stream(SkStream::NewFromFile(fPath.c_str())); + (void)fReader.init(stream.get()); +} + +int MSKPSrc::pageCount() const { return fReader.pageCount(); } + +SkISize MSKPSrc::size() const { return this->size(0); } +SkISize MSKPSrc::size(int i) const { return fReader.pageSize(i).toCeil(); } + +Error MSKPSrc::draw(SkCanvas* c) const { return this->draw(0, c); } +Error MSKPSrc::draw(int i, SkCanvas* canvas) const { + std::unique_ptr stream(SkStream::NewFromFile(fPath.c_str())); + if (!stream) { + return SkStringPrintf("Unable to open file: %s", fPath.c_str()); + } + if (fReader.pageCount() == 0) { + return SkStringPrintf("Unable to parse MultiPictureDocument file: %s", fPath.c_str()); + } + if (i >= fReader.pageCount()) { + return SkStringPrintf("MultiPictureDocument page number out of range: %d", i); + } + sk_sp page = fReader.readPage(stream.get(), i); + if (!page) { + return SkStringPrintf("SkMultiPictureDocumentReader failed on page %d: %s", + i, fPath.c_str()); + } + canvas->drawPicture(page); + return ""; +} + +Name MSKPSrc::name() const { return SkOSPath::Basename(fPath.c_str()); } + +/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/ + Error NullSink::draw(const Src& src, SkBitmap*, SkWStream*, SkString*) const { SkAutoTDelete canvas(SkCreateNullCanvas()); return src.draw(canvas); @@ -1099,44 +1134,15 @@ static Error draw_skdocument(const Src& src, SkDocument* doc, SkWStream* dst) { return "Source has empty dimensions"; } SkASSERT(doc); - int width = src.size().width(), - height = src.size().height(); - - if (FLAGS_multiPage) { - // Print the given DM:Src to a document, breaking on 8.5x11 pages. - const int kLetterWidth = 612, // 8.5 * 72 - kLetterHeight = 792; // 11 * 72 - const SkRect letter = SkRect::MakeWH(SkIntToScalar(kLetterWidth), - SkIntToScalar(kLetterHeight)); - - int xPages = ((width - 1) / kLetterWidth) + 1; - int yPages = ((height - 1) / kLetterHeight) + 1; - - for (int y = 0; y < yPages; ++y) { - for (int x = 0; x < xPages; ++x) { - int w = SkTMin(kLetterWidth, width - (x * kLetterWidth)); - int h = SkTMin(kLetterHeight, height - (y * kLetterHeight)); - SkCanvas* canvas = - doc->beginPage(SkIntToScalar(w), SkIntToScalar(h)); - if (!canvas) { - return "SkDocument::beginPage(w,h) returned nullptr"; - } - canvas->clipRect(letter); - canvas->translate(-letter.width() * x, -letter.height() * y); - Error err = src.draw(canvas); - if (!err.isEmpty()) { - return err; - } - doc->endPage(); - } - } - } else { + int pageCount = src.pageCount(); + for (int i = 0; i < pageCount; ++i) { + int width = src.size(i).width(), height = src.size(i).height(); SkCanvas* canvas = doc->beginPage(SkIntToScalar(width), SkIntToScalar(height)); if (!canvas) { return "SkDocument::beginPage(w,h) returned nullptr"; } - Error err = src.draw(canvas); + Error err = src.draw(i, canvas); if (!err.isEmpty()) { return err; } diff --git a/dm/DMSrcSink.h b/dm/DMSrcSink.h index 42f0e6d..395f59e 100644 --- a/dm/DMSrcSink.h +++ b/dm/DMSrcSink.h @@ -15,6 +15,7 @@ #include "SkBitmapRegionDecoder.h" #include "SkCanvas.h" #include "SkData.h" +#include "SkMultiPictureDocumentReader.h" #include "SkPicture.h" #include "gm.h" @@ -66,6 +67,11 @@ struct Src { virtual void modifyGrContextOptions(GrContextOptions* options) const {} virtual bool veto(SinkFlags) const { return false; } + virtual int pageCount() const { return 1; } + virtual Error SK_WARN_UNUSED_RESULT draw(int, SkCanvas* canvas) const { + return this->draw(canvas); + } + virtual SkISize size(int) const { return this->size(); } // Force Tasks using this Src to run on the main thread? virtual bool serial() const { return false; } }; @@ -243,6 +249,24 @@ private: /*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/ +class MSKPSrc : public Src { +public: + explicit MSKPSrc(Path path); + + int pageCount() const override; + Error draw(SkCanvas* c) const override; + Error draw(int, SkCanvas*) const override; + SkISize size() const override; + SkISize size(int) const override; + Name name() const override; + +private: + Path fPath; + SkMultiPictureDocumentReader fReader; +}; + +/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/ + class NullSink : public Sink { public: NullSink() {} diff --git a/gyp/dm.gypi b/gyp/dm.gypi index 4a4f8ba..d307c9c 100644 --- a/gyp/dm.gypi +++ b/gyp/dm.gypi @@ -24,7 +24,7 @@ 'skia_lib.gyp:skia_lib', 'svg.gyp:svg', 'tools.gyp:crash_handler', - 'tools.gyp:picture_utils', + 'tools.gyp:picture_utils', 'tools.gyp:proc_stats', 'tools.gyp:sk_tool_utils', 'tools.gyp:url_data_manager', @@ -43,6 +43,7 @@ '../dm/DMJsonWriter.cpp', '../gm/gm.cpp', + '../src/utils/SkMultiPictureDocumentReader.cpp', '../tools/debugger/SkDebugCanvas.cpp', '../tools/debugger/SkDrawCommand.cpp', '../tools/debugger/SkJsonWriteBuffer.cpp', diff --git a/gyp/utils.gypi b/gyp/utils.gypi index ea12203..fb8805e 100644 --- a/gyp/utils.gypi +++ b/gyp/utils.gypi @@ -54,6 +54,7 @@ '<(skia_src_path)/utils/SkMatrix22.cpp', '<(skia_src_path)/utils/SkMatrix22.h', '<(skia_src_path)/utils/SkMeshUtils.cpp', + '<(skia_src_path)/utils/SkMultiPictureDocument.cpp', '<(skia_src_path)/utils/SkNinePatch.cpp', '<(skia_src_path)/utils/SkNWayCanvas.cpp', '<(skia_src_path)/utils/SkNullCanvas.cpp', diff --git a/src/utils/SkMultiPictureDocument.cpp b/src/utils/SkMultiPictureDocument.cpp new file mode 100644 index 0000000..1cbf0ae --- /dev/null +++ b/src/utils/SkMultiPictureDocument.cpp @@ -0,0 +1,106 @@ +/* + * 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 "SkMultiPictureDocument.h" +#include "SkMultiPictureDocumentPriv.h" +#include "SkPicture.h" +#include "SkPictureRecorder.h" +#include "SkStream.h" + +/* + File format: + BEGINNING_OF_FILE: + kMagic + uint32_t version_number + uint32_t page_count + { + uint64_t offset + float sizeX + float sizeY + } * page_count + FIRST_OFFSET: + skp file + SECOND_OFFSET: + skp file + ... + LAST_OFFSET: + skp file + "\nEndOfMultiPicture\n" +*/ + +namespace { +static SkCanvas* trim(SkCanvas* canvas, + SkScalar w, SkScalar h, + const SkRect& trimBox) { + // Only trim if necessary. + if (trimBox != SkRect::MakeWH(w, h)) { + // All SkDocument implementations implement trimBox using a + // clip+translate. + canvas->clipRect(trimBox); + canvas->translate(trimBox.x(), trimBox.y()); + } + return canvas; +} + +struct NullWStream : public SkWStream { + NullWStream() : fN(0) {} + bool write(const void*, size_t n) override { + fN += n; + return true; + } + size_t bytesWritten() const override { return fN; } + size_t fN; +}; + +struct MultiPictureDocument final : public SkDocument { + SkPictureRecorder fPictureRecorder; + SkTArray> fPages; + MultiPictureDocument(SkWStream* s, void (*d)(SkWStream*, bool)) + : SkDocument(s, d) {} + ~MultiPictureDocument() { this->close(); } + + SkCanvas* onBeginPage(SkScalar w, SkScalar h, const SkRect& c) override { + return trim(fPictureRecorder.beginRecording(w, h), w, h, c); + } + void onEndPage() override { + fPages.emplace_back(fPictureRecorder.finishRecordingAsPicture()); + } + bool onClose(SkWStream* wStream) override { + SkASSERT(wStream); + SkASSERT(wStream->bytesWritten() == 0); + bool good = true; + good &= wStream->writeText(SkMultiPictureDocumentProtocol::kMagic); + good &= wStream->write32(SkToU32(1)); // version + good &= wStream->write32(SkToU32(fPages.count())); + uint64_t offset = wStream->bytesWritten(); + offset += fPages.count() * sizeof(SkMultiPictureDocumentProtocol::Entry); + for (const auto& page : fPages) { + SkRect cullRect = page->cullRect(); + // We recorded a picture at the origin. + SkASSERT(cullRect.x() == 0 && cullRect.y() == 0); + SkMultiPictureDocumentProtocol::Entry entry{ + offset, (float)cullRect.right(), (float)cullRect.bottom()}; + good &= wStream->write(&entry, sizeof(entry)); + NullWStream buffer; + page->serialize(&buffer); + offset += buffer.bytesWritten(); + } + for (const auto& page : fPages) { + page->serialize(wStream); + } + SkASSERT(wStream->bytesWritten() == offset); + good &= wStream->writeText("\nEndOfMultiPicture\n"); + fPages.reset(); + return good; + } + void onAbort() override { fPages.reset(); } +}; +} + +sk_sp SkMakeMultiPictureDocument(SkWStream* wStream) { + return sk_make_sp(wStream, nullptr); +} diff --git a/src/utils/SkMultiPictureDocument.h b/src/utils/SkMultiPictureDocument.h new file mode 100644 index 0000000..1da105e --- /dev/null +++ b/src/utils/SkMultiPictureDocument.h @@ -0,0 +1,16 @@ +/* + * Copyright 2016 Google Inc. + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ +#ifndef SkMultiPictureDocument_DEFINED +#define SkMultiPictureDocument_DEFINED + +#include "SkDocument.h" + +/** Writes into an experimental, undocumented file format that is + useful for debugging documents printed via Skia. */ +SK_API sk_sp SkMakeMultiPictureDocument(SkWStream* dst); + +#endif // SkMultiPictureDocument_DEFINED diff --git a/src/utils/SkMultiPictureDocumentPriv.h b/src/utils/SkMultiPictureDocumentPriv.h new file mode 100644 index 0000000..124dad7 --- /dev/null +++ b/src/utils/SkMultiPictureDocumentPriv.h @@ -0,0 +1,23 @@ +/* + * Copyright 2016 Google Inc. + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#ifndef SkMultiPictureDocumentPriv_DEFINED +#define SkMultiPictureDocumentPriv_DEFINED + +#include "stdint.h" + +namespace SkMultiPictureDocumentProtocol { +static constexpr char kMagic[] = "Skia Multi-Picture Doc\n\n"; + +struct Entry { + uint64_t offset; + float sizeX; + float sizeY; +}; +} + +#endif // SkMultiPictureDocumentPriv_DEFINED diff --git a/src/utils/SkMultiPictureDocumentReader.cpp b/src/utils/SkMultiPictureDocumentReader.cpp new file mode 100644 index 0000000..6bc77bf --- /dev/null +++ b/src/utils/SkMultiPictureDocumentReader.cpp @@ -0,0 +1,49 @@ +/* + * 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 "SkMultiPictureDocumentPriv.h" +#include "SkMultiPictureDocumentReader.h" +#include "SkPicture.h" +#include "SkStream.h" + +bool SkMultiPictureDocumentReader::init(SkStreamSeekable* stream) { + if (!stream) { + return false; + } + stream->seek(0); + const size_t size = sizeof(SkMultiPictureDocumentProtocol::kMagic) - 1; + char buffer[size]; + if (size != stream->read(buffer, size) || + 0 != memcmp(SkMultiPictureDocumentProtocol::kMagic, buffer, size)) { + stream = nullptr; + return false; + } + bool good = true; + uint32_t versionNumber = stream->readU32(); + if (versionNumber != 1) { + return false; + } + uint32_t pageCount = stream->readU32(); + fSizes.reset(pageCount); + fOffsets.reset(pageCount); + for (uint32_t i = 0; i < pageCount; ++i) { + SkMultiPictureDocumentProtocol::Entry entry; + good &= sizeof(entry) == stream->read(&entry, sizeof(entry)); + fSizes[i] = SkSize::Make(entry.sizeX, entry.sizeY); + good &= SkTFitsIn(entry.offset); + fOffsets[i] = static_cast(entry.offset); + } + return good; +} + +sk_sp SkMultiPictureDocumentReader::readPage(SkStreamSeekable* stream, + int pageNumber) const { + SkASSERT(pageNumber >= 0); + SkASSERT(pageNumber < fOffsets.count()); + SkAssertResult(stream->seek(fOffsets[pageNumber])); + return SkPicture::MakeFromStream(stream); +} diff --git a/src/utils/SkMultiPictureDocumentReader.h b/src/utils/SkMultiPictureDocumentReader.h new file mode 100644 index 0000000..8e0a630 --- /dev/null +++ b/src/utils/SkMultiPictureDocumentReader.h @@ -0,0 +1,45 @@ +/* + * Copyright 2016 Google Inc. + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ +#ifndef SkMultiPictureDocumentReader_DEFINED +#define SkMultiPictureDocumentReader_DEFINED + +#include "../private/SkTArray.h" +#include "SkPicture.h" +#include "SkSize.h" +#include "SkStream.h" + +/** A lightweight helper class for reading a Skia MultiPictureDocument. */ +class SkMultiPictureDocumentReader { +public: + /** Initialize the MultiPictureDocument. Does not take ownership + of the SkStreamSeekable. */ + bool init(SkStreamSeekable*); + + /** Return to factory settings. */ + void reset() { + fSizes.reset(); + fOffsets.reset(); + } + + /** Call this after calling init() */ + int pageCount() const { return fSizes.count(); } + + /** Deserialize a page from the stream. Call init() first. The + SkStreamSeekable doesn't need to be the same object, but + should point to the same information as before. */ + sk_sp readPage(SkStreamSeekable*, int) const; + + /** Fetch the size of the given page, without deserializing the + entire page. */ + SkSize pageSize(int i) const { return fSizes[i]; } + +private: + SkTArray fSizes; + SkTArray fOffsets; +}; + +#endif // SkMultiPictureDocumentReader_DEFINED -- 2.7.4