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 "tools/viewer/BisectSlide.h"
9
10#include "include/core/SkStream.h"
11#include "src/utils/SkOSPath.h"
12#include "tools/ToolUtils.h"
13
14#include <utility>
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    ToolUtils::sniff_paths(filepath, [&](const SkMatrix& matrix,
25                                         const SkPath& path,
26                                         const SkPaint& paint) {
27        SkRect bounds;
28        SkIRect ibounds;
29        matrix.mapRect(&bounds, path.getBounds());
30        bounds.roundOut(&ibounds);
31        bisect->fDrawBounds.join(ibounds);
32        bisect->fFoundPaths.push_back() = {path, paint, matrix};
33    });
34    return bisect;
35}
36
37BisectSlide::BisectSlide(const char filepath[])
38        : fFilePath(filepath) {
39    const char* basename = strrchr(fFilePath.c_str(), SkOSPath::SEPARATOR);
40    fName.printf("BISECT_%s", basename ? basename + 1 : fFilePath.c_str());
41}
42
43bool BisectSlide::onChar(SkUnichar c) {
44    switch (c) {
45        case 'X':
46            if (!fTossedPaths.empty()) {
47                using std::swap;
48                swap(fFoundPaths, fTossedPaths);
49                if ('X' == fTrail.back()) {
50                    fTrail.pop_back();
51                } else {
52                    fTrail.push_back('X');
53                }
54            }
55            return true;
56
57        case 'x':
58            if (fFoundPaths.count() > 1) {
59                int midpt = (fFoundPaths.count() + 1) / 2;
60                fPathHistory.emplace(fFoundPaths, fTossedPaths);
61                fTossedPaths.reset(fFoundPaths.begin() + midpt, fFoundPaths.count() - midpt);
62                fFoundPaths.resize_back(midpt);
63                fTrail.push_back('x');
64            }
65            return true;
66
67        case 'Z': {
68            if (!fPathHistory.empty()) {
69                fFoundPaths = fPathHistory.top().first;
70                fTossedPaths = fPathHistory.top().second;
71                fPathHistory.pop();
72                char ch;
73                do {
74                    ch = fTrail.back();
75                    fTrail.pop_back();
76                } while (ch != 'x');
77            }
78            return true;
79        }
80
81        case 'D':
82            SkDebugf("viewer --bisect %s", fFilePath.c_str());
83            if (!fTrail.empty()) {
84                SkDebugf(" ");
85                for (char ch : fTrail) {
86                    SkDebugf("%c", ch);
87                }
88            }
89            SkDebugf("\n");
90            for (const FoundPath& foundPath : fFoundPaths) {
91                foundPath.fPath.dump();
92            }
93            return true;
94    }
95
96    return false;
97}
98
99void BisectSlide::draw(SkCanvas* canvas) {
100    SkAutoCanvasRestore acr(canvas, true);
101    canvas->translate(-fDrawBounds.left(), -fDrawBounds.top());
102
103    for (const FoundPath& path : fFoundPaths) {
104        SkAutoCanvasRestore acr2(canvas, true);
105        canvas->concat(path.fViewMatrix);
106        canvas->drawPath(path.fPath, path.fPaint);
107    }
108}
109