xref: /aosp_15_r20/external/skia/modules/skottie/src/SkottieTool.cpp (revision c8dee2aa9b3f27cf6c858bd81872bdeb2c07ed17)
1*c8dee2aaSAndroid Build Coastguard Worker /*
2*c8dee2aaSAndroid Build Coastguard Worker  * Copyright 2018 Google Inc.
3*c8dee2aaSAndroid Build Coastguard Worker  *
4*c8dee2aaSAndroid Build Coastguard Worker  * Use of this source code is governed by a BSD-style license that can be
5*c8dee2aaSAndroid Build Coastguard Worker  * found in the LICENSE file.
6*c8dee2aaSAndroid Build Coastguard Worker  */
7*c8dee2aaSAndroid Build Coastguard Worker 
8*c8dee2aaSAndroid Build Coastguard Worker #include "include/codec/SkCodec.h"
9*c8dee2aaSAndroid Build Coastguard Worker #include "include/codec/SkJpegDecoder.h"
10*c8dee2aaSAndroid Build Coastguard Worker #include "include/codec/SkPngDecoder.h"
11*c8dee2aaSAndroid Build Coastguard Worker #include "include/codec/SkWebpDecoder.h"
12*c8dee2aaSAndroid Build Coastguard Worker #include "include/core/SkCanvas.h"
13*c8dee2aaSAndroid Build Coastguard Worker #include "include/core/SkColor.h"
14*c8dee2aaSAndroid Build Coastguard Worker #include "include/core/SkData.h"
15*c8dee2aaSAndroid Build Coastguard Worker #include "include/core/SkFontMgr.h"
16*c8dee2aaSAndroid Build Coastguard Worker #include "include/core/SkGraphics.h"
17*c8dee2aaSAndroid Build Coastguard Worker #include "include/core/SkImage.h"
18*c8dee2aaSAndroid Build Coastguard Worker #include "include/core/SkImageInfo.h"
19*c8dee2aaSAndroid Build Coastguard Worker #include "include/core/SkMatrix.h"
20*c8dee2aaSAndroid Build Coastguard Worker #include "include/core/SkPixmap.h"
21*c8dee2aaSAndroid Build Coastguard Worker #include "include/core/SkRect.h"
22*c8dee2aaSAndroid Build Coastguard Worker #include "include/core/SkRefCnt.h"
23*c8dee2aaSAndroid Build Coastguard Worker #include "include/core/SkStream.h"
24*c8dee2aaSAndroid Build Coastguard Worker #include "include/core/SkString.h"
25*c8dee2aaSAndroid Build Coastguard Worker #include "include/core/SkSurface.h"
26*c8dee2aaSAndroid Build Coastguard Worker #include "include/core/SkTypes.h"
27*c8dee2aaSAndroid Build Coastguard Worker #include "include/encode/SkPngEncoder.h"
28*c8dee2aaSAndroid Build Coastguard Worker #include "include/private/base/SkDebug.h"
29*c8dee2aaSAndroid Build Coastguard Worker #include "include/private/base/SkTPin.h"
30*c8dee2aaSAndroid Build Coastguard Worker #include "include/private/base/SkTo.h"
31*c8dee2aaSAndroid Build Coastguard Worker #include "modules/skottie/include/ExternalLayer.h"
32*c8dee2aaSAndroid Build Coastguard Worker #include "modules/skottie/include/Skottie.h"
33*c8dee2aaSAndroid Build Coastguard Worker #include "modules/skottie/utils/SkottieUtils.h"
34*c8dee2aaSAndroid Build Coastguard Worker #include "modules/skresources/include/SkResources.h"
35*c8dee2aaSAndroid Build Coastguard Worker #include "modules/skshaper/utils/FactoryHelpers.h"
36*c8dee2aaSAndroid Build Coastguard Worker #include "src/core/SkOSFile.h"
37*c8dee2aaSAndroid Build Coastguard Worker #include "src/core/SkTaskGroup.h"
38*c8dee2aaSAndroid Build Coastguard Worker #include "src/utils/SkOSPath.h"
39*c8dee2aaSAndroid Build Coastguard Worker #include "tools/flags/CommandLineFlags.h"
40*c8dee2aaSAndroid Build Coastguard Worker 
41*c8dee2aaSAndroid Build Coastguard Worker #if !defined(CPU_ONLY)
42*c8dee2aaSAndroid Build Coastguard Worker #include "include/gpu/GpuTypes.h"
43*c8dee2aaSAndroid Build Coastguard Worker #include "include/gpu/ganesh/GrDirectContext.h"
44*c8dee2aaSAndroid Build Coastguard Worker #include "include/gpu/ganesh/GrTypes.h"
45*c8dee2aaSAndroid Build Coastguard Worker #include "include/gpu/ganesh/SkSurfaceGanesh.h"
46*c8dee2aaSAndroid Build Coastguard Worker #include "tools/gpu/ContextType.h"
47*c8dee2aaSAndroid Build Coastguard Worker #include "tools/gpu/GrContextFactory.h"
48*c8dee2aaSAndroid Build Coastguard Worker #endif
49*c8dee2aaSAndroid Build Coastguard Worker 
50*c8dee2aaSAndroid Build Coastguard Worker #if !defined(CPU_ONLY) && !defined(GPU_ONLY)
51*c8dee2aaSAndroid Build Coastguard Worker #include "include/core/SkPicture.h"
52*c8dee2aaSAndroid Build Coastguard Worker #include "include/core/SkPictureRecorder.h"
53*c8dee2aaSAndroid Build Coastguard Worker #include "include/core/SkSerialProcs.h"
54*c8dee2aaSAndroid Build Coastguard Worker #include "src/image/SkImage_Base.h"
55*c8dee2aaSAndroid Build Coastguard Worker #endif
56*c8dee2aaSAndroid Build Coastguard Worker 
57*c8dee2aaSAndroid Build Coastguard Worker #include <algorithm>
58*c8dee2aaSAndroid Build Coastguard Worker #include <chrono>
59*c8dee2aaSAndroid Build Coastguard Worker #include <cstdio>
60*c8dee2aaSAndroid Build Coastguard Worker #include <cstring>
61*c8dee2aaSAndroid Build Coastguard Worker #include <functional>
62*c8dee2aaSAndroid Build Coastguard Worker #include <memory>
63*c8dee2aaSAndroid Build Coastguard Worker #include <numeric>
64*c8dee2aaSAndroid Build Coastguard Worker #include <utility>
65*c8dee2aaSAndroid Build Coastguard Worker #include <vector>
66*c8dee2aaSAndroid Build Coastguard Worker 
67*c8dee2aaSAndroid Build Coastguard Worker #if defined(HAVE_VIDEO_ENCODER)
68*c8dee2aaSAndroid Build Coastguard Worker     #include <future>
69*c8dee2aaSAndroid Build Coastguard Worker     #include "experimental/ffmpeg/SkVideoEncoder.h"
70*c8dee2aaSAndroid Build Coastguard Worker     const char* formats_help = "Output format (png, skp, mp4, or null)";
71*c8dee2aaSAndroid Build Coastguard Worker #else
72*c8dee2aaSAndroid Build Coastguard Worker     const char* formats_help = "Output format (png, skp, or null)";
73*c8dee2aaSAndroid Build Coastguard Worker #endif
74*c8dee2aaSAndroid Build Coastguard Worker 
75*c8dee2aaSAndroid Build Coastguard Worker #if defined(SK_BUILD_FOR_MAC) && defined(SK_FONTMGR_CORETEXT_AVAILABLE)
76*c8dee2aaSAndroid Build Coastguard Worker #include "include/ports/SkFontMgr_mac_ct.h"
77*c8dee2aaSAndroid Build Coastguard Worker #elif defined(SK_BUILD_FOR_ANDROID) && defined(SK_FONTMGR_ANDROID_AVAILABLE)
78*c8dee2aaSAndroid Build Coastguard Worker #include "include/ports/SkFontMgr_android.h"
79*c8dee2aaSAndroid Build Coastguard Worker #include "include/ports/SkFontScanner_FreeType.h"
80*c8dee2aaSAndroid Build Coastguard Worker #elif defined(SK_BUILD_FOR_UNIX) && defined(SK_FONTMGR_FONTCONFIG_AVAILABLE) && defined(SK_TYPEFACE_FACTORY_FREETYPE)
81*c8dee2aaSAndroid Build Coastguard Worker #include "include/ports/SkFontMgr_fontconfig.h"
82*c8dee2aaSAndroid Build Coastguard Worker #include "include/ports/SkFontScanner_FreeType.h"
83*c8dee2aaSAndroid Build Coastguard Worker #else
84*c8dee2aaSAndroid Build Coastguard Worker #include "include/ports/SkFontMgr_empty.h"
85*c8dee2aaSAndroid Build Coastguard Worker #endif
86*c8dee2aaSAndroid Build Coastguard Worker 
87*c8dee2aaSAndroid Build Coastguard Worker static DEFINE_string2(input    , i, nullptr, "Input .json file.");
88*c8dee2aaSAndroid Build Coastguard Worker static DEFINE_string2(writePath, w, nullptr, "Output directory.  Frames are names [0-9]{6}.png.");
89*c8dee2aaSAndroid Build Coastguard Worker static DEFINE_string2(format   , f, "png"  , formats_help);
90*c8dee2aaSAndroid Build Coastguard Worker 
91*c8dee2aaSAndroid Build Coastguard Worker static DEFINE_double(t0,    0, "Timeline start [0..1].");
92*c8dee2aaSAndroid Build Coastguard Worker static DEFINE_double(t1,    1, "Timeline stop [0..1].");
93*c8dee2aaSAndroid Build Coastguard Worker static DEFINE_double(fps,   0, "Decode frames per second (default is animation native fps).");
94*c8dee2aaSAndroid Build Coastguard Worker 
95*c8dee2aaSAndroid Build Coastguard Worker static DEFINE_int(width , 800, "Render width.");
96*c8dee2aaSAndroid Build Coastguard Worker static DEFINE_int(height, 600, "Render height.");
97*c8dee2aaSAndroid Build Coastguard Worker static DEFINE_int(threads,  0, "Number of worker threads (0 -> cores count).");
98*c8dee2aaSAndroid Build Coastguard Worker 
99*c8dee2aaSAndroid Build Coastguard Worker static DEFINE_bool2(gpu, g, false, "Enable GPU rasterization.");
100*c8dee2aaSAndroid Build Coastguard Worker 
101*c8dee2aaSAndroid Build Coastguard Worker namespace {
102*c8dee2aaSAndroid Build Coastguard Worker 
103*c8dee2aaSAndroid Build Coastguard Worker static constexpr SkColor kClearColor = SK_ColorWHITE;
104*c8dee2aaSAndroid Build Coastguard Worker 
105*c8dee2aaSAndroid Build Coastguard Worker enum class OutputFormat {
106*c8dee2aaSAndroid Build Coastguard Worker     kPNG,
107*c8dee2aaSAndroid Build Coastguard Worker     kSKP,
108*c8dee2aaSAndroid Build Coastguard Worker     kNull,
109*c8dee2aaSAndroid Build Coastguard Worker     kMP4,
110*c8dee2aaSAndroid Build Coastguard Worker };
111*c8dee2aaSAndroid Build Coastguard Worker 
112*c8dee2aaSAndroid Build Coastguard Worker 
ms_since(std::chrono::steady_clock::time_point start)113*c8dee2aaSAndroid Build Coastguard Worker auto ms_since(std::chrono::steady_clock::time_point start) {
114*c8dee2aaSAndroid Build Coastguard Worker     const auto elapsed = std::chrono::steady_clock::now() - start;
115*c8dee2aaSAndroid Build Coastguard Worker     return std::chrono::duration_cast<std::chrono::milliseconds>(elapsed).count();
116*c8dee2aaSAndroid Build Coastguard Worker }
117*c8dee2aaSAndroid Build Coastguard Worker 
make_file_stream(size_t frame_index,const char * extension)118*c8dee2aaSAndroid Build Coastguard Worker std::unique_ptr<SkFILEWStream> make_file_stream(size_t frame_index, const char* extension) {
119*c8dee2aaSAndroid Build Coastguard Worker     const auto file = SkStringPrintf("0%06zu.%s", frame_index, extension);
120*c8dee2aaSAndroid Build Coastguard Worker     const auto path = SkOSPath::Join(FLAGS_writePath[0], file.c_str());
121*c8dee2aaSAndroid Build Coastguard Worker 
122*c8dee2aaSAndroid Build Coastguard Worker     auto stream = std::make_unique<SkFILEWStream>(path.c_str());
123*c8dee2aaSAndroid Build Coastguard Worker 
124*c8dee2aaSAndroid Build Coastguard Worker     return stream->isValid() ? std::move(stream) : nullptr;
125*c8dee2aaSAndroid Build Coastguard Worker }
126*c8dee2aaSAndroid Build Coastguard Worker 
127*c8dee2aaSAndroid Build Coastguard Worker class FrameSink {
128*c8dee2aaSAndroid Build Coastguard Worker public:
129*c8dee2aaSAndroid Build Coastguard Worker     virtual ~FrameSink() = default;
130*c8dee2aaSAndroid Build Coastguard Worker 
131*c8dee2aaSAndroid Build Coastguard Worker     static std::unique_ptr<FrameSink> Make(OutputFormat fmt, size_t frame_count);
132*c8dee2aaSAndroid Build Coastguard Worker 
133*c8dee2aaSAndroid Build Coastguard Worker     virtual void writeFrame(sk_sp<SkImage> frame, size_t frame_index) = 0;
134*c8dee2aaSAndroid Build Coastguard Worker 
finalize(double fps)135*c8dee2aaSAndroid Build Coastguard Worker     virtual void finalize(double fps) {}
136*c8dee2aaSAndroid Build Coastguard Worker 
137*c8dee2aaSAndroid Build Coastguard Worker protected:
138*c8dee2aaSAndroid Build Coastguard Worker     FrameSink() = default;
139*c8dee2aaSAndroid Build Coastguard Worker 
140*c8dee2aaSAndroid Build Coastguard Worker private:
141*c8dee2aaSAndroid Build Coastguard Worker     FrameSink(const FrameSink&)            = delete;
142*c8dee2aaSAndroid Build Coastguard Worker     FrameSink& operator=(const FrameSink&) = delete;
143*c8dee2aaSAndroid Build Coastguard Worker };
144*c8dee2aaSAndroid Build Coastguard Worker 
145*c8dee2aaSAndroid Build Coastguard Worker class PNGSink final : public FrameSink {
146*c8dee2aaSAndroid Build Coastguard Worker public:
writeFrame(sk_sp<SkImage> frame,size_t frame_index)147*c8dee2aaSAndroid Build Coastguard Worker     void writeFrame(sk_sp<SkImage> frame, size_t frame_index) override {
148*c8dee2aaSAndroid Build Coastguard Worker         auto stream = make_file_stream(frame_index, "png");
149*c8dee2aaSAndroid Build Coastguard Worker 
150*c8dee2aaSAndroid Build Coastguard Worker         if (!frame || !stream) {
151*c8dee2aaSAndroid Build Coastguard Worker             return;
152*c8dee2aaSAndroid Build Coastguard Worker         }
153*c8dee2aaSAndroid Build Coastguard Worker 
154*c8dee2aaSAndroid Build Coastguard Worker         // Set encoding options to favor speed over size.
155*c8dee2aaSAndroid Build Coastguard Worker         SkPngEncoder::Options options;
156*c8dee2aaSAndroid Build Coastguard Worker         options.fZLibLevel   = 1;
157*c8dee2aaSAndroid Build Coastguard Worker         options.fFilterFlags = SkPngEncoder::FilterFlag::kNone;
158*c8dee2aaSAndroid Build Coastguard Worker 
159*c8dee2aaSAndroid Build Coastguard Worker         SkPixmap pixmap;
160*c8dee2aaSAndroid Build Coastguard Worker         SkAssertResult(frame->peekPixels(&pixmap));
161*c8dee2aaSAndroid Build Coastguard Worker 
162*c8dee2aaSAndroid Build Coastguard Worker         SkPngEncoder::Encode(stream.get(), pixmap, options);
163*c8dee2aaSAndroid Build Coastguard Worker     }
164*c8dee2aaSAndroid Build Coastguard Worker };
165*c8dee2aaSAndroid Build Coastguard Worker 
166*c8dee2aaSAndroid Build Coastguard Worker class NullSink final : public FrameSink {
167*c8dee2aaSAndroid Build Coastguard Worker public:
writeFrame(sk_sp<SkImage>,size_t)168*c8dee2aaSAndroid Build Coastguard Worker     void writeFrame(sk_sp<SkImage>, size_t) override {}
169*c8dee2aaSAndroid Build Coastguard Worker };
170*c8dee2aaSAndroid Build Coastguard Worker 
171*c8dee2aaSAndroid Build Coastguard Worker #if defined(HAVE_VIDEO_ENCODER)
172*c8dee2aaSAndroid Build Coastguard Worker class MP4Sink final : public FrameSink {
173*c8dee2aaSAndroid Build Coastguard Worker public:
MP4Sink(size_t frame_count)174*c8dee2aaSAndroid Build Coastguard Worker     explicit MP4Sink(size_t frame_count) {
175*c8dee2aaSAndroid Build Coastguard Worker         fFrames.resize(frame_count);
176*c8dee2aaSAndroid Build Coastguard Worker     }
177*c8dee2aaSAndroid Build Coastguard Worker 
writeFrame(sk_sp<SkImage> frame,size_t frame_index)178*c8dee2aaSAndroid Build Coastguard Worker     void writeFrame(sk_sp<SkImage> frame, size_t frame_index) override {
179*c8dee2aaSAndroid Build Coastguard Worker         fFrames[frame_index].set_value(std::move(frame));
180*c8dee2aaSAndroid Build Coastguard Worker     }
181*c8dee2aaSAndroid Build Coastguard Worker 
finalize(double fps)182*c8dee2aaSAndroid Build Coastguard Worker     void finalize(double fps) override {
183*c8dee2aaSAndroid Build Coastguard Worker         SkVideoEncoder encoder;
184*c8dee2aaSAndroid Build Coastguard Worker         if (!encoder.beginRecording({FLAGS_width, FLAGS_height}, sk_double_round2int(fps))) {
185*c8dee2aaSAndroid Build Coastguard Worker             fprintf(stderr, "Invalid video stream configuration.\n");
186*c8dee2aaSAndroid Build Coastguard Worker         }
187*c8dee2aaSAndroid Build Coastguard Worker 
188*c8dee2aaSAndroid Build Coastguard Worker         std::vector<double> starved_ms;
189*c8dee2aaSAndroid Build Coastguard Worker         starved_ms.reserve(fFrames.size());
190*c8dee2aaSAndroid Build Coastguard Worker 
191*c8dee2aaSAndroid Build Coastguard Worker         for (auto& frame_promise : fFrames) {
192*c8dee2aaSAndroid Build Coastguard Worker             const auto start = std::chrono::steady_clock::now();
193*c8dee2aaSAndroid Build Coastguard Worker             auto frame = frame_promise.get_future().get();
194*c8dee2aaSAndroid Build Coastguard Worker             starved_ms.push_back(ms_since(start));
195*c8dee2aaSAndroid Build Coastguard Worker 
196*c8dee2aaSAndroid Build Coastguard Worker             if (!frame) continue;
197*c8dee2aaSAndroid Build Coastguard Worker 
198*c8dee2aaSAndroid Build Coastguard Worker             SkPixmap pixmap;
199*c8dee2aaSAndroid Build Coastguard Worker             SkAssertResult(frame->peekPixels(&pixmap));
200*c8dee2aaSAndroid Build Coastguard Worker             encoder.addFrame(pixmap);
201*c8dee2aaSAndroid Build Coastguard Worker         }
202*c8dee2aaSAndroid Build Coastguard Worker 
203*c8dee2aaSAndroid Build Coastguard Worker         auto mp4 = encoder.endRecording();
204*c8dee2aaSAndroid Build Coastguard Worker 
205*c8dee2aaSAndroid Build Coastguard Worker         SkFILEWStream{FLAGS_writePath[0]}
206*c8dee2aaSAndroid Build Coastguard Worker             .write(mp4->data(), mp4->size());
207*c8dee2aaSAndroid Build Coastguard Worker 
208*c8dee2aaSAndroid Build Coastguard Worker         // If everything's going well, the first frame should account for the most,
209*c8dee2aaSAndroid Build Coastguard Worker         // and ideally nearly all, starvation.
210*c8dee2aaSAndroid Build Coastguard Worker         double first = starved_ms[0];
211*c8dee2aaSAndroid Build Coastguard Worker         std::sort(starved_ms.begin(), starved_ms.end());
212*c8dee2aaSAndroid Build Coastguard Worker         double sum = std::accumulate(starved_ms.begin(), starved_ms.end(), 0);
213*c8dee2aaSAndroid Build Coastguard Worker         printf("Encoder starved stats: "
214*c8dee2aaSAndroid Build Coastguard Worker                "min %gms, med %gms, avg %gms, max %gms, sum %gms, first %gms (%s)\n",
215*c8dee2aaSAndroid Build Coastguard Worker                starved_ms[0], starved_ms[fFrames.size()/2], sum/fFrames.size(), starved_ms.back(),
216*c8dee2aaSAndroid Build Coastguard Worker                sum, first, first == starved_ms.back() ? "ok" : "BAD");
217*c8dee2aaSAndroid Build Coastguard Worker 
218*c8dee2aaSAndroid Build Coastguard Worker     }
219*c8dee2aaSAndroid Build Coastguard Worker 
220*c8dee2aaSAndroid Build Coastguard Worker     std::vector<std::promise<sk_sp<SkImage>>> fFrames;
221*c8dee2aaSAndroid Build Coastguard Worker };
222*c8dee2aaSAndroid Build Coastguard Worker #endif // HAVE_VIDEO_ENCODER
223*c8dee2aaSAndroid Build Coastguard Worker 
Make(OutputFormat fmt,size_t frame_count)224*c8dee2aaSAndroid Build Coastguard Worker std::unique_ptr<FrameSink> FrameSink::Make(OutputFormat fmt, size_t frame_count) {
225*c8dee2aaSAndroid Build Coastguard Worker     switch (fmt) {
226*c8dee2aaSAndroid Build Coastguard Worker     case OutputFormat::kPNG:
227*c8dee2aaSAndroid Build Coastguard Worker         return std::make_unique<PNGSink>();
228*c8dee2aaSAndroid Build Coastguard Worker     case OutputFormat::kSKP:
229*c8dee2aaSAndroid Build Coastguard Worker         // The SKP generator does not use a sink.
230*c8dee2aaSAndroid Build Coastguard Worker         [[fallthrough]];
231*c8dee2aaSAndroid Build Coastguard Worker     case OutputFormat::kNull:
232*c8dee2aaSAndroid Build Coastguard Worker         return std::make_unique<NullSink>();
233*c8dee2aaSAndroid Build Coastguard Worker     case OutputFormat::kMP4:
234*c8dee2aaSAndroid Build Coastguard Worker #if defined(HAVE_VIDEO_ENCODER)
235*c8dee2aaSAndroid Build Coastguard Worker         return std::make_unique<MP4Sink>(frame_count);
236*c8dee2aaSAndroid Build Coastguard Worker #else
237*c8dee2aaSAndroid Build Coastguard Worker         return nullptr;
238*c8dee2aaSAndroid Build Coastguard Worker #endif
239*c8dee2aaSAndroid Build Coastguard Worker     }
240*c8dee2aaSAndroid Build Coastguard Worker 
241*c8dee2aaSAndroid Build Coastguard Worker     SkUNREACHABLE;
242*c8dee2aaSAndroid Build Coastguard Worker }
243*c8dee2aaSAndroid Build Coastguard Worker 
244*c8dee2aaSAndroid Build Coastguard Worker class FrameGenerator {
245*c8dee2aaSAndroid Build Coastguard Worker public:
246*c8dee2aaSAndroid Build Coastguard Worker     virtual ~FrameGenerator() = default;
247*c8dee2aaSAndroid Build Coastguard Worker 
248*c8dee2aaSAndroid Build Coastguard Worker     static std::unique_ptr<FrameGenerator> Make(FrameSink*, OutputFormat, const SkMatrix&);
249*c8dee2aaSAndroid Build Coastguard Worker 
generateFrame(const skottie::Animation *,size_t frame_index)250*c8dee2aaSAndroid Build Coastguard Worker     virtual void generateFrame(const skottie::Animation*, size_t frame_index) {}
251*c8dee2aaSAndroid Build Coastguard Worker 
252*c8dee2aaSAndroid Build Coastguard Worker protected:
FrameGenerator(FrameSink * sink)253*c8dee2aaSAndroid Build Coastguard Worker     explicit FrameGenerator(FrameSink* sink) : fSink(sink) {}
254*c8dee2aaSAndroid Build Coastguard Worker 
255*c8dee2aaSAndroid Build Coastguard Worker     FrameSink* fSink;
256*c8dee2aaSAndroid Build Coastguard Worker 
257*c8dee2aaSAndroid Build Coastguard Worker private:
258*c8dee2aaSAndroid Build Coastguard Worker     FrameGenerator(const FrameGenerator&)            = delete;
259*c8dee2aaSAndroid Build Coastguard Worker     FrameGenerator& operator=(const FrameGenerator&) = delete;
260*c8dee2aaSAndroid Build Coastguard Worker };
261*c8dee2aaSAndroid Build Coastguard Worker 
262*c8dee2aaSAndroid Build Coastguard Worker class CPUGenerator final : public FrameGenerator {
263*c8dee2aaSAndroid Build Coastguard Worker public:
264*c8dee2aaSAndroid Build Coastguard Worker #if defined(GPU_ONLY)
Make(FrameSink * sink,const SkMatrix & matrix)265*c8dee2aaSAndroid Build Coastguard Worker     static std::unique_ptr<FrameGenerator> Make(FrameSink* sink, const SkMatrix& matrix) {
266*c8dee2aaSAndroid Build Coastguard Worker         return nullptr;
267*c8dee2aaSAndroid Build Coastguard Worker     }
268*c8dee2aaSAndroid Build Coastguard Worker #else
269*c8dee2aaSAndroid Build Coastguard Worker     static std::unique_ptr<FrameGenerator> Make(FrameSink* sink, const SkMatrix& matrix) {
270*c8dee2aaSAndroid Build Coastguard Worker         auto surface = SkSurfaces::Raster(SkImageInfo::MakeN32Premul(FLAGS_width, FLAGS_height));
271*c8dee2aaSAndroid Build Coastguard Worker         if (!surface) {
272*c8dee2aaSAndroid Build Coastguard Worker             SkDebugf("Could not allocate a %d x %d surface.\n", FLAGS_width, FLAGS_height);
273*c8dee2aaSAndroid Build Coastguard Worker             return nullptr;
274*c8dee2aaSAndroid Build Coastguard Worker         }
275*c8dee2aaSAndroid Build Coastguard Worker 
276*c8dee2aaSAndroid Build Coastguard Worker         return std::unique_ptr<FrameGenerator>(new CPUGenerator(sink, std::move(surface), matrix));
277*c8dee2aaSAndroid Build Coastguard Worker     }
278*c8dee2aaSAndroid Build Coastguard Worker 
279*c8dee2aaSAndroid Build Coastguard Worker     void generateFrame(const skottie::Animation* anim, size_t frame_index) override {
280*c8dee2aaSAndroid Build Coastguard Worker         fSurface->getCanvas()->clear(kClearColor);
281*c8dee2aaSAndroid Build Coastguard Worker         anim->render(fSurface->getCanvas());
282*c8dee2aaSAndroid Build Coastguard Worker 
283*c8dee2aaSAndroid Build Coastguard Worker         fSink->writeFrame(fSurface->makeImageSnapshot(), frame_index);
284*c8dee2aaSAndroid Build Coastguard Worker     }
285*c8dee2aaSAndroid Build Coastguard Worker 
286*c8dee2aaSAndroid Build Coastguard Worker private:
287*c8dee2aaSAndroid Build Coastguard Worker     CPUGenerator(FrameSink* sink, sk_sp<SkSurface> surface, const SkMatrix& scale_matrix)
288*c8dee2aaSAndroid Build Coastguard Worker         : FrameGenerator(sink)
289*c8dee2aaSAndroid Build Coastguard Worker         , fSurface(std::move(surface))
290*c8dee2aaSAndroid Build Coastguard Worker     {
291*c8dee2aaSAndroid Build Coastguard Worker         fSurface->getCanvas()->concat(scale_matrix);
292*c8dee2aaSAndroid Build Coastguard Worker     }
293*c8dee2aaSAndroid Build Coastguard Worker 
294*c8dee2aaSAndroid Build Coastguard Worker     const sk_sp<SkSurface> fSurface;
295*c8dee2aaSAndroid Build Coastguard Worker #endif // !GPU_ONLY
296*c8dee2aaSAndroid Build Coastguard Worker };
297*c8dee2aaSAndroid Build Coastguard Worker 
298*c8dee2aaSAndroid Build Coastguard Worker class SKPGenerator final : public FrameGenerator {
299*c8dee2aaSAndroid Build Coastguard Worker public:
300*c8dee2aaSAndroid Build Coastguard Worker #if defined(CPU_ONLY) || defined(GPU_ONLY)
Make(FrameSink * sink,const SkMatrix & matrix)301*c8dee2aaSAndroid Build Coastguard Worker     static std::unique_ptr<FrameGenerator> Make(FrameSink* sink, const SkMatrix& matrix) {
302*c8dee2aaSAndroid Build Coastguard Worker         return nullptr;
303*c8dee2aaSAndroid Build Coastguard Worker     }
304*c8dee2aaSAndroid Build Coastguard Worker #else
305*c8dee2aaSAndroid Build Coastguard Worker     static std::unique_ptr<FrameGenerator> Make(FrameSink* sink, const SkMatrix& scale_matrix) {
306*c8dee2aaSAndroid Build Coastguard Worker         return std::unique_ptr<FrameGenerator>(new SKPGenerator(sink, scale_matrix));
307*c8dee2aaSAndroid Build Coastguard Worker     }
308*c8dee2aaSAndroid Build Coastguard Worker 
309*c8dee2aaSAndroid Build Coastguard Worker     void generateFrame(const skottie::Animation* anim, size_t frame_index) override {
310*c8dee2aaSAndroid Build Coastguard Worker         auto* canvas = fRecorder.beginRecording(FLAGS_width, FLAGS_height);
311*c8dee2aaSAndroid Build Coastguard Worker         canvas->concat(fScaleMatrix);
312*c8dee2aaSAndroid Build Coastguard Worker         anim->render(canvas);
313*c8dee2aaSAndroid Build Coastguard Worker 
314*c8dee2aaSAndroid Build Coastguard Worker         auto frame  = fRecorder.finishRecordingAsPicture();
315*c8dee2aaSAndroid Build Coastguard Worker         auto stream = make_file_stream(frame_index, "skp");
316*c8dee2aaSAndroid Build Coastguard Worker 
317*c8dee2aaSAndroid Build Coastguard Worker         if (frame && stream) {
318*c8dee2aaSAndroid Build Coastguard Worker             SkSerialProcs sProcs;
319*c8dee2aaSAndroid Build Coastguard Worker             sProcs.fImageProc = [](SkImage* img, void*) -> sk_sp<SkData> {
320*c8dee2aaSAndroid Build Coastguard Worker                 return SkPngEncoder::Encode(as_IB(img)->directContext(), img,
321*c8dee2aaSAndroid Build Coastguard Worker                                             SkPngEncoder::Options{});
322*c8dee2aaSAndroid Build Coastguard Worker             };
323*c8dee2aaSAndroid Build Coastguard Worker             frame->serialize(stream.get(), &sProcs);
324*c8dee2aaSAndroid Build Coastguard Worker         }
325*c8dee2aaSAndroid Build Coastguard Worker     }
326*c8dee2aaSAndroid Build Coastguard Worker 
327*c8dee2aaSAndroid Build Coastguard Worker private:
328*c8dee2aaSAndroid Build Coastguard Worker     SKPGenerator(FrameSink* sink, const SkMatrix& scale_matrix)
329*c8dee2aaSAndroid Build Coastguard Worker         : FrameGenerator(sink)
330*c8dee2aaSAndroid Build Coastguard Worker         , fScaleMatrix(scale_matrix)
331*c8dee2aaSAndroid Build Coastguard Worker     {}
332*c8dee2aaSAndroid Build Coastguard Worker 
333*c8dee2aaSAndroid Build Coastguard Worker     const SkMatrix    fScaleMatrix;
334*c8dee2aaSAndroid Build Coastguard Worker     SkPictureRecorder fRecorder;
335*c8dee2aaSAndroid Build Coastguard Worker #endif // !CPU_ONLY && !GPU_ONLY
336*c8dee2aaSAndroid Build Coastguard Worker };
337*c8dee2aaSAndroid Build Coastguard Worker 
338*c8dee2aaSAndroid Build Coastguard Worker class GPUGenerator final : public FrameGenerator {
339*c8dee2aaSAndroid Build Coastguard Worker public:
340*c8dee2aaSAndroid Build Coastguard Worker #if defined(CPU_ONLY)
Make(FrameSink * sink,const SkMatrix & matrix)341*c8dee2aaSAndroid Build Coastguard Worker     static std::unique_ptr<FrameGenerator> Make(FrameSink* sink, const SkMatrix& matrix) {
342*c8dee2aaSAndroid Build Coastguard Worker         return nullptr;
343*c8dee2aaSAndroid Build Coastguard Worker     }
344*c8dee2aaSAndroid Build Coastguard Worker #else
345*c8dee2aaSAndroid Build Coastguard Worker     static std::unique_ptr<FrameGenerator> Make(FrameSink* sink, const SkMatrix& matrix) {
346*c8dee2aaSAndroid Build Coastguard Worker         auto gpu_generator = std::unique_ptr<GPUGenerator>(new GPUGenerator(sink, matrix));
347*c8dee2aaSAndroid Build Coastguard Worker 
348*c8dee2aaSAndroid Build Coastguard Worker         return gpu_generator->isValid()
349*c8dee2aaSAndroid Build Coastguard Worker                 ? std::unique_ptr<FrameGenerator>(gpu_generator.release())
350*c8dee2aaSAndroid Build Coastguard Worker                 : nullptr;
351*c8dee2aaSAndroid Build Coastguard Worker     }
352*c8dee2aaSAndroid Build Coastguard Worker 
353*c8dee2aaSAndroid Build Coastguard Worker     ~GPUGenerator() override {
354*c8dee2aaSAndroid Build Coastguard Worker         // ensure all pending reads are completed
355*c8dee2aaSAndroid Build Coastguard Worker         fCtx->flushAndSubmit(GrSyncCpu::kYes);
356*c8dee2aaSAndroid Build Coastguard Worker     }
357*c8dee2aaSAndroid Build Coastguard Worker 
358*c8dee2aaSAndroid Build Coastguard Worker     void generateFrame(const skottie::Animation* anim, size_t frame_index) override {
359*c8dee2aaSAndroid Build Coastguard Worker         fSurface->getCanvas()->clear(kClearColor);
360*c8dee2aaSAndroid Build Coastguard Worker         anim->render(fSurface->getCanvas());
361*c8dee2aaSAndroid Build Coastguard Worker 
362*c8dee2aaSAndroid Build Coastguard Worker         auto rec = std::make_unique<AsyncRec>(fSink, frame_index);
363*c8dee2aaSAndroid Build Coastguard Worker         fSurface->asyncRescaleAndReadPixels(SkImageInfo::MakeN32Premul(FLAGS_width, FLAGS_height),
364*c8dee2aaSAndroid Build Coastguard Worker                                             {0, 0, FLAGS_width, FLAGS_height},
365*c8dee2aaSAndroid Build Coastguard Worker                                             SkSurface::RescaleGamma::kSrc,
366*c8dee2aaSAndroid Build Coastguard Worker                                             SkImage::RescaleMode::kNearest,
367*c8dee2aaSAndroid Build Coastguard Worker                                             AsyncCallback, rec.release());
368*c8dee2aaSAndroid Build Coastguard Worker 
369*c8dee2aaSAndroid Build Coastguard Worker         fCtx->submit();
370*c8dee2aaSAndroid Build Coastguard Worker     }
371*c8dee2aaSAndroid Build Coastguard Worker 
372*c8dee2aaSAndroid Build Coastguard Worker private:
373*c8dee2aaSAndroid Build Coastguard Worker     GPUGenerator(FrameSink* sink, const SkMatrix& matrix)
374*c8dee2aaSAndroid Build Coastguard Worker         : FrameGenerator(sink)
375*c8dee2aaSAndroid Build Coastguard Worker     {
376*c8dee2aaSAndroid Build Coastguard Worker         fCtx = fFactory.getContextInfo(skgpu::ContextType::kGL).directContext();
377*c8dee2aaSAndroid Build Coastguard Worker         fSurface = SkSurfaces::RenderTarget(fCtx,
378*c8dee2aaSAndroid Build Coastguard Worker                                             skgpu::Budgeted::kNo,
379*c8dee2aaSAndroid Build Coastguard Worker                                             SkImageInfo::MakeN32Premul(FLAGS_width, FLAGS_height),
380*c8dee2aaSAndroid Build Coastguard Worker                                             0,
381*c8dee2aaSAndroid Build Coastguard Worker                                             GrSurfaceOrigin::kTopLeft_GrSurfaceOrigin,
382*c8dee2aaSAndroid Build Coastguard Worker                                             nullptr);
383*c8dee2aaSAndroid Build Coastguard Worker         if (fSurface) {
384*c8dee2aaSAndroid Build Coastguard Worker             fSurface->getCanvas()->concat(matrix);
385*c8dee2aaSAndroid Build Coastguard Worker         } else {
386*c8dee2aaSAndroid Build Coastguard Worker             fprintf(stderr, "Could not initialize GL context.\n");
387*c8dee2aaSAndroid Build Coastguard Worker         }
388*c8dee2aaSAndroid Build Coastguard Worker     }
389*c8dee2aaSAndroid Build Coastguard Worker 
390*c8dee2aaSAndroid Build Coastguard Worker     bool isValid() const { return !!fSurface; }
391*c8dee2aaSAndroid Build Coastguard Worker 
392*c8dee2aaSAndroid Build Coastguard Worker     struct AsyncRec {
393*c8dee2aaSAndroid Build Coastguard Worker         FrameSink* sink;
394*c8dee2aaSAndroid Build Coastguard Worker         size_t     index;
395*c8dee2aaSAndroid Build Coastguard Worker 
396*c8dee2aaSAndroid Build Coastguard Worker         AsyncRec(FrameSink* sink, size_t index) : sink(sink), index(index) {}
397*c8dee2aaSAndroid Build Coastguard Worker     };
398*c8dee2aaSAndroid Build Coastguard Worker 
399*c8dee2aaSAndroid Build Coastguard Worker     static void AsyncCallback(SkSurface::ReadPixelsContext ctx,
400*c8dee2aaSAndroid Build Coastguard Worker                               std::unique_ptr<const SkSurface::AsyncReadResult> result) {
401*c8dee2aaSAndroid Build Coastguard Worker         std::unique_ptr<const AsyncRec> rec(reinterpret_cast<const AsyncRec*>(ctx));
402*c8dee2aaSAndroid Build Coastguard Worker         if (result && result->count() == 1) {
403*c8dee2aaSAndroid Build Coastguard Worker             SkPixmap pm(SkImageInfo::MakeN32Premul(FLAGS_width, FLAGS_height),
404*c8dee2aaSAndroid Build Coastguard Worker                         result->data(0), result->rowBytes(0));
405*c8dee2aaSAndroid Build Coastguard Worker 
406*c8dee2aaSAndroid Build Coastguard Worker             auto release_proc = [](const void*, SkImages::ReleaseContext ctx) {
407*c8dee2aaSAndroid Build Coastguard Worker                 std::unique_ptr<const SkSurface::AsyncReadResult>
408*c8dee2aaSAndroid Build Coastguard Worker                         adopted(reinterpret_cast<const SkSurface::AsyncReadResult*>(ctx));
409*c8dee2aaSAndroid Build Coastguard Worker             };
410*c8dee2aaSAndroid Build Coastguard Worker 
411*c8dee2aaSAndroid Build Coastguard Worker             auto frame_image =
412*c8dee2aaSAndroid Build Coastguard Worker                     SkImages::RasterFromPixmap(pm, release_proc, (void*)result.release());
413*c8dee2aaSAndroid Build Coastguard Worker 
414*c8dee2aaSAndroid Build Coastguard Worker             rec->sink->writeFrame(std::move(frame_image), rec->index);
415*c8dee2aaSAndroid Build Coastguard Worker         }
416*c8dee2aaSAndroid Build Coastguard Worker     }
417*c8dee2aaSAndroid Build Coastguard Worker 
418*c8dee2aaSAndroid Build Coastguard Worker     sk_gpu_test::GrContextFactory fFactory;
419*c8dee2aaSAndroid Build Coastguard Worker     GrDirectContext*              fCtx;
420*c8dee2aaSAndroid Build Coastguard Worker     sk_sp<SkSurface>              fSurface;
421*c8dee2aaSAndroid Build Coastguard Worker #endif // !CPU_ONLY
422*c8dee2aaSAndroid Build Coastguard Worker };
423*c8dee2aaSAndroid Build Coastguard Worker 
Make(FrameSink * sink,OutputFormat fmt,const SkMatrix & matrix)424*c8dee2aaSAndroid Build Coastguard Worker std::unique_ptr<FrameGenerator> FrameGenerator::Make(FrameSink* sink,
425*c8dee2aaSAndroid Build Coastguard Worker                                                      OutputFormat fmt,
426*c8dee2aaSAndroid Build Coastguard Worker                                                      const SkMatrix& matrix) {
427*c8dee2aaSAndroid Build Coastguard Worker     if (fmt == OutputFormat::kSKP) {
428*c8dee2aaSAndroid Build Coastguard Worker         return SKPGenerator::Make(sink, matrix);
429*c8dee2aaSAndroid Build Coastguard Worker     }
430*c8dee2aaSAndroid Build Coastguard Worker 
431*c8dee2aaSAndroid Build Coastguard Worker     return FLAGS_gpu
432*c8dee2aaSAndroid Build Coastguard Worker             ? GPUGenerator::Make(sink, matrix)
433*c8dee2aaSAndroid Build Coastguard Worker             : CPUGenerator::Make(sink, matrix);
434*c8dee2aaSAndroid Build Coastguard Worker }
435*c8dee2aaSAndroid Build Coastguard Worker 
436*c8dee2aaSAndroid Build Coastguard Worker class Logger final : public skottie::Logger {
437*c8dee2aaSAndroid Build Coastguard Worker public:
438*c8dee2aaSAndroid Build Coastguard Worker     struct LogEntry {
439*c8dee2aaSAndroid Build Coastguard Worker         SkString fMessage,
440*c8dee2aaSAndroid Build Coastguard Worker                  fJSON;
441*c8dee2aaSAndroid Build Coastguard Worker     };
442*c8dee2aaSAndroid Build Coastguard Worker 
log(skottie::Logger::Level lvl,const char message[],const char json[])443*c8dee2aaSAndroid Build Coastguard Worker     void log(skottie::Logger::Level lvl, const char message[], const char json[]) override {
444*c8dee2aaSAndroid Build Coastguard Worker         auto& log = lvl == skottie::Logger::Level::kError ? fErrors : fWarnings;
445*c8dee2aaSAndroid Build Coastguard Worker         log.push_back({ SkString(message), json ? SkString(json) : SkString() });
446*c8dee2aaSAndroid Build Coastguard Worker     }
447*c8dee2aaSAndroid Build Coastguard Worker 
report() const448*c8dee2aaSAndroid Build Coastguard Worker     void report() const {
449*c8dee2aaSAndroid Build Coastguard Worker         SkDebugf("Animation loaded with %zu error%s, %zu warning%s.\n",
450*c8dee2aaSAndroid Build Coastguard Worker                  fErrors.size(), fErrors.size() == 1 ? "" : "s",
451*c8dee2aaSAndroid Build Coastguard Worker                  fWarnings.size(), fWarnings.size() == 1 ? "" : "s");
452*c8dee2aaSAndroid Build Coastguard Worker 
453*c8dee2aaSAndroid Build Coastguard Worker         const auto& show = [](const LogEntry& log, const char prefix[]) {
454*c8dee2aaSAndroid Build Coastguard Worker             SkDebugf("%s%s", prefix, log.fMessage.c_str());
455*c8dee2aaSAndroid Build Coastguard Worker             if (!log.fJSON.isEmpty())
456*c8dee2aaSAndroid Build Coastguard Worker                 SkDebugf(" : %s", log.fJSON.c_str());
457*c8dee2aaSAndroid Build Coastguard Worker             SkDebugf("\n");
458*c8dee2aaSAndroid Build Coastguard Worker         };
459*c8dee2aaSAndroid Build Coastguard Worker 
460*c8dee2aaSAndroid Build Coastguard Worker         for (const auto& err : fErrors)   show(err, "  !! ");
461*c8dee2aaSAndroid Build Coastguard Worker         for (const auto& wrn : fWarnings) show(wrn, "  ?? ");
462*c8dee2aaSAndroid Build Coastguard Worker     }
463*c8dee2aaSAndroid Build Coastguard Worker 
464*c8dee2aaSAndroid Build Coastguard Worker private:
465*c8dee2aaSAndroid Build Coastguard Worker     std::vector<LogEntry> fErrors,
466*c8dee2aaSAndroid Build Coastguard Worker                           fWarnings;
467*c8dee2aaSAndroid Build Coastguard Worker };
468*c8dee2aaSAndroid Build Coastguard Worker 
469*c8dee2aaSAndroid Build Coastguard Worker } // namespace
470*c8dee2aaSAndroid Build Coastguard Worker 
471*c8dee2aaSAndroid Build Coastguard Worker extern bool gSkUseThreadLocalStrikeCaches_IAcknowledgeThisIsIncrediblyExperimental;
472*c8dee2aaSAndroid Build Coastguard Worker 
main(int argc,char ** argv)473*c8dee2aaSAndroid Build Coastguard Worker int main(int argc, char** argv) {
474*c8dee2aaSAndroid Build Coastguard Worker     gSkUseThreadLocalStrikeCaches_IAcknowledgeThisIsIncrediblyExperimental = true;
475*c8dee2aaSAndroid Build Coastguard Worker     CommandLineFlags::Parse(argc, argv);
476*c8dee2aaSAndroid Build Coastguard Worker     SkGraphics::Init();
477*c8dee2aaSAndroid Build Coastguard Worker 
478*c8dee2aaSAndroid Build Coastguard Worker     if (FLAGS_input.isEmpty() || FLAGS_writePath.isEmpty()) {
479*c8dee2aaSAndroid Build Coastguard Worker         SkDebugf("Missing required 'input' and 'writePath' args.\n");
480*c8dee2aaSAndroid Build Coastguard Worker         return 1;
481*c8dee2aaSAndroid Build Coastguard Worker     }
482*c8dee2aaSAndroid Build Coastguard Worker 
483*c8dee2aaSAndroid Build Coastguard Worker     OutputFormat fmt;
484*c8dee2aaSAndroid Build Coastguard Worker     if (0 == std::strcmp(FLAGS_format[0], "png")) {
485*c8dee2aaSAndroid Build Coastguard Worker         fmt = OutputFormat::kPNG;
486*c8dee2aaSAndroid Build Coastguard Worker     } else if (0 == std::strcmp(FLAGS_format[0], "skp")) {
487*c8dee2aaSAndroid Build Coastguard Worker         fmt = OutputFormat::kSKP;
488*c8dee2aaSAndroid Build Coastguard Worker     } else if (0 == std::strcmp(FLAGS_format[0], "null")) {
489*c8dee2aaSAndroid Build Coastguard Worker         fmt = OutputFormat::kNull;
490*c8dee2aaSAndroid Build Coastguard Worker #if defined(HAVE_VIDEO_ENCODER)
491*c8dee2aaSAndroid Build Coastguard Worker     } else if (0 == std::strcmp(FLAGS_format[0], "mp4")) {
492*c8dee2aaSAndroid Build Coastguard Worker         fmt = OutputFormat::kMP4;
493*c8dee2aaSAndroid Build Coastguard Worker #endif
494*c8dee2aaSAndroid Build Coastguard Worker     } else {
495*c8dee2aaSAndroid Build Coastguard Worker         fprintf(stderr, "Unknown format: %s\n", FLAGS_format[0]);
496*c8dee2aaSAndroid Build Coastguard Worker         return 1;
497*c8dee2aaSAndroid Build Coastguard Worker     }
498*c8dee2aaSAndroid Build Coastguard Worker 
499*c8dee2aaSAndroid Build Coastguard Worker     if (fmt != OutputFormat::kMP4 && !sk_mkdir(FLAGS_writePath[0])) {
500*c8dee2aaSAndroid Build Coastguard Worker         return 1;
501*c8dee2aaSAndroid Build Coastguard Worker     }
502*c8dee2aaSAndroid Build Coastguard Worker 
503*c8dee2aaSAndroid Build Coastguard Worker     SkCodecs::Register(SkPngDecoder::Decoder());
504*c8dee2aaSAndroid Build Coastguard Worker     SkCodecs::Register(SkJpegDecoder::Decoder());
505*c8dee2aaSAndroid Build Coastguard Worker     SkCodecs::Register(SkWebpDecoder::Decoder());
506*c8dee2aaSAndroid Build Coastguard Worker 
507*c8dee2aaSAndroid Build Coastguard Worker     // If necessary, clients should use a font manager that would load fonts from the system.
508*c8dee2aaSAndroid Build Coastguard Worker #if defined(SK_BUILD_FOR_MAC) && defined(SK_FONTMGR_CORETEXT_AVAILABLE)
509*c8dee2aaSAndroid Build Coastguard Worker     sk_sp<SkFontMgr> fontMgr = SkFontMgr_New_CoreText(nullptr);
510*c8dee2aaSAndroid Build Coastguard Worker #elif defined(SK_BUILD_FOR_ANDROID) && defined(SK_FONTMGR_ANDROID_AVAILABLE) && defined(SK_TYPEFACE_FACTORY_FREETYPE)
511*c8dee2aaSAndroid Build Coastguard Worker     sk_sp<SkFontMgr> fontMgr = SkFontMgr_New_Android(nullptr, SkFontScanner_Make_FreeType());
512*c8dee2aaSAndroid Build Coastguard Worker #elif defined(SK_BUILD_FOR_UNIX) && defined(SK_FONTMGR_FONTCONFIG_AVAILABLE) && defined(SK_TYPEFACE_FACTORY_FREETYPE)
513*c8dee2aaSAndroid Build Coastguard Worker     sk_sp<SkFontMgr> fontMgr = SkFontMgr_New_FontConfig(nullptr, SkFontScanner_Make_FreeType());
514*c8dee2aaSAndroid Build Coastguard Worker #else
515*c8dee2aaSAndroid Build Coastguard Worker     sk_sp<SkFontMgr> fontMgr = SkFontMgr_New_Custom_Empty();
516*c8dee2aaSAndroid Build Coastguard Worker #endif
517*c8dee2aaSAndroid Build Coastguard Worker 
518*c8dee2aaSAndroid Build Coastguard Worker     auto predecode = skresources::ImageDecodeStrategy::kPreDecode;
519*c8dee2aaSAndroid Build Coastguard Worker     auto logger = sk_make_sp<Logger>();
520*c8dee2aaSAndroid Build Coastguard Worker     auto rp = skresources::CachingResourceProvider::Make(
521*c8dee2aaSAndroid Build Coastguard Worker             skresources::DataURIResourceProviderProxy::Make(
522*c8dee2aaSAndroid Build Coastguard Worker                     skresources::FileResourceProvider::Make(SkOSPath::Dirname(FLAGS_input[0]),
523*c8dee2aaSAndroid Build Coastguard Worker                                                             predecode),
524*c8dee2aaSAndroid Build Coastguard Worker                     predecode,
525*c8dee2aaSAndroid Build Coastguard Worker                     fontMgr));
526*c8dee2aaSAndroid Build Coastguard Worker     auto data   = SkData::MakeFromFileName(FLAGS_input[0]);
527*c8dee2aaSAndroid Build Coastguard Worker     auto precomp_interceptor =
528*c8dee2aaSAndroid Build Coastguard Worker             sk_make_sp<skottie_utils::ExternalAnimationPrecompInterceptor>(rp, "__");
529*c8dee2aaSAndroid Build Coastguard Worker 
530*c8dee2aaSAndroid Build Coastguard Worker     if (!data) {
531*c8dee2aaSAndroid Build Coastguard Worker         SkDebugf("Could not load %s.\n", FLAGS_input[0]);
532*c8dee2aaSAndroid Build Coastguard Worker         return 1;
533*c8dee2aaSAndroid Build Coastguard Worker     }
534*c8dee2aaSAndroid Build Coastguard Worker 
535*c8dee2aaSAndroid Build Coastguard Worker     const auto build_animation = [&fontMgr, &rp, &data](
536*c8dee2aaSAndroid Build Coastguard Worker             const sk_sp<Logger>& logger,
537*c8dee2aaSAndroid Build Coastguard Worker             const sk_sp<skottie::PrecompInterceptor>& pint) {
538*c8dee2aaSAndroid Build Coastguard Worker         return skottie::Animation::Builder()
539*c8dee2aaSAndroid Build Coastguard Worker             .setFontManager(fontMgr)
540*c8dee2aaSAndroid Build Coastguard Worker             .setLogger(logger)
541*c8dee2aaSAndroid Build Coastguard Worker             .setPrecompInterceptor(pint)
542*c8dee2aaSAndroid Build Coastguard Worker             .setResourceProvider(rp)
543*c8dee2aaSAndroid Build Coastguard Worker             .setTextShapingFactory(SkShapers::BestAvailable())
544*c8dee2aaSAndroid Build Coastguard Worker             .make(static_cast<const char*>(data->data()), data->size());
545*c8dee2aaSAndroid Build Coastguard Worker     };
546*c8dee2aaSAndroid Build Coastguard Worker 
547*c8dee2aaSAndroid Build Coastguard Worker     // Instantiate an animation on the main thread for two reasons:
548*c8dee2aaSAndroid Build Coastguard Worker     //   - we need to know its duration upfront
549*c8dee2aaSAndroid Build Coastguard Worker     //   - we want to only report parsing errors once
550*c8dee2aaSAndroid Build Coastguard Worker     const auto anim = build_animation(logger, nullptr);
551*c8dee2aaSAndroid Build Coastguard Worker     if (!anim) {
552*c8dee2aaSAndroid Build Coastguard Worker         SkDebugf("Could not parse animation: '%s'.\n", FLAGS_input[0]);
553*c8dee2aaSAndroid Build Coastguard Worker         return 1;
554*c8dee2aaSAndroid Build Coastguard Worker     }
555*c8dee2aaSAndroid Build Coastguard Worker 
556*c8dee2aaSAndroid Build Coastguard Worker     const auto scale_matrix = SkMatrix::RectToRect(SkRect::MakeSize(anim->size()),
557*c8dee2aaSAndroid Build Coastguard Worker                                                    SkRect::MakeIWH(FLAGS_width, FLAGS_height),
558*c8dee2aaSAndroid Build Coastguard Worker                                                    SkMatrix::kCenter_ScaleToFit);
559*c8dee2aaSAndroid Build Coastguard Worker     logger->report();
560*c8dee2aaSAndroid Build Coastguard Worker 
561*c8dee2aaSAndroid Build Coastguard Worker     const auto t0 = SkTPin(FLAGS_t0, 0.0, 1.0),
562*c8dee2aaSAndroid Build Coastguard Worker                t1 = SkTPin(FLAGS_t1,  t0, 1.0),
563*c8dee2aaSAndroid Build Coastguard Worker        native_fps = anim->fps(),
564*c8dee2aaSAndroid Build Coastguard Worker            frame0 = anim->duration() * t0 * native_fps,
565*c8dee2aaSAndroid Build Coastguard Worker          duration = anim->duration() * (t1 - t0);
566*c8dee2aaSAndroid Build Coastguard Worker 
567*c8dee2aaSAndroid Build Coastguard Worker     double fps = FLAGS_fps > 0 ? FLAGS_fps : native_fps;
568*c8dee2aaSAndroid Build Coastguard Worker     if (fps <= 0) {
569*c8dee2aaSAndroid Build Coastguard Worker         SkDebugf("Invalid fps: %f.\n", fps);
570*c8dee2aaSAndroid Build Coastguard Worker         return 1;
571*c8dee2aaSAndroid Build Coastguard Worker     }
572*c8dee2aaSAndroid Build Coastguard Worker 
573*c8dee2aaSAndroid Build Coastguard Worker     auto frame_count = static_cast<int>(duration * fps);
574*c8dee2aaSAndroid Build Coastguard Worker     static constexpr int kMaxFrames = 10000;
575*c8dee2aaSAndroid Build Coastguard Worker     if (frame_count > kMaxFrames) {
576*c8dee2aaSAndroid Build Coastguard Worker         frame_count = kMaxFrames;
577*c8dee2aaSAndroid Build Coastguard Worker         fps = frame_count / duration;
578*c8dee2aaSAndroid Build Coastguard Worker     }
579*c8dee2aaSAndroid Build Coastguard Worker     const auto fps_scale = native_fps / fps;
580*c8dee2aaSAndroid Build Coastguard Worker 
581*c8dee2aaSAndroid Build Coastguard Worker     printf("Rendering %f seconds (%d frames @%f fps).\n", duration, frame_count, fps);
582*c8dee2aaSAndroid Build Coastguard Worker 
583*c8dee2aaSAndroid Build Coastguard Worker     const auto sink = FrameSink::Make(fmt, frame_count);
584*c8dee2aaSAndroid Build Coastguard Worker 
585*c8dee2aaSAndroid Build Coastguard Worker     std::vector<double> frames_ms(frame_count);
586*c8dee2aaSAndroid Build Coastguard Worker 
587*c8dee2aaSAndroid Build Coastguard Worker     const auto thread_count = FLAGS_gpu ? 0 : FLAGS_threads - 1;
588*c8dee2aaSAndroid Build Coastguard Worker     SkTaskGroup::Enabler enabler(thread_count);
589*c8dee2aaSAndroid Build Coastguard Worker 
590*c8dee2aaSAndroid Build Coastguard Worker     SkTaskGroup tg;
591*c8dee2aaSAndroid Build Coastguard Worker     {
592*c8dee2aaSAndroid Build Coastguard Worker         // Depending on type (gpu vs. everything else), we use either a single generator
593*c8dee2aaSAndroid Build Coastguard Worker         // or one generator per worker thread, respectively.
594*c8dee2aaSAndroid Build Coastguard Worker         // Scoping is important for the single generator case because we want its destructor to
595*c8dee2aaSAndroid Build Coastguard Worker         // flush out any pending async operations.
596*c8dee2aaSAndroid Build Coastguard Worker         std::unique_ptr<FrameGenerator> singleton_generator;
597*c8dee2aaSAndroid Build Coastguard Worker         if (FLAGS_gpu) {
598*c8dee2aaSAndroid Build Coastguard Worker             singleton_generator = FrameGenerator::Make(sink.get(), fmt, scale_matrix);
599*c8dee2aaSAndroid Build Coastguard Worker         }
600*c8dee2aaSAndroid Build Coastguard Worker 
601*c8dee2aaSAndroid Build Coastguard Worker         tg.batch(frame_count, [&](int i) {
602*c8dee2aaSAndroid Build Coastguard Worker             // SkTaskGroup::Enabler creates a LIFO work pool,
603*c8dee2aaSAndroid Build Coastguard Worker             // but we want our early frames to start first.
604*c8dee2aaSAndroid Build Coastguard Worker             i = frame_count - 1 - i;
605*c8dee2aaSAndroid Build Coastguard Worker 
606*c8dee2aaSAndroid Build Coastguard Worker             const auto start = std::chrono::steady_clock::now();
607*c8dee2aaSAndroid Build Coastguard Worker             thread_local static auto* anim =
608*c8dee2aaSAndroid Build Coastguard Worker                     build_animation(nullptr, precomp_interceptor).release();
609*c8dee2aaSAndroid Build Coastguard Worker             thread_local static auto* gen = singleton_generator
610*c8dee2aaSAndroid Build Coastguard Worker                     ? singleton_generator.get()
611*c8dee2aaSAndroid Build Coastguard Worker                     : FrameGenerator::Make(sink.get(), fmt, scale_matrix).release();
612*c8dee2aaSAndroid Build Coastguard Worker 
613*c8dee2aaSAndroid Build Coastguard Worker             if (gen && anim) {
614*c8dee2aaSAndroid Build Coastguard Worker                 anim->seekFrame(frame0 + i * fps_scale);
615*c8dee2aaSAndroid Build Coastguard Worker                 gen->generateFrame(anim, SkToSizeT(i));
616*c8dee2aaSAndroid Build Coastguard Worker             } else {
617*c8dee2aaSAndroid Build Coastguard Worker                 sink->writeFrame(nullptr, SkToSizeT(i));
618*c8dee2aaSAndroid Build Coastguard Worker             }
619*c8dee2aaSAndroid Build Coastguard Worker 
620*c8dee2aaSAndroid Build Coastguard Worker             frames_ms[i] = ms_since(start);
621*c8dee2aaSAndroid Build Coastguard Worker         });
622*c8dee2aaSAndroid Build Coastguard Worker     }
623*c8dee2aaSAndroid Build Coastguard Worker 
624*c8dee2aaSAndroid Build Coastguard Worker     sink->finalize(fps);
625*c8dee2aaSAndroid Build Coastguard Worker     tg.wait();
626*c8dee2aaSAndroid Build Coastguard Worker 
627*c8dee2aaSAndroid Build Coastguard Worker 
628*c8dee2aaSAndroid Build Coastguard Worker     std::sort(frames_ms.begin(), frames_ms.end());
629*c8dee2aaSAndroid Build Coastguard Worker     double sum = std::accumulate(frames_ms.begin(), frames_ms.end(), 0);
630*c8dee2aaSAndroid Build Coastguard Worker     printf("Frame time stats: min %gms, med %gms, avg %gms, max %gms, sum %gms\n",
631*c8dee2aaSAndroid Build Coastguard Worker            frames_ms[0], frames_ms[frame_count/2], sum/frame_count, frames_ms.back(), sum);
632*c8dee2aaSAndroid Build Coastguard Worker 
633*c8dee2aaSAndroid Build Coastguard Worker     return 0;
634*c8dee2aaSAndroid Build Coastguard Worker }
635