1/*
2 * Copyright 2018 Google Inc.
3 *
4 * Use of this source code is governed by a BSD-style license that can be
5 * found in the LICENSE file.
6 */
7
8#include "BisectSlide.h"
9
10#include "SkDOM.h"
11#include "SkOSPath.h"
12#include "SkPicture.h"
13#include "SkStream.h"
14#include "../experimental/svg/model/SkSVGDOM.h"
15
16sk_sp<BisectSlide> BisectSlide::Create(const char filepath[]) {
17    SkFILEStream stream(filepath);
18    if (!stream.isValid()) {
19        SkDebugf("BISECT: invalid input file at \"%s\"\n", filepath);
20        return nullptr;
21    }
22
23    sk_sp<BisectSlide> bisect(new BisectSlide(filepath));
24    if (bisect->fFilePath.endsWith(".svg")) {
25        SkDOM xml;
26        if (!xml.build(stream)) {
27            SkDebugf("BISECT: XML parsing failed: \"%s\"\n", filepath);
28            return nullptr;
29        }
30        sk_sp<SkSVGDOM> svg = SkSVGDOM::MakeFromDOM(xml);
31        if (!svg) {
32            SkDebugf("BISECT: couldn't load svg at \"%s\"\n", filepath);
33            return nullptr;
34        }
35        svg->setContainerSize(SkSize::Make(bisect->getDimensions()));
36        svg->render(bisect.get());
37    } else {
38        sk_sp<SkPicture> skp = SkPicture::MakeFromStream(&stream);
39        if (!skp) {
40            SkDebugf("BISECT: couldn't load skp at \"%s\"\n", filepath);
41            return nullptr;
42        }
43        skp->playback(bisect.get());
44    }
45
46    return bisect;
47}
48
49BisectSlide::BisectSlide(const char filepath[])
50        : SkCanvas(4096, 4096, nullptr)
51        , fFilePath(filepath) {
52    const char* basename = strrchr(fFilePath.c_str(), SkOSPath::SEPARATOR);
53    fName.printf("BISECT_%s", basename ? basename + 1 : fFilePath.c_str());
54}
55
56// Called through SkPicture::playback only during creation.
57void BisectSlide::onDrawPath(const SkPath& path, const SkPaint& paint) {
58    SkRect bounds;
59    SkIRect ibounds;
60    this->getTotalMatrix().mapRect(&bounds, path.getBounds());
61    bounds.roundOut(&ibounds);
62    fDrawBounds.join(ibounds);
63    fFoundPaths.push_back() = {path, paint, this->getTotalMatrix()};
64}
65
66bool BisectSlide::onChar(SkUnichar c) {
67    switch (c) {
68        case 'X':
69            if (!fTossedPaths.empty()) {
70                SkTSwap(fFoundPaths, fTossedPaths);
71                if ('X' == fTrail.back()) {
72                    fTrail.pop_back();
73                } else {
74                    fTrail.push_back('X');
75                }
76            }
77            return true;
78
79        case 'x':
80            if (fFoundPaths.count() > 1) {
81                int midpt = (fFoundPaths.count() + 1) / 2;
82                fPathHistory.emplace(fFoundPaths, fTossedPaths);
83                fTossedPaths.reset(fFoundPaths.begin() + midpt, fFoundPaths.count() - midpt);
84                fFoundPaths.resize_back(midpt);
85                fTrail.push_back('x');
86            }
87            return true;
88
89        case 'Z': {
90            if (!fPathHistory.empty()) {
91                fFoundPaths = fPathHistory.top().first;
92                fTossedPaths = fPathHistory.top().second;
93                fPathHistory.pop();
94                char ch;
95                do {
96                    ch = fTrail.back();
97                    fTrail.pop_back();
98                } while (ch != 'x');
99            }
100            return true;
101        }
102
103        case 'D':
104            SkDebugf("viewer --bisect %s", fFilePath.c_str());
105            if (!fTrail.empty()) {
106                SkDebugf(" ");
107                for (char ch : fTrail) {
108                    SkDebugf("%c", ch);
109                }
110            }
111            SkDebugf("\n");
112            for (const FoundPath& foundPath : fFoundPaths) {
113                foundPath.fPath.dump();
114            }
115            return true;
116    }
117
118    return false;
119}
120
121void BisectSlide::draw(SkCanvas* canvas) {
122    SkAutoCanvasRestore acr(canvas, true);
123    canvas->translate(-fDrawBounds.left(), -fDrawBounds.top());
124
125    for (const FoundPath& path : fFoundPaths) {
126        SkAutoCanvasRestore acr(canvas, true);
127        canvas->concat(path.fViewMatrix);
128        canvas->drawPath(path.fPath, path.fPaint);
129    }
130}
131