diff options
author | 2014-06-03 13:57:14 -0700 | |
---|---|---|
committer | 2014-06-03 13:57:14 -0700 | |
commit | 30bf3e2ffcb78fc76e5a62b2ca67638e0411cba9 (patch) | |
tree | 1a2f9f974e462b621439d714f80e53a9986dbd3b /dm | |
parent | 9c4ff80d9b76e1bda532fb9182f66f67cfc95324 (diff) |
DM: add pdf
BUG=skia:2598
R=halcanary@google.com, mtklein@google.com
Author: mtklein@chromium.org
Review URL: https://codereview.chromium.org/312873002
Diffstat (limited to 'dm')
-rw-r--r-- | dm/DM.cpp | 37 | ||||
-rw-r--r-- | dm/DMPDFRasterizeTask.cpp | 41 | ||||
-rw-r--r-- | dm/DMPDFRasterizeTask.h | 43 | ||||
-rw-r--r-- | dm/DMPDFTask.cpp | 83 | ||||
-rw-r--r-- | dm/DMPDFTask.h | 40 | ||||
-rw-r--r-- | dm/DMWriteTask.cpp | 38 | ||||
-rw-r--r-- | dm/DMWriteTask.h | 12 | ||||
-rw-r--r-- | dm/README | 11 |
8 files changed, 273 insertions, 32 deletions
@@ -14,6 +14,7 @@ #include "DMCpuGMTask.h" #include "DMGpuGMTask.h" #include "DMGpuSupport.h" +#include "DMPDFTask.h" #include "DMReporter.h" #include "DMSKPTask.h" #include "DMTask.h" @@ -21,6 +22,13 @@ #include "DMTestTask.h" #include "DMWriteTask.h" +#ifdef SK_BUILD_POPPLER +# include "SkPDFRasterizer.h" +# define RASTERIZE_PDF_PROC SkPopplerRasterizePDF +#else +# define RASTERIZE_PDF_PROC NULL +#endif + #include <ctype.h> using skiagm::GM; @@ -49,9 +57,11 @@ DEFINE_string(match, "", "[~][^]substring[$] [...] of GM name to run.\n" "^ and $ requires an exact match\n" "If a GM does not match any list entry,\n" "it is skipped unless some list entry starts with ~"); -DEFINE_string(config, "565 8888 gpu nonrendering", - "Options: 565 8888 gpu nonrendering msaa4 msaa16 nvprmsaa4 nvprmsaa16 gpunull gpudebug angle mesa"); -DEFINE_bool(dryRun, false, "Just print the tests that would be run, without actually running them."); +DEFINE_string(config, "565 8888 pdf gpu nonrendering", + "Options: 565 8888 pdf gpu nonrendering msaa4 msaa16 nvprmsaa4 nvprmsaa16 " + "gpunull gpudebug angle mesa"); +DEFINE_bool(dryRun, false, + "Just print the tests that would be run, without actually running them."); DEFINE_bool(leaks, false, "Print leaked instance-counted objects at exit?"); DEFINE_string(skps, "", "Directory to read skps from."); @@ -100,17 +110,18 @@ static void kick_off_gms(const SkTDArray<GMRegistry::Factory>& gms, } for (int i = 0; i < gms.count(); i++) { for (int j = 0; j < configs.count(); j++) { - START("565", CpuGMTask, kRGB_565_SkColorType); - START("8888", CpuGMTask, kN32_SkColorType); - START("gpu", GpuGMTask, native, 0); - START("msaa4", GpuGMTask, native, 4); - START("msaa16", GpuGMTask, native, 16); - START("nvprmsaa4", GpuGMTask, nvpr, 4); + START("565", CpuGMTask, kRGB_565_SkColorType); + START("8888", CpuGMTask, kN32_SkColorType); + START("gpu", GpuGMTask, native, 0); + START("msaa4", GpuGMTask, native, 4); + START("msaa16", GpuGMTask, native, 16); + START("nvprmsaa4", GpuGMTask, nvpr, 4); START("nvprmsaa16", GpuGMTask, nvpr, 16); - START("gpunull", GpuGMTask, null, 0); - START("gpudebug", GpuGMTask, debug, 0); - START("angle", GpuGMTask, angle, 0); - START("mesa", GpuGMTask, mesa, 0); + START("gpunull", GpuGMTask, null, 0); + START("gpudebug", GpuGMTask, debug, 0); + START("angle", GpuGMTask, angle, 0); + START("mesa", GpuGMTask, mesa, 0); + START("pdf", PDFTask, RASTERIZE_PDF_PROC); } } #undef START diff --git a/dm/DMPDFRasterizeTask.cpp b/dm/DMPDFRasterizeTask.cpp new file mode 100644 index 0000000000..ce6c10982f --- /dev/null +++ b/dm/DMPDFRasterizeTask.cpp @@ -0,0 +1,41 @@ +/* + * Copyright 2014 Google Inc. + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#include "DMPDFRasterizeTask.h" +#include "DMExpectationsTask.h" +#include "DMUtil.h" +#include "DMWriteTask.h" +#include "SkBitmap.h" +#include "SkCanvas.h" +#include "SkStream.h" + +namespace DM { + +PDFRasterizeTask::PDFRasterizeTask(const Task& parent, + SkData* pdf, + const Expectations& expectations, + RasterizePdfProc proc) + : CpuTask(parent) + , fName(UnderJoin(parent.name().c_str(), "rasterize")) + , fPdf(SkRef(pdf)) + , fExpectations(expectations) + , fRasterize(proc) {} + +void PDFRasterizeTask::draw() { + SkMemoryStream pdfStream(fPdf.get()); + SkBitmap bitmap; + + if (!fRasterize(&pdfStream, &bitmap)) { + this->fail(); + } + if (!fExpectations.check(*this, bitmap)) { + this->fail(); + this->spawnChild(SkNEW_ARGS(WriteTask, (*this, bitmap))); + } +} + +} // namespace DM diff --git a/dm/DMPDFRasterizeTask.h b/dm/DMPDFRasterizeTask.h new file mode 100644 index 0000000000..2e24b894e7 --- /dev/null +++ b/dm/DMPDFRasterizeTask.h @@ -0,0 +1,43 @@ +/* + * Copyright 2014 Google Inc. + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#ifndef DMPDFRasterizeTask_DEFINED +#define DMPDFRasterizeTask_DEFINED + +#include "DMExpectations.h" +#include "DMTask.h" +#include "SkBitmap.h" +#include "SkData.h" +#include "SkStream.h" +#include "SkString.h" +#include "SkTemplates.h" + +namespace DM { + +typedef bool (*RasterizePdfProc)(SkStream* pdf, SkBitmap* output); + +class PDFRasterizeTask : public CpuTask { +public: + PDFRasterizeTask(const Task& parent, + SkData* pdf, + const Expectations&, + RasterizePdfProc); + + virtual void draw() SK_OVERRIDE; + virtual bool shouldSkip() const SK_OVERRIDE { return NULL == fRasterize; } + virtual SkString name() const SK_OVERRIDE { return fName; } + +private: + const SkString fName; + SkAutoTUnref<SkData> fPdf; + const Expectations& fExpectations; + RasterizePdfProc fRasterize; +}; + +} // namespace DM + +#endif // DMPDFRasterizeTask_DEFINED diff --git a/dm/DMPDFTask.cpp b/dm/DMPDFTask.cpp new file mode 100644 index 0000000000..270f4b4fef --- /dev/null +++ b/dm/DMPDFTask.cpp @@ -0,0 +1,83 @@ +/* + * Copyright 2014 Google Inc. + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#include "DMPDFTask.h" +#include "DMExpectationsTask.h" +#include "DMPDFRasterizeTask.h" +#include "DMUtil.h" +#include "DMWriteTask.h" +#include "SkCommandLineFlags.h" +#include "SkDocument.h" + +DEFINE_bool(pdf, true, "PDF backend master switch."); + +namespace DM { + +PDFTask::PDFTask(const char* suffix, + Reporter* reporter, + TaskRunner* taskRunner, + const Expectations& expectations, + skiagm::GMRegistry::Factory factory, + RasterizePdfProc rasterizePdfProc) + : CpuTask(reporter, taskRunner) + , fGM(factory(NULL)) + , fName(UnderJoin(fGM->getName(), suffix)) + , fExpectations(expectations) + , fRasterize(rasterizePdfProc) {} + +namespace { + +class SinglePagePDF { +public: + SinglePagePDF(SkScalar width, SkScalar height) + : fDocument(SkDocument::CreatePDF(&fWriteStream)) + , fCanvas(fDocument->beginPage(width, height)) {} + + SkCanvas* canvas() { return fCanvas; } + + SkData* end() { + fCanvas->flush(); + fDocument->endPage(); + fDocument->close(); + return fWriteStream.copyToData(); + } + +private: + SkDynamicMemoryWStream fWriteStream; + SkAutoTUnref<SkDocument> fDocument; + SkCanvas* fCanvas; +}; + +} // namespace + +void PDFTask::draw() { + SinglePagePDF pdf(fGM->width(), fGM->height()); + //TODO(mtklein): GM doesn't do this. Why not? + //pdf.canvas()->concat(fGM->getInitialTransform()); + fGM->draw(pdf.canvas()); + + SkAutoTUnref<SkData> pdfData(pdf.end()); + SkASSERT(pdfData.get()); + + if (!(fGM->getFlags() & skiagm::GM::kSkipPDFRasterization_Flag)) { + this->spawnChild(SkNEW_ARGS(PDFRasterizeTask, + (*this, pdfData.get(), fExpectations, fRasterize))); + } + this->spawnChild(SkNEW_ARGS(WriteTask, (*this, pdfData.get(), ".pdf"))); +} + +bool PDFTask::shouldSkip() const { + if (!FLAGS_pdf) { + return true; + } + if (fGM->getFlags() & skiagm::GM::kSkipPDF_Flag) { + return true; + } + return false; +} + +} // namespace DM diff --git a/dm/DMPDFTask.h b/dm/DMPDFTask.h new file mode 100644 index 0000000000..d273df66d8 --- /dev/null +++ b/dm/DMPDFTask.h @@ -0,0 +1,40 @@ +#ifndef DMPDFTask_DEFINED +#define DMPDFTask_DEFINED + +#include "DMPDFRasterizeTask.h" +#include "DMExpectations.h" +#include "DMTask.h" +#include "SkBitmap.h" +#include "SkString.h" +#include "SkTemplates.h" +#include "gm.h" + +namespace DM { + +// This task renders a GM using Skia's PDF backend. +// If rasterizePdfProc is non-NULL, it will spawn a PDFRasterizeTask. +class PDFTask : public CpuTask { +public: + PDFTask(const char* suffix, + Reporter*, + TaskRunner*, + const Expectations&, + skiagm::GMRegistry::Factory, + RasterizePdfProc); + + virtual void draw() SK_OVERRIDE; + + virtual bool shouldSkip() const SK_OVERRIDE; + + virtual SkString name() const SK_OVERRIDE { return fName; } + +private: + SkAutoTDelete<skiagm::GM> fGM; + const SkString fName; + const Expectations& fExpectations; + RasterizePdfProc fRasterize; +}; + +} // namespace DM + +#endif // DMPDFTask_DEFINED diff --git a/dm/DMWriteTask.cpp b/dm/DMWriteTask.cpp index 98ea929288..13f25d0fc2 100644 --- a/dm/DMWriteTask.cpp +++ b/dm/DMWriteTask.cpp @@ -28,14 +28,26 @@ static int split_suffixes(int N, const char* name, SkTArray<SkString>* out) { return consumed; } -WriteTask::WriteTask(const Task& parent, SkBitmap bitmap) - : CpuTask(parent), fBitmap(bitmap) { +inline static SkString find_gm_name(const Task& parent, SkTArray<SkString>* suffixList) { const int suffixes = parent.depth() + 1; const SkString& name = parent.name(); - const int totalSuffixLength = split_suffixes(suffixes, name.c_str(), &fSuffixes); - fGmName.set(name.c_str(), name.size()-totalSuffixLength); + const int totalSuffixLength = split_suffixes(suffixes, name.c_str(), suffixList); + return SkString(name.c_str(), name.size() - totalSuffixLength); } +WriteTask::WriteTask(const Task& parent, SkBitmap bitmap) + : CpuTask(parent) + , fGmName(find_gm_name(parent, &fSuffixes)) + , fBitmap(bitmap) + , fData(NULL) + , fExtension(".png") {} + +WriteTask::WriteTask(const Task& parent, SkData *data, const char* ext) + : CpuTask(parent) + , fGmName(find_gm_name(parent, &fSuffixes)) + , fData(SkRef(data)) + , fExtension(ext) {} + void WriteTask::makeDirOrFail(SkString dir) { if (!sk_mkdir(dir.c_str())) { this->fail(); @@ -103,6 +115,16 @@ struct PngAndRaw { } }; +// Does not take ownership of data. +bool save_data_to_file(const SkData* data, const char* path) { + SkFILEWStream stream(path); + if (!stream.isValid() || !stream.write(data->data(), data->size())) { + SkDebugf("Can't write %s.\n", path); + return false; + } + return true; +} + } // namespace void WriteTask::draw() { @@ -112,9 +134,13 @@ void WriteTask::draw() { dir = SkOSPath::SkPathJoin(dir.c_str(), fSuffixes[i].c_str()); this->makeDirOrFail(dir); } + SkString path = SkOSPath::SkPathJoin(dir.c_str(), fGmName.c_str()); - path.append(".png"); - if (!PngAndRaw::Encode(fBitmap, path.c_str())) { + path.append(fExtension); + + const bool ok = fData.get() ? save_data_to_file(fData, path.c_str()) + : PngAndRaw::Encode(fBitmap, path.c_str()); + if (!ok) { this->fail(); } } diff --git a/dm/DMWriteTask.h b/dm/DMWriteTask.h index a90f66aec5..c2c1d9fd47 100644 --- a/dm/DMWriteTask.h +++ b/dm/DMWriteTask.h @@ -15,8 +15,12 @@ namespace DM { class WriteTask : public CpuTask { public: - WriteTask(const Task& parent, // WriteTask must be a child Task. Pass its parent here. - SkBitmap bitmap); // Bitmap to write. + WriteTask(const Task& parent, // WriteTask must be a child task. + SkBitmap bitmap); // Bitmap to encode to PNG and write to disk. + + WriteTask(const Task& parent, // WriteTask must be a child task. + SkData *data, // Pre-encoded data to write to disk. + const char* ext); // File extension. virtual void draw() SK_OVERRIDE; virtual bool shouldSkip() const SK_OVERRIDE; @@ -34,8 +38,10 @@ public: private: SkTArray<SkString> fSuffixes; - SkString fGmName; + const SkString fGmName; const SkBitmap fBitmap; + SkAutoTUnref<SkData> fData; + const char* fExtension; void makeDirOrFail(SkString dir); }; @@ -1,13 +1,4 @@ -DM is like GM, but multithreaded. It doesn't do everything GM does yet. - -Current approximate list of missing features: - --config pdf - --mismatchPath - --missingExpectationsPath - --writePicturePath - - --deferred - +DM is like GM, but multithreaded. It doesn't do everything GM does. DM's design is based around Tasks and a TaskRunner. |