diff --git a/BUILD.gn b/BUILD.gn index f50f7be1c4..240ba78c10 100644 --- a/BUILD.gn +++ b/BUILD.gn @@ -1947,6 +1947,7 @@ if (skia_enable_tools) { bundle_ios_data = true } sources = [ + "tools/viewer/BisectSlide.cpp", "tools/viewer/GMSlide.cpp", "tools/viewer/ImGuiLayer.cpp", "tools/viewer/ImageSlide.cpp", diff --git a/gn/samples.gni b/gn/samples.gni index 48c580c2a3..135cf78013 100644 --- a/gn/samples.gni +++ b/gn/samples.gni @@ -68,7 +68,6 @@ samples_sources = [ "$_samplecode/SamplePathText.cpp", "$_samplecode/SamplePathClip.cpp", "$_samplecode/SamplePathEffects.cpp", - "$_samplecode/SamplePathFinder.cpp", "$_samplecode/SamplePathFuzz.cpp", "$_samplecode/SamplePathOverstroke.cpp", "$_samplecode/SamplePdfFileViewer.cpp", diff --git a/samplecode/SamplePathFinder.cpp b/samplecode/SamplePathFinder.cpp deleted file mode 100644 index d53c40bb5e..0000000000 --- a/samplecode/SamplePathFinder.cpp +++ /dev/null @@ -1,164 +0,0 @@ -/* - * Copyright 2017 Google Inc. - * - * Use of this source code is governed by a BSD-style license that can be - * found in the LICENSE file. - */ - -#include "SampleCode.h" -#include "SkCanvas.h" -#include "SkCommandLineFlags.h" -#include "SkDOM.h" -#include "SkSVGDOM.h" -#include "SkOSPath.h" -#include "SkPath.h" -#include "SkPicture.h" -#include "SkStream.h" -#include - -/** - * This is a simple utility designed to extract the paths from an SKP file and then isolate a single - * one of them. Use the 'x' and 'X' keys to guide a binary search: - * - * 'x': Throw out half the paths. - * 'X': Toggle which half gets tossed and which half is kept. - * 'Z': Back up one level. - * 'D': Dump the path. - */ -class PathFinderView : public SampleView, public SkCanvas { -public: - PathFinderView(const char name[] = nullptr) - : SkCanvas(4096, 4096, nullptr) - , fFilename(name) { - SkFILEStream stream(fFilename.c_str()); - if (!stream.isValid()) { - SkDebugf("invalid input file at \"%s\"\n", fFilename.c_str()); - return; - } - if (fFilename.endsWith(".svg")) { - SkDOM xml; - if (!xml.build(stream)) { - SkDebugf("XML parsing failed: \"%s\"\n", fFilename.c_str()); - return; - } - sk_sp svg = SkSVGDOM::MakeFromDOM(xml); - if (!svg) { - SkDebugf("couldn't load svg at \"%s\"\n", fFilename.c_str()); - return; - } - svg->setContainerSize(SkSize::Make(500, 500)); - svg->render(this); - } else { - sk_sp pic = SkPicture::MakeFromStream(&stream); - if (!pic) { - SkDebugf("couldn't load skp at \"%s\"\n", fFilename.c_str()); - return; - } - pic->playback(this); - } - } - - ~PathFinderView() override {} - -private: - // Called through SkPicture::playback during construction. - void onDrawPath(const SkPath& path, const SkPaint& paint) override { - fPaths.push_back() = {path, paint, this->getTotalMatrix()}; - } - - // overrides from SkEventSink - bool onQuery(SkEvent* evt) override { - if (SampleCode::TitleQ(*evt)) { - SkString name("PATHFINDER:"); - const char* basename = strrchr(fFilename.c_str(), SkOSPath::SEPARATOR); - name.append(basename ? basename+1: fFilename.c_str()); - SampleCode::TitleR(evt, name.c_str()); - return true; - } - SkUnichar key; - if (SampleCode::CharQ(*evt, &key)) { - if (this->handleKeystroke(key)) { - return true; - } - } - return this->INHERITED::onQuery(evt); - } - - bool handleKeystroke(SkUnichar key) { - switch (key) { - case 'X': - if (!fTossedPaths.empty()) { - SkTSwap(fPaths, fTossedPaths); - if ('X' == fTrail.back()) { - fTrail.pop_back(); - } else { - fTrail.push_back('X'); - } - } - return true; - case 'x': - if (fPaths.count() > 1) { - int midpt = (fPaths.count() + 1) / 2; - fPathHistory.emplace(fPaths, fTossedPaths); - fTossedPaths.reset(fPaths.begin() + midpt, fPaths.count() - midpt); - fPaths.resize_back(midpt); - fTrail.push_back('x'); - } - return true; - case 'Z': { - if (!fPathHistory.empty()) { - fPaths = fPathHistory.top().first; - fTossedPaths = fPathHistory.top().second; - fPathHistory.pop(); - char ch; - do { - ch = fTrail.back(); - fTrail.pop_back(); - } while (ch != 'x'); - } - return true; - } - case 'D': - SkDebugf("SampleApp --pathfinder %s", fFilename.c_str()); - if (!fTrail.empty()) { - SkDebugf(" --keys "); - for (char ch : fTrail) { - SkDebugf("%c", ch); - } - } - SkDebugf("\n"); - for (const FoundPath& foundPath : fPaths) { - foundPath.fPath.dump(); - } - return true; - } - return false; - } - - void onDrawContent(SkCanvas* canvas) override { - for (const FoundPath& path : fPaths) { - SkAutoCanvasRestore acr(canvas, true); - canvas->concat(path.fViewMatrix); - canvas->drawPath(path.fPath, path.fPaint); - } - } - - struct FoundPath { - SkPath fPath; - SkPaint fPaint; - SkMatrix fViewMatrix; - }; - - SkString fFilename; - SkTArray fPaths; - SkTArray fTossedPaths; - SkTArray fTrail; - - std::stack, SkTArray>> fPathHistory; - - typedef SampleView INHERITED; -}; - -SampleView* CreateSamplePathFinderView(const char filename[]) { - return new PathFinderView(filename); -} diff --git a/tools/viewer/BisectSlide.cpp b/tools/viewer/BisectSlide.cpp new file mode 100644 index 0000000000..63e69d278c --- /dev/null +++ b/tools/viewer/BisectSlide.cpp @@ -0,0 +1,130 @@ +/* + * Copyright 2018 Google Inc. + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#include "BisectSlide.h" + +#include "SkDOM.h" +#include "SkOSPath.h" +#include "SkPicture.h" +#include "SkStream.h" +#include "../experimental/svg/model/SkSVGDOM.h" + +sk_sp BisectSlide::Create(const char filepath[]) { + SkFILEStream stream(filepath); + if (!stream.isValid()) { + SkDebugf("BISECT: invalid input file at \"%s\"\n", filepath); + return nullptr; + } + + sk_sp bisect(new BisectSlide(filepath)); + if (bisect->fFilePath.endsWith(".svg")) { + SkDOM xml; + if (!xml.build(stream)) { + SkDebugf("BISECT: XML parsing failed: \"%s\"\n", filepath); + return nullptr; + } + sk_sp svg = SkSVGDOM::MakeFromDOM(xml); + if (!svg) { + SkDebugf("BISECT: couldn't load svg at \"%s\"\n", filepath); + return nullptr; + } + svg->setContainerSize(SkSize::Make(bisect->getDimensions())); + svg->render(bisect.get()); + } else { + sk_sp skp = SkPicture::MakeFromStream(&stream); + if (!skp) { + SkDebugf("BISECT: couldn't load skp at \"%s\"\n", filepath); + return nullptr; + } + skp->playback(bisect.get()); + } + + return bisect; +} + +BisectSlide::BisectSlide(const char filepath[]) + : SkCanvas(4096, 4096, nullptr) + , fFilePath(filepath) { + const char* basename = strrchr(fFilePath.c_str(), SkOSPath::SEPARATOR); + fName.printf("BISECT_%s", basename ? basename + 1 : fFilePath.c_str()); +} + +// Called through SkPicture::playback only during creation. +void BisectSlide::onDrawPath(const SkPath& path, const SkPaint& paint) { + SkRect bounds; + SkIRect ibounds; + this->getTotalMatrix().mapRect(&bounds, path.getBounds()); + bounds.roundOut(&ibounds); + fDrawBounds.join(ibounds); + fFoundPaths.push_back() = {path, paint, this->getTotalMatrix()}; +} + +bool BisectSlide::onChar(SkUnichar c) { + switch (c) { + case 'X': + if (!fTossedPaths.empty()) { + SkTSwap(fFoundPaths, fTossedPaths); + if ('X' == fTrail.back()) { + fTrail.pop_back(); + } else { + fTrail.push_back('X'); + } + } + return true; + + case 'x': + if (fFoundPaths.count() > 1) { + int midpt = (fFoundPaths.count() + 1) / 2; + fPathHistory.emplace(fFoundPaths, fTossedPaths); + fTossedPaths.reset(fFoundPaths.begin() + midpt, fFoundPaths.count() - midpt); + fFoundPaths.resize_back(midpt); + fTrail.push_back('x'); + } + return true; + + case 'Z': { + if (!fPathHistory.empty()) { + fFoundPaths = fPathHistory.top().first; + fTossedPaths = fPathHistory.top().second; + fPathHistory.pop(); + char ch; + do { + ch = fTrail.back(); + fTrail.pop_back(); + } while (ch != 'x'); + } + return true; + } + + case 'D': + SkDebugf("viewer --bisect %s", fFilePath.c_str()); + if (!fTrail.empty()) { + SkDebugf(" "); + for (char ch : fTrail) { + SkDebugf("%c", ch); + } + } + SkDebugf("\n"); + for (const FoundPath& foundPath : fFoundPaths) { + foundPath.fPath.dump(); + } + return true; + } + + return false; +} + +void BisectSlide::draw(SkCanvas* canvas) { + SkAutoCanvasRestore acr(canvas, true); + canvas->translate(-fDrawBounds.left(), -fDrawBounds.top()); + + for (const FoundPath& path : fFoundPaths) { + SkAutoCanvasRestore acr(canvas, true); + canvas->concat(path.fViewMatrix); + canvas->drawPath(path.fPath, path.fPaint); + } +} diff --git a/tools/viewer/BisectSlide.h b/tools/viewer/BisectSlide.h new file mode 100644 index 0000000000..c5300c6396 --- /dev/null +++ b/tools/viewer/BisectSlide.h @@ -0,0 +1,54 @@ +/* + * Copyright 2018 Google Inc. + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#ifndef BisectSlide_DEFINED +#define BisectSlide_DEFINED + +#include "SkCanvas.h" +#include "SkPath.h" +#include "Slide.h" +#include + +/** + * This is a simple utility designed to extract the paths from an SKP file and then isolate a single + * one of them via bisect. Use the 'x' and 'X' keys to guide a binary search: + * + * 'x': Throw out half the paths. + * 'X': Toggle which half gets tossed and which half is kept. + * 'Z': Back up one level. + * 'D': Dump the path. + */ +class BisectSlide : public Slide, public SkCanvas { +public: + static sk_sp Create(const char filepath[]); + + // Slide overrides. + SkISize getDimensions() const override { return fDrawBounds.size(); } + bool onChar(SkUnichar c) override; + void draw(SkCanvas* canvas) override; + +private: + BisectSlide(const char filepath[]); + + // SkCanvas override called only during creation. + void onDrawPath(const SkPath& path, const SkPaint& paint) override; + + struct FoundPath { + SkPath fPath; + SkPaint fPaint; + SkMatrix fViewMatrix; + }; + + SkString fFilePath; + SkIRect fDrawBounds = SkIRect::MakeEmpty(); + SkTArray fFoundPaths; + SkTArray fTossedPaths; + SkTArray fTrail; + std::stack, SkTArray>> fPathHistory; +}; + +#endif diff --git a/tools/viewer/Viewer.cpp b/tools/viewer/Viewer.cpp index c412218939..6582490d50 100644 --- a/tools/viewer/Viewer.cpp +++ b/tools/viewer/Viewer.cpp @@ -7,6 +7,7 @@ #include "Viewer.h" +#include "BisectSlide.h" #include "GMSlide.h" #include "ImageSlide.h" #include "Resources.h" @@ -82,6 +83,8 @@ static DEFINE_string2(backend, b, "sw", "Backend to use. Allowed values are " BA static DEFINE_int32(msaa, 1, "Number of subpixel samples. 0 for no HW antialiasing."); +DEFINE_string(bisect, "", "Path to a .skp or .svg file to bisect."); + DECLARE_int32(threads) const char* kBackendTypeStrings[sk_app::Window::kBackendTypeCount] = { @@ -478,23 +481,36 @@ Viewer::Viewer(int argc, char** argv, void* platformData) void Viewer::initSlides() { fAllSlideNames = Json::Value(Json::arrayValue); + // Bisect slide. + if (!FLAGS_bisect.isEmpty()) { + sk_sp bisect = BisectSlide::Create(FLAGS_bisect[0]); + if (bisect && !SkCommandLineFlags::ShouldSkip(FLAGS_match, bisect->getName().c_str())) { + if (FLAGS_bisect.count() >= 2) { + for (const char* ch = FLAGS_bisect[1]; *ch; ++ch) { + bisect->onChar(*ch); + } + } + fSlides.push_back(std::move(bisect)); + } + } + + // GMs + int firstGM = fSlides.count(); const skiagm::GMRegistry* gms(skiagm::GMRegistry::Head()); while (gms) { std::unique_ptr gm(gms->factory()(nullptr)); if (!SkCommandLineFlags::ShouldSkip(FLAGS_match, gm->getName())) { sk_sp slide(new GMSlide(gm.release())); - fSlides.push_back(slide); + fSlides.push_back(std::move(slide)); } gms = gms->next(); } - - // reverse array - for (int i = 0; i < fSlides.count()/2; ++i) { - sk_sp temp = fSlides[i]; - fSlides[i] = fSlides[fSlides.count() - i - 1]; - fSlides[fSlides.count() - i - 1] = temp; + // reverse gms + int numGMs = fSlides.count() - firstGM; + for (int i = 0; i < numGMs/2; ++i) { + std::swap(fSlides[firstGM + i], fSlides[fSlides.count() - i - 1]); } // samples