blob: 5e819b17e93f00816b763d1fe99a744a908723dc [file] [log] [blame]
Florin Malita79725d32018-06-05 16:16:57 -04001/*
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 "SkCanvas.h"
9#include "SkCommandLineFlags.h"
10#include "SkGraphics.h"
Florin Malita0c604ed2018-07-13 15:47:27 -040011#include "SkMakeUnique.h"
Florin Malita79725d32018-06-05 16:16:57 -040012#include "SkOSFile.h"
13#include "SkOSPath.h"
Florin Malita0c604ed2018-07-13 15:47:27 -040014#include "SkPictureRecorder.h"
Florin Malita79725d32018-06-05 16:16:57 -040015#include "SkStream.h"
16#include "SkSurface.h"
17
18#if defined(SK_ENABLE_SKOTTIE)
19#include "Skottie.h"
20#endif
21
Florin Malita0c604ed2018-07-13 15:47:27 -040022DEFINE_string2(input , i, nullptr, "Input .json file.");
Florin Malita79725d32018-06-05 16:16:57 -040023DEFINE_string2(writePath, w, nullptr, "Output directory. Frames are names [0-9]{6}.png.");
Florin Malita0c604ed2018-07-13 15:47:27 -040024DEFINE_string2(format , f, "png" , "Output format (png or skp)");
Florin Malita79725d32018-06-05 16:16:57 -040025
26DEFINE_double(t0, 0, "Timeline start [0..1].");
27DEFINE_double(t1, 1, "Timeline stop [0..1].");
28DEFINE_double(fps, 30, "Decode frames per second.");
29
30DEFINE_int32(width , 800, "Render width.");
31DEFINE_int32(height, 600, "Render height.");
32
Florin Malita0c604ed2018-07-13 15:47:27 -040033namespace {
34
35class Sink : public SkNoncopyable {
36public:
37 virtual ~Sink() = default;
38
39 bool handleFrame(const sk_sp<skottie::Animation>& anim, size_t idx) const {
40 const auto frame_file = SkStringPrintf("0%06d.%s", idx, fExtension.c_str());
41 SkFILEWStream stream (SkOSPath::Join(FLAGS_writePath[0], frame_file.c_str()).c_str());
42
43 if (!stream.isValid()) {
44 SkDebugf("Could not open '%s/%s' for writing.\n",
45 FLAGS_writePath[0], frame_file.c_str());
46 return false;
47 }
48
49 return this->saveFrame(anim, &stream);
50 }
51
52protected:
53 Sink(const char* ext) : fExtension(ext) {}
54
55 virtual bool saveFrame(const sk_sp<skottie::Animation>& anim, SkFILEWStream*) const = 0;
56
57private:
58 const SkString fExtension;
59
60 using INHERITED = SkNoncopyable;
61};
62
63class PNGSink final : public Sink {
64public:
65 PNGSink()
66 : INHERITED("png")
67 , fSurface(SkSurface::MakeRasterN32Premul(FLAGS_width, FLAGS_height)) {
68 if (!fSurface) {
69 SkDebugf("Could not allocate a %d x %d surface.\n", FLAGS_width, FLAGS_height);
70 }
71 }
72
73 bool saveFrame(const sk_sp<skottie::Animation>& anim, SkFILEWStream* stream) const override {
74 if (!fSurface) return false;
75
76 auto* canvas = fSurface->getCanvas();
77 SkAutoCanvasRestore acr(canvas, true);
78
79 canvas->concat(SkMatrix::MakeRectToRect(SkRect::MakeSize(anim->size()),
80 SkRect::MakeIWH(FLAGS_width, FLAGS_height),
81 SkMatrix::kCenter_ScaleToFit));
82
83 canvas->clear(SK_ColorTRANSPARENT);
84 anim->render(canvas);
85
86 auto png_data = fSurface->makeImageSnapshot()->encodeToData();
87 if (!png_data) {
88 SkDebugf("Failed to encode frame!\n");
89 return false;
90 }
91
92 return stream->write(png_data->data(), png_data->size());
93 }
94
95private:
96 const sk_sp<SkSurface> fSurface;
97
98 using INHERITED = Sink;
99};
100
101class SKPSink final : public Sink {
102public:
103 SKPSink() : INHERITED("skp") {}
104
105 bool saveFrame(const sk_sp<skottie::Animation>& anim, SkFILEWStream* stream) const override {
106 SkPictureRecorder recorder;
107
108 auto canvas = recorder.beginRecording(FLAGS_width, FLAGS_height);
109 canvas->concat(SkMatrix::MakeRectToRect(SkRect::MakeSize(anim->size()),
110 SkRect::MakeIWH(FLAGS_width, FLAGS_height),
111 SkMatrix::kCenter_ScaleToFit));
112 anim->render(canvas);
113 recorder.finishRecordingAsPicture()->serialize(stream);
114
115 return true;
116 }
117
118private:
119 const sk_sp<SkSurface> fSurface;
120
121 using INHERITED = Sink;
122};
123
124} // namespace
125
Florin Malita79725d32018-06-05 16:16:57 -0400126int main(int argc, char** argv) {
Florin Malita79725d32018-06-05 16:16:57 -0400127 SkCommandLineFlags::Parse(argc, argv);
128 SkAutoGraphics ag;
129
130 if (FLAGS_input.isEmpty() || FLAGS_writePath.isEmpty()) {
131 SkDebugf("Missing required 'input' and 'writePath' args.\n");
132 return 1;
133 }
134
135 if (FLAGS_fps <= 0) {
136 SkDebugf("Invalid fps: %f.\n", FLAGS_fps);
137 return 1;
138 }
139
140 if (!sk_mkdir(FLAGS_writePath[0])) {
141 return 1;
142 }
143
Florin Malita0c604ed2018-07-13 15:47:27 -0400144 std::unique_ptr<Sink> sink;
145 if (0 == strcmp(FLAGS_format[0], "png")) {
146 sink = skstd::make_unique<PNGSink>();
147 } else if (0 == strcmp(FLAGS_format[0], "skp")) {
148 sink = skstd::make_unique<SKPSink>();
149 } else {
150 SkDebugf("Unknown format: %s\n", FLAGS_format[0]);
151 return 1;
152 }
153
Florin Malita79725d32018-06-05 16:16:57 -0400154 auto anim = skottie::Animation::MakeFromFile(FLAGS_input[0]);
155 if (!anim) {
156 SkDebugf("Could not load animation: '%s'.\n", FLAGS_input[0]);
157 return 1;
158 }
159
Florin Malita79725d32018-06-05 16:16:57 -0400160 static constexpr double kMaxFrames = 10000;
161 const auto t0 = SkTPin(FLAGS_t0, 0.0, 1.0),
162 t1 = SkTPin(FLAGS_t1, t0, 1.0),
163 advance = 1 / std::min(anim->duration() * FLAGS_fps, kMaxFrames);
164
165 size_t frame_index = 0;
166 for (auto t = t0; t <= t1; t += advance) {
Florin Malita79725d32018-06-05 16:16:57 -0400167 anim->seek(t);
Florin Malita0c604ed2018-07-13 15:47:27 -0400168 sink->handleFrame(anim, frame_index++);
Florin Malita79725d32018-06-05 16:16:57 -0400169 }
Florin Malita79725d32018-06-05 16:16:57 -0400170
171 return 0;
172}