From 95a7b76a44edd2f25423a4d395df553b80fe06d7 Mon Sep 17 00:00:00 2001 From: Bryce Thomas Date: Fri, 2 Mar 2018 13:54:21 -0800 Subject: dm: support printing specific page of mskp to SVG Currently with dm, it's possible to convert an .mskp to a multi-page PDF as follows: out/Release/dm --src mskp --mskps /tmp/filename.mskp -w /tmp \ --config pdf --verbose The SVG equivalent partially works, although only outputs the first page: out/Release/dm --src mskp --mskps /tmp/filename.mskp -w /tmp \ --config svg --verbose This CL adds support for passing extended options to SVG. Specifically, the 'page' option , which now determines which page of the source mskp gets converted to the SVG output. The new syntax is as follows: out/Release/dm --src mskp --mskps /tmp/filename.mskp -w /tmp \ --config svg[page=2] --verbose The `[key=value]` syntax is the same extended options syntax currently used by dm with --config gpu, e.g. `gpu[api=gl,color=8888]`. BUG=skia:7601 Change-Id: I3523d79b1cdbbba9e80fd46501331877091bdead Reviewed-on: https://skia-review.googlesource.com/105404 Commit-Queue: Mike Klein Reviewed-by: Mike Klein Reviewed-by: Mike Klein --- dm/DM.cpp | 4 + dm/DMSrcSink.cpp | 15 ++- dm/DMSrcSink.h | 5 +- tools/flags/SkCommonFlagsConfig.cpp | 235 ++++++++++++++++++++++++------------ tools/flags/SkCommonFlagsConfig.h | 16 +++ 5 files changed, 191 insertions(+), 84 deletions(-) diff --git a/dm/DM.cpp b/dm/DM.cpp index 1d3d815647..972c70d484 100644 --- a/dm/DM.cpp +++ b/dm/DM.cpp @@ -899,6 +899,10 @@ static Sink* create_sink(const GrContextOptions& grCtxOptions, const SkCommandLi } } #endif + if (const SkCommandLineConfigSvg* svgConfig = config->asConfigSvg()) { + int pageIndex = svgConfig->getPageIndex(); + return new SVGSink(pageIndex); + } #define SINK(t, sink, ...) if (config->getBackend().equals(t)) { return new sink(__VA_ARGS__); } diff --git a/dm/DMSrcSink.cpp b/dm/DMSrcSink.cpp index 2229b9c26d..149138d832 100644 --- a/dm/DMSrcSink.cpp +++ b/dm/DMSrcSink.cpp @@ -1838,14 +1838,23 @@ Error DebugSink::draw(const Src& src, SkBitmap*, SkWStream* dst, SkString*) cons /*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/ -SVGSink::SVGSink() {} +SVGSink::SVGSink(int pageIndex) : fPageIndex(pageIndex) {} Error SVGSink::draw(const Src& src, SkBitmap*, SkWStream* dst, SkString*) const { #if defined(SK_XML) + if (src.pageCount() > 1) { + int pageCount = src.pageCount(); + if (fPageIndex > pageCount - 1) { + return Error(SkStringPrintf("Page index %d too high for document with only %d pages.", + fPageIndex, pageCount)); + } + } std::unique_ptr xmlWriter(new SkXMLStreamWriter(dst)); - return src.draw(SkSVGCanvas::Make(SkRect::MakeWH(SkIntToScalar(src.size().width()), + return src.draw(fPageIndex, + SkSVGCanvas::Make(SkRect::MakeWH(SkIntToScalar(src.size().width()), SkIntToScalar(src.size().height())), - xmlWriter.get()).get()); + xmlWriter.get()) + .get()); #else return Error("SVG sink is disabled."); #endif // SK_XML diff --git a/dm/DMSrcSink.h b/dm/DMSrcSink.h index 6cc5df690f..227ef5341a 100644 --- a/dm/DMSrcSink.h +++ b/dm/DMSrcSink.h @@ -458,11 +458,14 @@ public: class SVGSink : public Sink { public: - SVGSink(); + SVGSink(int pageIndex = 0); Error draw(const Src&, SkBitmap*, SkWStream*, SkString*) const override; const char* fileExtension() const override { return "svg"; } SinkFlags flags() const override { return SinkFlags{ SinkFlags::kVector, SinkFlags::kDirect }; } + +private: + int fPageIndex; }; diff --git a/tools/flags/SkCommonFlagsConfig.cpp b/tools/flags/SkCommonFlagsConfig.cpp index 7ff6ce6849..85495c68d8 100644 --- a/tools/flags/SkCommonFlagsConfig.cpp +++ b/tools/flags/SkCommonFlagsConfig.cpp @@ -7,6 +7,7 @@ #include "SkCommonFlagsConfig.h" #include "SkImageInfo.h" +#include "SkTHash.h" #include @@ -187,42 +188,6 @@ SkCommandLineConfig::SkCommandLineConfig(const SkString& tag, const SkString& ba SkCommandLineConfig::~SkCommandLineConfig() { } -#if SK_SUPPORT_GPU -SkCommandLineConfigGpu::SkCommandLineConfigGpu( - const SkString& tag, const SkTArray& viaParts, ContextType contextType, bool useNVPR, - bool useDIText, int samples, SkColorType colorType, SkAlphaType alphaType, - sk_sp colorSpace, bool useStencilBuffers, bool testThreading) - : SkCommandLineConfig(tag, SkString("gpu"), viaParts) - , fContextType(contextType) - , fContextOverrides(ContextOverrides::kNone) - , fUseDIText(useDIText) - , fSamples(samples) - , fColorType(colorType) - , fAlphaType(alphaType) - , fColorSpace(std::move(colorSpace)) - , fTestThreading(testThreading) { - if (useNVPR) { - fContextOverrides |= ContextOverrides::kRequireNVPRSupport; - } else { - // We don't disable NVPR for instanced configs. Otherwise the caps wouldn't use mixed - // samples and we couldn't test the mixed samples backend for simple shapes. - fContextOverrides |= ContextOverrides::kDisableNVPR; - } - // Subtle logic: If the config has a color space attached, we're going to be rendering to sRGB, - // so we need that capability. In addition, to get the widest test coverage, we DO NOT require - // that we can disable sRGB decode. (That's for rendering sRGB sources to legacy surfaces). - // - // If the config doesn't have a color space attached, we're going to be rendering in legacy - // mode. In that case, we don't require sRGB capability and we defer to the client to decide on - // sRGB decode control. - if (fColorSpace) { - fContextOverrides |= ContextOverrides::kRequireSRGBSupport; - fContextOverrides |= ContextOverrides::kAllowSRGBWithoutDecodeControl; - } - if (!useStencilBuffers) { - fContextOverrides |= ContextOverrides::kAvoidStencilBuffers; - } -} static bool parse_option_int(const SkString& value, int* outInt) { if (value.isEmpty()) { return false; @@ -246,6 +211,7 @@ static bool parse_option_bool(const SkString& value, bool* outBool) { } return false; } +#if SK_SUPPORT_GPU static bool parse_option_gpu_api(const SkString& value, SkCommandLineConfigGpu::ContextType* outContextType) { if (value.equals("gl")) { @@ -385,74 +351,180 @@ static bool parse_option_gpu_color(const SkString& value, } return false; } +#endif + +// Extended options take form --config item[key1=value1,key2=value2,...] +// Example: --config gpu[api=gl,color=8888] +class ExtendedOptions { +public: + ExtendedOptions(const SkString& optionsString, bool* outParseSucceeded) { + SkTArray optionParts; + SkStrSplit(optionsString.c_str(), ",", kStrict_SkStrSplitMode, &optionParts); + for (int i = 0; i < optionParts.count(); ++i) { + SkTArray keyValueParts; + SkStrSplit(optionParts[i].c_str(), "=", kStrict_SkStrSplitMode, &keyValueParts); + if (keyValueParts.count() != 2) { + *outParseSucceeded = false; + return; + } + const SkString& key = keyValueParts[0]; + const SkString& value = keyValueParts[1]; + if (fOptionsMap.find(key) == nullptr) { + fOptionsMap.set(key, value); + } else { + // Duplicate values are not allowed. + *outParseSucceeded = false; + return; + } + } + *outParseSucceeded = true; + } + +#if SK_SUPPORT_GPU + bool get_option_gpu_color(const char* optionKey, + SkColorType* outColorType, + SkAlphaType* alphaType, + sk_sp* outColorSpace, + bool optional = true) const { + SkString* optionValue = fOptionsMap.find(SkString(optionKey)); + if (optionValue == nullptr) { + return optional; + } + return parse_option_gpu_color(*optionValue, outColorType, alphaType, outColorSpace); + } + + bool get_option_gpu_api(const char* optionKey, + SkCommandLineConfigGpu::ContextType* outContextType, + bool optional = true) const { + SkString* optionValue = fOptionsMap.find(SkString(optionKey)); + if (optionValue == nullptr) { + return optional; + } + return parse_option_gpu_api(*optionValue, outContextType); + } +#endif + + bool get_option_int(const char* optionKey, int* outInt, bool optional = true) const { + SkString* optionValue = fOptionsMap.find(SkString(optionKey)); + if (optionValue == nullptr) { + return optional; + } + return parse_option_int(*optionValue, outInt); + } + + bool get_option_bool(const char* optionKey, bool* outBool, bool optional = true) const { + SkString* optionValue = fOptionsMap.find(SkString(optionKey)); + if (optionValue == nullptr) { + return optional; + } + return parse_option_bool(*optionValue, outBool); + } + +private: + SkTHashMap fOptionsMap; +}; + +#if SK_SUPPORT_GPU +SkCommandLineConfigGpu::SkCommandLineConfigGpu( + const SkString& tag, const SkTArray& viaParts, ContextType contextType, bool useNVPR, + bool useDIText, int samples, SkColorType colorType, SkAlphaType alphaType, + sk_sp colorSpace, bool useStencilBuffers, bool testThreading) + : SkCommandLineConfig(tag, SkString("gpu"), viaParts) + , fContextType(contextType) + , fContextOverrides(ContextOverrides::kNone) + , fUseDIText(useDIText) + , fSamples(samples) + , fColorType(colorType) + , fAlphaType(alphaType) + , fColorSpace(std::move(colorSpace)) + , fTestThreading(testThreading) { + if (useNVPR) { + fContextOverrides |= ContextOverrides::kRequireNVPRSupport; + } else { + // We don't disable NVPR for instanced configs. Otherwise the caps wouldn't use mixed + // samples and we couldn't test the mixed samples backend for simple shapes. + fContextOverrides |= ContextOverrides::kDisableNVPR; + } + // Subtle logic: If the config has a color space attached, we're going to be rendering to sRGB, + // so we need that capability. In addition, to get the widest test coverage, we DO NOT require + // that we can disable sRGB decode. (That's for rendering sRGB sources to legacy surfaces). + // + // If the config doesn't have a color space attached, we're going to be rendering in legacy + // mode. In that case, we don't require sRGB capability and we defer to the client to decide on + // sRGB decode control. + if (fColorSpace) { + fContextOverrides |= ContextOverrides::kRequireSRGBSupport; + fContextOverrides |= ContextOverrides::kAllowSRGBWithoutDecodeControl; + } + if (!useStencilBuffers) { + fContextOverrides |= ContextOverrides::kAvoidStencilBuffers; + } +} SkCommandLineConfigGpu* parse_command_line_config_gpu(const SkString& tag, const SkTArray& vias, const SkString& options) { // Defaults for GPU backend. - bool seenAPI = false; SkCommandLineConfigGpu::ContextType contextType = GrContextFactory::kGL_ContextType; - bool seenUseNVPR = false; bool useNVPR = false; - bool seenUseDIText =false; bool useDIText = false; - bool seenSamples = false; int samples = 1; - bool seenColor = false; SkColorType colorType = kRGBA_8888_SkColorType; SkAlphaType alphaType = kPremul_SkAlphaType; sk_sp colorSpace = nullptr; - bool seenUseStencils = false; bool useStencils = true; - bool seenTestThreading = false; bool testThreading = false; - SkTArray optionParts; - SkStrSplit(options.c_str(), ",", kStrict_SkStrSplitMode, &optionParts); - for (int i = 0; i < optionParts.count(); ++i) { - SkTArray keyValueParts; - SkStrSplit(optionParts[i].c_str(), "=", kStrict_SkStrSplitMode, &keyValueParts); - if (keyValueParts.count() != 2) { - return nullptr; - } - const SkString& key = keyValueParts[0]; - const SkString& value = keyValueParts[1]; - bool valueOk = false; - if (key.equals("api") && !seenAPI) { - valueOk = parse_option_gpu_api(value, &contextType); - seenAPI = true; - } else if (key.equals("nvpr") && !seenUseNVPR) { - valueOk = parse_option_bool(value, &useNVPR); - seenUseNVPR = true; - } else if (key.equals("dit") && !seenUseDIText) { - valueOk = parse_option_bool(value, &useDIText); - seenUseDIText = true; - } else if (key.equals("samples") && !seenSamples) { - valueOk = parse_option_int(value, &samples); - seenSamples = true; - } else if (key.equals("color") && !seenColor) { - valueOk = parse_option_gpu_color(value, &colorType, &alphaType, &colorSpace); - seenColor = true; - } else if (key.equals("stencils") && !seenUseStencils) { - valueOk = parse_option_bool(value, &useStencils); - seenUseStencils = true; - } else if (key.equals("testThreading") && !seenTestThreading) { - valueOk = parse_option_bool(value, &testThreading); - seenTestThreading = true; - } - if (!valueOk) { - return nullptr; - } + bool parseSucceeded = false; + ExtendedOptions extendedOptions(options, &parseSucceeded); + if (!parseSucceeded) { + return nullptr; } - if (!seenAPI) { + + bool validOptions = + extendedOptions.get_option_gpu_api("api", &contextType, false) && + extendedOptions.get_option_bool("nvpr", &useNVPR) && + extendedOptions.get_option_bool("dit", &useDIText) && + extendedOptions.get_option_int("samples", &samples) && + extendedOptions.get_option_gpu_color("color", &colorType, &alphaType, &colorSpace) && + extendedOptions.get_option_bool("stencils", &useStencils) && + extendedOptions.get_option_bool("testThreading", &testThreading); + + if (!validOptions) { return nullptr; } + return new SkCommandLineConfigGpu(tag, vias, contextType, useNVPR, useDIText, samples, colorType, alphaType, colorSpace, useStencils, testThreading); } #endif +SkCommandLineConfigSvg::SkCommandLineConfigSvg(const SkString& tag, + const SkTArray& viaParts, int pageIndex) + : SkCommandLineConfig(tag, SkString("svg"), viaParts), fPageIndex(pageIndex) {} + +SkCommandLineConfigSvg* parse_command_line_config_svg(const SkString& tag, + const SkTArray& vias, + const SkString& options) { + // Defaults for SVG backend. + int pageIndex = 0; + + bool parseSucceeded = false; + ExtendedOptions extendedOptions(options, &parseSucceeded); + if (!parseSucceeded) { + return nullptr; + } + + bool validOptions = extendedOptions.get_option_int("page", &pageIndex); + + if (!validOptions) { + return nullptr; + } + + return new SkCommandLineConfigSvg(tag, vias, pageIndex); +} + void ParseConfigs(const SkCommandLineFlags::StringArray& configs, SkCommandLineConfigArray* outResult) { outResult->reset(); @@ -502,6 +574,9 @@ void ParseConfigs(const SkCommandLineFlags::StringArray& configs, parsedConfig = parse_command_line_config_gpu(tag, vias, extendedOptions); } #endif + if (extendedBackend.equals("svg")) { + parsedConfig = parse_command_line_config_svg(tag, vias, extendedOptions); + } if (!parsedConfig) { parsedConfig = new SkCommandLineConfig(tag, simpleBackend, vias); } diff --git a/tools/flags/SkCommonFlagsConfig.h b/tools/flags/SkCommonFlagsConfig.h index 49d536c0cd..d88edf1c3b 100644 --- a/tools/flags/SkCommonFlagsConfig.h +++ b/tools/flags/SkCommonFlagsConfig.h @@ -19,6 +19,7 @@ DECLARE_string(config); #if SK_SUPPORT_GPU class SkCommandLineConfigGpu; #endif +class SkCommandLineConfigSvg; // SkCommandLineConfig represents a Skia rendering configuration string. // The string has following form: @@ -34,6 +35,7 @@ class SkCommandLineConfig { #if SK_SUPPORT_GPU virtual const SkCommandLineConfigGpu* asConfigGpu() const { return nullptr; } #endif + virtual const SkCommandLineConfigSvg* asConfigSvg() const { return nullptr; } const SkString& getTag() const { return fTag; } const SkString& getBackend() const { return fBackend; } const SkTArray& getViaParts() const { return fViaParts; } @@ -85,6 +87,20 @@ class SkCommandLineConfigGpu : public SkCommandLineConfig { }; #endif +// SkCommandLineConfigSvg is a SkCommandLineConfig that extracts information out of the backend +// part of the tag. It is constructed tags that have: +// * backends of form "svg[option=value,option2=value,...]" +class SkCommandLineConfigSvg : public SkCommandLineConfig { +public: + SkCommandLineConfigSvg(const SkString& tag, const SkTArray& viaParts, int pageIndex); + const SkCommandLineConfigSvg* asConfigSvg() const override { return this; } + + int getPageIndex() const { return fPageIndex; } + +private: + int fPageIndex; +}; + typedef SkTArray, true> SkCommandLineConfigArray; void ParseConfigs(const SkCommandLineFlags::StringArray& configList, SkCommandLineConfigArray* outResult); -- cgit v1.2.3