Flutter Engine
The Flutter Engine
Loading...
Searching...
No Matches
SkottieTool.cpp
Go to the documentation of this file.
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
14#include "include/core/SkData.h"
21#include "include/core/SkRect.h"
36#include "src/core/SkOSFile.h"
38#include "src/utils/SkOSPath.h"
40
41#if !defined(CPU_ONLY)
44#include "include/gpu/GrTypes.h"
48#endif
49
50#if !defined(CPU_ONLY) && !defined(GPU_ONLY)
55#endif
56
57#include <algorithm>
58#include <chrono>
59#include <cstdio>
60#include <cstring>
61#include <functional>
62#include <memory>
63#include <numeric>
64#include <utility>
65#include <vector>
66
67#if defined(HAVE_VIDEO_ENCODER)
68 #include <future>
70 const char* formats_help = "Output format (png, skp, mp4, or null)";
71#else
72 const char* formats_help = "Output format (png, skp, or null)";
73#endif
74
75#if defined(SK_BUILD_FOR_MAC) && defined(SK_FONTMGR_CORETEXT_AVAILABLE)
77#elif defined(SK_BUILD_FOR_ANDROID) && defined(SK_FONTMGR_ANDROID_AVAILABLE)
80#elif defined(SK_BUILD_FOR_UNIX) && defined(SK_FONTMGR_FONTCONFIG_AVAILABLE)
82#else
84#endif
85
86static DEFINE_string2(input , i, nullptr, "Input .json file.");
87static DEFINE_string2(writePath, w, nullptr, "Output directory. Frames are names [0-9]{6}.png.");
88static DEFINE_string2(format , f, "png" , formats_help);
89
90static DEFINE_double(t0, 0, "Timeline start [0..1].");
91static DEFINE_double(t1, 1, "Timeline stop [0..1].");
92static DEFINE_double(fps, 0, "Decode frames per second (default is animation native fps).");
93
94static DEFINE_int(width , 800, "Render width.");
95static DEFINE_int(height, 600, "Render height.");
96static DEFINE_int(threads, 0, "Number of worker threads (0 -> cores count).");
97
98static DEFINE_bool2(gpu, g, false, "Enable GPU rasterization.");
99
100namespace {
101
102static constexpr SkColor kClearColor = SK_ColorWHITE;
103
104enum class OutputFormat {
105 kPNG,
106 kSKP,
107 kNull,
108 kMP4,
109};
110
111
112auto ms_since(std::chrono::steady_clock::time_point start) {
113 const auto elapsed = std::chrono::steady_clock::now() - start;
114 return std::chrono::duration_cast<std::chrono::milliseconds>(elapsed).count();
115}
116
117std::unique_ptr<SkFILEWStream> make_file_stream(size_t frame_index, const char* extension) {
118 const auto file = SkStringPrintf("0%06zu.%s", frame_index, extension);
119 const auto path = SkOSPath::Join(FLAGS_writePath[0], file.c_str());
120
121 auto stream = std::make_unique<SkFILEWStream>(path.c_str());
122
123 return stream->isValid() ? std::move(stream) : nullptr;
124}
125
126class FrameSink {
127public:
128 virtual ~FrameSink() = default;
129
130 static std::unique_ptr<FrameSink> Make(OutputFormat fmt, size_t frame_count);
131
132 virtual void writeFrame(sk_sp<SkImage> frame, size_t frame_index) = 0;
133
134 virtual void finalize(double fps) {}
135
136protected:
137 FrameSink() = default;
138
139private:
140 FrameSink(const FrameSink&) = delete;
141 FrameSink& operator=(const FrameSink&) = delete;
142};
143
144class PNGSink final : public FrameSink {
145public:
146 void writeFrame(sk_sp<SkImage> frame, size_t frame_index) override {
147 auto stream = make_file_stream(frame_index, "png");
148
149 if (!frame || !stream) {
150 return;
151 }
152
153 // Set encoding options to favor speed over size.
157
158 SkPixmap pixmap;
159 SkAssertResult(frame->peekPixels(&pixmap));
160
161 SkPngEncoder::Encode(stream.get(), pixmap, options);
162 }
163};
164
165class NullSink final : public FrameSink {
166public:
167 void writeFrame(sk_sp<SkImage>, size_t) override {}
168};
169
170#if defined(HAVE_VIDEO_ENCODER)
171class MP4Sink final : public FrameSink {
172public:
173 explicit MP4Sink(size_t frame_count) {
174 fFrames.resize(frame_count);
175 }
176
177 void writeFrame(sk_sp<SkImage> frame, size_t frame_index) override {
178 fFrames[frame_index].set_value(std::move(frame));
179 }
180
181 void finalize(double fps) override {
183 if (!encoder.beginRecording({FLAGS_width, FLAGS_height}, sk_double_round2int(fps))) {
184 fprintf(stderr, "Invalid video stream configuration.\n");
185 }
186
187 std::vector<double> starved_ms;
188 starved_ms.reserve(fFrames.size());
189
190 for (auto& frame_promise : fFrames) {
191 const auto start = std::chrono::steady_clock::now();
192 auto frame = frame_promise.get_future().get();
193 starved_ms.push_back(ms_since(start));
194
195 if (!frame) continue;
196
197 SkPixmap pixmap;
198 SkAssertResult(frame->peekPixels(&pixmap));
199 encoder.addFrame(pixmap);
200 }
201
202 auto mp4 = encoder.endRecording();
203
204 SkFILEWStream{FLAGS_writePath[0]}
205 .write(mp4->data(), mp4->size());
206
207 // If everything's going well, the first frame should account for the most,
208 // and ideally nearly all, starvation.
209 double first = starved_ms[0];
210 std::sort(starved_ms.begin(), starved_ms.end());
211 double sum = std::accumulate(starved_ms.begin(), starved_ms.end(), 0);
212 printf("Encoder starved stats: "
213 "min %gms, med %gms, avg %gms, max %gms, sum %gms, first %gms (%s)\n",
214 starved_ms[0], starved_ms[fFrames.size()/2], sum/fFrames.size(), starved_ms.back(),
215 sum, first, first == starved_ms.back() ? "ok" : "BAD");
216
217 }
218
219 std::vector<std::promise<sk_sp<SkImage>>> fFrames;
220};
221#endif // HAVE_VIDEO_ENCODER
222
223std::unique_ptr<FrameSink> FrameSink::Make(OutputFormat fmt, size_t frame_count) {
224 switch (fmt) {
225 case OutputFormat::kPNG:
226 return std::make_unique<PNGSink>();
227 case OutputFormat::kSKP:
228 // The SKP generator does not use a sink.
229 [[fallthrough]];
230 case OutputFormat::kNull:
231 return std::make_unique<NullSink>();
232 case OutputFormat::kMP4:
233#if defined(HAVE_VIDEO_ENCODER)
234 return std::make_unique<MP4Sink>(frame_count);
235#else
236 return nullptr;
237#endif
238 }
239
241}
242
243class FrameGenerator {
244public:
245 virtual ~FrameGenerator() = default;
246
247 static std::unique_ptr<FrameGenerator> Make(FrameSink*, OutputFormat, const SkMatrix&);
248
249 virtual void generateFrame(const skottie::Animation*, size_t frame_index) {}
250
251protected:
252 explicit FrameGenerator(FrameSink* sink) : fSink(sink) {}
253
254 FrameSink* fSink;
255
256private:
257 FrameGenerator(const FrameGenerator&) = delete;
258 FrameGenerator& operator=(const FrameGenerator&) = delete;
259};
260
261class CPUGenerator final : public FrameGenerator {
262public:
263#if defined(GPU_ONLY)
264 static std::unique_ptr<FrameGenerator> Make(FrameSink* sink, const SkMatrix& matrix) {
265 return nullptr;
266 }
267#else
268 static std::unique_ptr<FrameGenerator> Make(FrameSink* sink, const SkMatrix& matrix) {
269 auto surface = SkSurfaces::Raster(SkImageInfo::MakeN32Premul(FLAGS_width, FLAGS_height));
270 if (!surface) {
271 SkDebugf("Could not allocate a %d x %d surface.\n", FLAGS_width, FLAGS_height);
272 return nullptr;
273 }
274
275 return std::unique_ptr<FrameGenerator>(new CPUGenerator(sink, std::move(surface), matrix));
276 }
277
278 void generateFrame(const skottie::Animation* anim, size_t frame_index) override {
279 fSurface->getCanvas()->clear(kClearColor);
280 anim->render(fSurface->getCanvas());
281
282 fSink->writeFrame(fSurface->makeImageSnapshot(), frame_index);
283 }
284
285private:
286 CPUGenerator(FrameSink* sink, sk_sp<SkSurface> surface, const SkMatrix& scale_matrix)
287 : FrameGenerator(sink)
288 , fSurface(std::move(surface))
289 {
290 fSurface->getCanvas()->concat(scale_matrix);
291 }
292
293 const sk_sp<SkSurface> fSurface;
294#endif // !GPU_ONLY
295};
296
297class SKPGenerator final : public FrameGenerator {
298public:
299#if defined(CPU_ONLY) || defined(GPU_ONLY)
300 static std::unique_ptr<FrameGenerator> Make(FrameSink* sink, const SkMatrix& matrix) {
301 return nullptr;
302 }
303#else
304 static std::unique_ptr<FrameGenerator> Make(FrameSink* sink, const SkMatrix& scale_matrix) {
305 return std::unique_ptr<FrameGenerator>(new SKPGenerator(sink, scale_matrix));
306 }
307
308 void generateFrame(const skottie::Animation* anim, size_t frame_index) override {
309 auto* canvas = fRecorder.beginRecording(FLAGS_width, FLAGS_height);
310 canvas->concat(fScaleMatrix);
311 anim->render(canvas);
312
313 auto frame = fRecorder.finishRecordingAsPicture();
314 auto stream = make_file_stream(frame_index, "skp");
315
316 if (frame && stream) {
317 SkSerialProcs sProcs;
318 sProcs.fImageProc = [](SkImage* img, void*) -> sk_sp<SkData> {
319 return SkPngEncoder::Encode(as_IB(img)->directContext(), img,
321 };
322 frame->serialize(stream.get(), &sProcs);
323 }
324 }
325
326private:
327 SKPGenerator(FrameSink* sink, const SkMatrix& scale_matrix)
328 : FrameGenerator(sink)
329 , fScaleMatrix(scale_matrix)
330 {}
331
332 const SkMatrix fScaleMatrix;
333 SkPictureRecorder fRecorder;
334#endif // !CPU_ONLY && !GPU_ONLY
335};
336
337class GPUGenerator final : public FrameGenerator {
338public:
339#if defined(CPU_ONLY)
340 static std::unique_ptr<FrameGenerator> Make(FrameSink* sink, const SkMatrix& matrix) {
341 return nullptr;
342 }
343#else
344 static std::unique_ptr<FrameGenerator> Make(FrameSink* sink, const SkMatrix& matrix) {
345 auto gpu_generator = std::unique_ptr<GPUGenerator>(new GPUGenerator(sink, matrix));
346
347 return gpu_generator->isValid()
348 ? std::unique_ptr<FrameGenerator>(gpu_generator.release())
349 : nullptr;
350 }
351
352 ~GPUGenerator() override {
353 // ensure all pending reads are completed
354 fCtx->flushAndSubmit(GrSyncCpu::kYes);
355 }
356
357 void generateFrame(const skottie::Animation* anim, size_t frame_index) override {
358 fSurface->getCanvas()->clear(kClearColor);
359 anim->render(fSurface->getCanvas());
360
361 auto rec = std::make_unique<AsyncRec>(fSink, frame_index);
362 fSurface->asyncRescaleAndReadPixels(SkImageInfo::MakeN32Premul(FLAGS_width, FLAGS_height),
363 {0, 0, FLAGS_width, FLAGS_height},
364 SkSurface::RescaleGamma::kSrc,
365 SkImage::RescaleMode::kNearest,
366 AsyncCallback, rec.release());
367
368 fCtx->submit();
369 }
370
371private:
372 GPUGenerator(FrameSink* sink, const SkMatrix& matrix)
373 : FrameGenerator(sink)
374 {
375 fCtx = fFactory.getContextInfo(skgpu::ContextType::kGL).directContext();
376 fSurface = SkSurfaces::RenderTarget(fCtx,
378 SkImageInfo::MakeN32Premul(FLAGS_width, FLAGS_height),
379 0,
381 nullptr);
382 if (fSurface) {
383 fSurface->getCanvas()->concat(matrix);
384 } else {
385 fprintf(stderr, "Could not initialize GL context.\n");
386 }
387 }
388
389 bool isValid() const { return !!fSurface; }
390
391 struct AsyncRec {
392 FrameSink* sink;
393 size_t index;
394
395 AsyncRec(FrameSink* sink, size_t index) : sink(sink), index(index) {}
396 };
397
398 static void AsyncCallback(SkSurface::ReadPixelsContext ctx,
399 std::unique_ptr<const SkSurface::AsyncReadResult> result) {
400 std::unique_ptr<const AsyncRec> rec(reinterpret_cast<const AsyncRec*>(ctx));
401 if (result && result->count() == 1) {
402 SkPixmap pm(SkImageInfo::MakeN32Premul(FLAGS_width, FLAGS_height),
403 result->data(0), result->rowBytes(0));
404
405 auto release_proc = [](const void*, SkImages::ReleaseContext ctx) {
406 std::unique_ptr<const SkSurface::AsyncReadResult>
407 adopted(reinterpret_cast<const SkSurface::AsyncReadResult*>(ctx));
408 };
409
410 auto frame_image =
411 SkImages::RasterFromPixmap(pm, release_proc, (void*)result.release());
412
413 rec->sink->writeFrame(std::move(frame_image), rec->index);
414 }
415 }
416
418 GrDirectContext* fCtx;
419 sk_sp<SkSurface> fSurface;
420#endif // !CPU_ONLY
421};
422
423std::unique_ptr<FrameGenerator> FrameGenerator::Make(FrameSink* sink,
424 OutputFormat fmt,
425 const SkMatrix& matrix) {
426 if (fmt == OutputFormat::kSKP) {
427 return SKPGenerator::Make(sink, matrix);
428 }
429
430 return FLAGS_gpu
431 ? GPUGenerator::Make(sink, matrix)
432 : CPUGenerator::Make(sink, matrix);
433}
434
435class Logger final : public skottie::Logger {
436public:
437 struct LogEntry {
438 SkString fMessage,
439 fJSON;
440 };
441
442 void log(skottie::Logger::Level lvl, const char message[], const char json[]) override {
443 auto& log = lvl == skottie::Logger::Level::kError ? fErrors : fWarnings;
444 log.push_back({ SkString(message), json ? SkString(json) : SkString() });
445 }
446
447 void report() const {
448 SkDebugf("Animation loaded with %zu error%s, %zu warning%s.\n",
449 fErrors.size(), fErrors.size() == 1 ? "" : "s",
450 fWarnings.size(), fWarnings.size() == 1 ? "" : "s");
451
452 const auto& show = [](const LogEntry& log, const char prefix[]) {
453 SkDebugf("%s%s", prefix, log.fMessage.c_str());
454 if (!log.fJSON.isEmpty())
455 SkDebugf(" : %s", log.fJSON.c_str());
456 SkDebugf("\n");
457 };
458
459 for (const auto& err : fErrors) show(err, " !! ");
460 for (const auto& wrn : fWarnings) show(wrn, " ?? ");
461 }
462
463private:
464 std::vector<LogEntry> fErrors,
465 fWarnings;
466};
467
468} // namespace
469
471
472int main(int argc, char** argv) {
476
477 if (FLAGS_input.isEmpty() || FLAGS_writePath.isEmpty()) {
478 SkDebugf("Missing required 'input' and 'writePath' args.\n");
479 return 1;
480 }
481
482 OutputFormat fmt;
483 if (0 == std::strcmp(FLAGS_format[0], "png")) {
484 fmt = OutputFormat::kPNG;
485 } else if (0 == std::strcmp(FLAGS_format[0], "skp")) {
486 fmt = OutputFormat::kSKP;
487 } else if (0 == std::strcmp(FLAGS_format[0], "null")) {
488 fmt = OutputFormat::kNull;
489#if defined(HAVE_VIDEO_ENCODER)
490 } else if (0 == std::strcmp(FLAGS_format[0], "mp4")) {
491 fmt = OutputFormat::kMP4;
492#endif
493 } else {
494 fprintf(stderr, "Unknown format: %s\n", FLAGS_format[0]);
495 return 1;
496 }
497
498 if (fmt != OutputFormat::kMP4 && !sk_mkdir(FLAGS_writePath[0])) {
499 return 1;
500 }
501
502 SkCodecs::Register(SkPngDecoder::Decoder());
503 SkCodecs::Register(SkJpegDecoder::Decoder());
504 SkCodecs::Register(SkWebpDecoder::Decoder());
505
506 // If necessary, clients should use a font manager that would load fonts from the system.
507#if defined(SK_BUILD_FOR_MAC) && defined(SK_FONTMGR_CORETEXT_AVAILABLE)
509#elif defined(SK_BUILD_FOR_ANDROID) && defined(SK_FONTMGR_ANDROID_AVAILABLE)
510 sk_sp<SkFontMgr> fontMgr = SkFontMgr_New_Android(nullptr, std::make_unique<SkFontScanner_FreeType>());
511#elif defined(SK_BUILD_FOR_UNIX) && defined(SK_FONTMGR_FONTCONFIG_AVAILABLE)
513#else
515#endif
516
518 auto logger = sk_make_sp<Logger>();
522 predecode),
523 predecode,
524 fontMgr));
525 auto data = SkData::MakeFromFileName(FLAGS_input[0]);
526 auto precomp_interceptor =
527 sk_make_sp<skottie_utils::ExternalAnimationPrecompInterceptor>(rp, "__");
528
529 if (!data) {
530 SkDebugf("Could not load %s.\n", FLAGS_input[0]);
531 return 1;
532 }
533
534 // Instantiate an animation on the main thread for two reasons:
535 // - we need to know its duration upfront
536 // - we want to only report parsing errors once
537 auto anim = skottie::Animation::Builder()
539 .setLogger(logger)
542 .make(static_cast<const char*>(data->data()), data->size());
543 if (!anim) {
544 SkDebugf("Could not parse animation: '%s'.\n", FLAGS_input[0]);
545 return 1;
546 }
547
548 const auto scale_matrix = SkMatrix::RectToRect(SkRect::MakeSize(anim->size()),
549 SkRect::MakeIWH(FLAGS_width, FLAGS_height),
551 logger->report();
552
553 const auto t0 = SkTPin(FLAGS_t0, 0.0, 1.0),
554 t1 = SkTPin(FLAGS_t1, t0, 1.0),
555 native_fps = anim->fps(),
556 frame0 = anim->duration() * t0 * native_fps,
557 duration = anim->duration() * (t1 - t0);
558
559 double fps = FLAGS_fps > 0 ? FLAGS_fps : native_fps;
560 if (fps <= 0) {
561 SkDebugf("Invalid fps: %f.\n", fps);
562 return 1;
563 }
564
565 auto frame_count = static_cast<int>(duration * fps);
566 static constexpr int kMaxFrames = 10000;
567 if (frame_count > kMaxFrames) {
568 frame_count = kMaxFrames;
569 fps = frame_count / duration;
570 }
571 const auto fps_scale = native_fps / fps;
572
573 printf("Rendering %f seconds (%d frames @%f fps).\n", duration, frame_count, fps);
574
575 const auto sink = FrameSink::Make(fmt, frame_count);
576
577 std::vector<double> frames_ms(frame_count);
578
579 const auto thread_count = FLAGS_gpu ? 0 : FLAGS_threads - 1;
580 SkTaskGroup::Enabler enabler(thread_count);
581
582 SkTaskGroup tg;
583 {
584 // Depending on type (gpu vs. everything else), we use either a single generator
585 // or one generator per worker thread, respectively.
586 // Scoping is important for the single generator case because we want its destructor to
587 // flush out any pending async operations.
588 std::unique_ptr<FrameGenerator> singleton_generator;
589 if (FLAGS_gpu) {
590 singleton_generator = FrameGenerator::Make(sink.get(), fmt, scale_matrix);
591 }
592
593 tg.batch(frame_count, [&](int i) {
594 // SkTaskGroup::Enabler creates a LIFO work pool,
595 // but we want our early frames to start first.
596 i = frame_count - 1 - i;
597
598 const auto start = std::chrono::steady_clock::now();
599 thread_local static auto* anim =
602 .setPrecompInterceptor(precomp_interceptor)
603 .make(static_cast<const char*>(data->data()), data->size())
604 .release();
605 thread_local static auto* gen = singleton_generator
606 ? singleton_generator.get()
607 : FrameGenerator::Make(sink.get(), fmt, scale_matrix).release();
608
609 if (gen && anim) {
610 anim->seekFrame(frame0 + i * fps_scale);
611 gen->generateFrame(anim, SkToSizeT(i));
612 } else {
613 sink->writeFrame(nullptr, SkToSizeT(i));
614 }
615
616 frames_ms[i] = ms_since(start);
617 });
618 }
619
620 sink->finalize(fps);
621 tg.wait();
622
623
624 std::sort(frames_ms.begin(), frames_ms.end());
625 double sum = std::accumulate(frames_ms.begin(), frames_ms.end(), 0);
626 printf("Frame time stats: min %gms, med %gms, avg %gms, max %gms, sum %gms\n",
627 frames_ms[0], frames_ms[frame_count/2], sum/frame_count, frames_ms.back(), sum);
628
629 return 0;
630}
#define DEFINE_int(name, defaultValue, helpString)
#define DEFINE_bool2(name, shortName, defaultValue, helpString)
#define DEFINE_string2(name, shortName, defaultValue, helpString)
#define DEFINE_double(name, defaultValue, helpString)
const char * options
@ kTopLeft_GrSurfaceOrigin
Definition GrTypes.h:148
#define SkAssertResult(cond)
Definition SkAssert.h:123
#define SkUNREACHABLE
Definition SkAssert.h:135
uint32_t SkColor
Definition SkColor.h:37
constexpr SkColor SK_ColorWHITE
Definition SkColor.h:122
void SK_SPI SkDebugf(const char format[],...) SK_PRINTF_LIKE(1
#define sk_double_round2int(x)
SK_API sk_sp< SkFontMgr > SkFontMgr_New_Android(const SkFontMgr_Android_CustomFonts *custom)
SK_API sk_sp< SkFontMgr > SkFontMgr_New_Custom_Empty()
SK_API sk_sp< SkFontMgr > SkFontMgr_New_FontConfig(FcConfig *fc)
SK_API sk_sp< SkFontMgr > SkFontMgr_New_CoreText(CTFontCollectionRef)
static SkImage_Base * as_IB(SkImage *image)
static std::unique_ptr< SkEncoder > Make(SkWStream *dst, const SkPixmap *src, const SkYUVAPixmaps *srcYUVA, const SkColorSpace *srcYUVAColorSpace, const SkJpegEncoder::Options &options)
bool sk_mkdir(const char *path)
SK_API SkString static SkString SkStringPrintf()
Definition SkString.h:287
static constexpr const T & SkTPin(const T &x, const T &lo, const T &hi)
Definition SkTPin.h:19
constexpr size_t SkToSizeT(S x)
Definition SkTo.h:31
bool gSkUseThreadLocalStrikeCaches_IAcknowledgeThisIsIncrediblyExperimental
const char * formats_help
static void Parse(int argc, const char *const *argv)
static sk_sp< SkData > MakeFromFileName(const char path[])
Definition SkData.cpp:148
bool write(const void *buffer, size_t size) override
Definition SkStream.cpp:426
static void Init()
static SkMatrix RectToRect(const SkRect &src, const SkRect &dst, ScaleToFit mode=kFill_ScaleToFit)
Definition SkMatrix.h:157
@ kCenter_ScaleToFit
scales and aligns to center
Definition SkMatrix.h:139
static SkString Join(const char *rootPath, const char *relativePath)
Definition SkOSPath.cpp:14
static SkString Dirname(const char *fullPath)
Definition SkOSPath.cpp:36
void * ReadPixelsContext
Definition SkSurface.h:464
void batch(int N, std::function< void(int)> fn)
Builder & setResourceProvider(sk_sp< ResourceProvider >)
Definition Skottie.cpp:309
Builder & setFontManager(sk_sp< SkFontMgr >)
Definition Skottie.cpp:314
Builder & setPrecompInterceptor(sk_sp< PrecompInterceptor >)
Definition Skottie.cpp:334
Builder & setLogger(sk_sp< Logger >)
Definition Skottie.cpp:324
sk_sp< Animation > make(SkStream *)
Definition Skottie.cpp:349
Builder & setTextShapingFactory(sk_sp< SkShapers::Factory >)
Definition Skottie.cpp:344
void seekFrame(double t, sksg::InvalidationController *ic=nullptr)
Definition Skottie.cpp:513
const SkSize & size() const
Definition Skottie.h:286
double fps() const
Definition Skottie.h:273
double duration() const
Definition Skottie.h:268
void render(SkCanvas *canvas, const SkRect *dst=nullptr) const
Definition Skottie.cpp:482
virtual void log(Level, const char message[], const char *json=nullptr)=0
static sk_sp< CachingResourceProvider > Make(sk_sp< ResourceProvider > rp)
static sk_sp< DataURIResourceProviderProxy > Make(sk_sp< ResourceProvider > rp, ImageDecodeStrategy=ImageDecodeStrategy::kLazyDecode, sk_sp< const SkFontMgr > fontMgr=nullptr)
static sk_sp< FileResourceProvider > Make(SkString base_dir, ImageDecodeStrategy=ImageDecodeStrategy::kLazyDecode)
VkSurfaceKHR surface
Definition main.cc:49
sk_sp< SkFontMgr > fontMgr
Definition examples.cpp:32
double duration
Definition examples.cpp:30
double frame
Definition examples.cpp:31
GAsyncResult * result
static FlMethodResponse * show(FlTextInputPlugin *self)
uint32_t uint32_t * format
Win32Message message
char ** argv
Definition library.h:9
SK_API sk_sp< SkImage > RasterFromPixmap(const SkPixmap &pixmap, RasterReleaseProc rasterReleaseProc, ReleaseContext releaseContext)
void * ReleaseContext
Definition SkImage.h:50
constexpr SkCodecs::Decoder Decoder()
constexpr SkCodecs::Decoder Decoder()
SK_API bool Encode(SkWStream *dst, const SkPixmap &src, const Options &options)
unsigned useCenter Optional< SkMatrix > matrix
Definition SkRecords.h:258
std::string printf(const char *fmt,...) SK_PRINTF_LIKE(1
sk_sp< Factory > BestAvailable()
SK_API sk_sp< SkSurface > Raster(const SkImageInfo &imageInfo, size_t rowBytes, const SkSurfaceProps *surfaceProps)
SK_API sk_sp< SkSurface > RenderTarget(GrRecordingContext *context, skgpu::Budgeted budgeted, const SkImageInfo &imageInfo, int sampleCount, GrSurfaceOrigin surfaceOrigin, const SkSurfaceProps *surfaceProps, bool shouldCreateWithMips=false, bool isProtected=false)
constexpr SkCodecs::Decoder Decoder()
DEF_SWITCHES_START aot vmservice shared library Name of the *so containing AOT compiled Dart assets for launching the service isolate vm snapshot The VM snapshot data that will be memory mapped as read only SnapshotAssetPath must be present isolate snapshot The isolate snapshot data that will be memory mapped as read only SnapshotAssetPath must be present cache dir path
Definition switches.h:57
it will be possible to load the file into Perfetto s trace viewer disable asset Prevents usage of any non test fonts unless they were explicitly Loaded via prefetched default font Indicates whether the embedding started a prefetch of the default font manager before creating the engine run In non interactive keep the shell running after the Dart script has completed enable serial On low power devices with low core running concurrent GC tasks on threads can cause them to contend with the UI thread which could potentially lead to jank This option turns off all concurrent GC activities domain network JSON encoded network policy per domain This overrides the DisallowInsecureConnections switch Embedder can specify whether to allow or disallow insecure connections at a domain level old gen heap size
Definition switches.h:259
Definition gen.py:1
Definition main.py:1
Definition ref_ptr.h:256
static SkString fmt(SkColor4f c)
Definition p3.cpp:43
SkScalar w
int32_t height
int32_t width
static SkImageInfo MakeN32Premul(int width, int height)
static SkRect MakeIWH(int w, int h)
Definition SkRect.h:623
static constexpr SkRect MakeSize(const SkSize &size)
Definition SkRect.h:633
SkSerialImageProc fImageProc