Flutter Engine
The Flutter Engine
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.");
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.
155 options.fZLibLevel = 1;
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]];
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},
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
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")) {
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
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()
538 .setFontManager(fontMgr)
539 .setLogger(logger)
540 .setResourceProvider(rp)
541 .setTextShapingFactory(SkShapers::BestAvailable())
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 =
601 .setResourceProvider(rp)
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}
const char * options
SkAssertResult(font.textToGlyphs("Hello", 5, SkTextEncoding::kUTF8, glyphs, std::size(glyphs))==count)
@ kTopLeft_GrSurfaceOrigin
Definition: GrTypes.h:148
#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)
Definition: SkImage_Base.h:201
bool sk_mkdir(const char *path)
static std::vector< SkPDFIndirectReference > sort(const THashSet< SkPDFIndirectReference > &src)
SK_API SkString SkStringPrintf(const char *format,...) SK_PRINTF_LIKE(1
Creates a new string and writes into it using a printf()-style format.
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
static DEFINE_int(width, 800, "Render width.")
static DEFINE_string2(input, i, nullptr, "Input .json file.")
int main(int argc, char **argv)
static DEFINE_double(t0, 0, "Timeline start [0..1].")
const char * formats_help
Definition: SkottieTool.cpp:72
static DEFINE_bool2(gpu, g, false, "Enable GPU rasterization.")
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()
Definition: SkGraphics.cpp:22
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)
Definition: SkTaskGroup.cpp:24
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)
Definition: SkResources.h:247
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
void SK_API Register(Decoder d)
Definition: SkCodec.cpp:135
SK_API sk_sp< SkImage > RasterFromPixmap(const SkPixmap &pixmap, RasterReleaseProc rasterReleaseProc, ReleaseContext releaseContext)
void * ReleaseContext
Definition: SkImage.h:50
constexpr SkCodecs::Decoder Decoder()
Definition: SkJpegDecoder.h:38
SK_API sk_sp< SkDocument > Make(SkWStream *dst, const SkSerialProcs *=nullptr, std::function< void(const SkPicture *)> onEndPage=nullptr)
constexpr SkCodecs::Decoder Decoder()
Definition: SkPngDecoder.h:38
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
Definition: SkSLString.cpp:83
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()
Definition: SkWebpDecoder.h:38
DlVertices::Builder Builder
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
@ kNull
Definition: paint.cc:59
Definition: gen.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
Definition: SkSerialProcs.h:90
std::shared_ptr< const fml::Mapping > data
Definition: texture_gles.cc:63