Flutter Engine
The Flutter Engine
Functions | Variables
SkottieTool.cpp File Reference
#include "include/codec/SkCodec.h"
#include "include/codec/SkJpegDecoder.h"
#include "include/codec/SkPngDecoder.h"
#include "include/codec/SkWebpDecoder.h"
#include "include/core/SkCanvas.h"
#include "include/core/SkColor.h"
#include "include/core/SkData.h"
#include "include/core/SkFontMgr.h"
#include "include/core/SkGraphics.h"
#include "include/core/SkImage.h"
#include "include/core/SkImageInfo.h"
#include "include/core/SkMatrix.h"
#include "include/core/SkPixmap.h"
#include "include/core/SkRect.h"
#include "include/core/SkRefCnt.h"
#include "include/core/SkStream.h"
#include "include/core/SkString.h"
#include "include/core/SkSurface.h"
#include "include/core/SkTypes.h"
#include "include/encode/SkPngEncoder.h"
#include "include/private/base/SkDebug.h"
#include "include/private/base/SkTPin.h"
#include "include/private/base/SkTo.h"
#include "modules/skottie/include/ExternalLayer.h"
#include "modules/skottie/include/Skottie.h"
#include "modules/skottie/utils/SkottieUtils.h"
#include "modules/skresources/include/SkResources.h"
#include "modules/skshaper/utils/FactoryHelpers.h"
#include "src/core/SkOSFile.h"
#include "src/core/SkTaskGroup.h"
#include "src/utils/SkOSPath.h"
#include "tools/flags/CommandLineFlags.h"
#include "include/gpu/GpuTypes.h"
#include "include/gpu/GrDirectContext.h"
#include "include/gpu/GrTypes.h"
#include "include/gpu/ganesh/SkSurfaceGanesh.h"
#include "tools/gpu/ContextType.h"
#include "tools/gpu/GrContextFactory.h"
#include "include/core/SkPicture.h"
#include "include/core/SkPictureRecorder.h"
#include "include/core/SkSerialProcs.h"
#include "src/image/SkImage_Base.h"
#include <algorithm>
#include <chrono>
#include <cstdio>
#include <cstring>
#include <functional>
#include <memory>
#include <numeric>
#include <utility>
#include <vector>
#include "include/ports/SkFontMgr_empty.h"

Go to the source code of this file.

Functions

static DEFINE_string2 (input, i, nullptr, "Input .json file.")
 
static DEFINE_string2 (writePath, w, nullptr, "Output directory. Frames are names [0-9]{6}.png.")
 
static DEFINE_string2 (format, f, "png", formats_help)
 
static DEFINE_double (t0, 0, "Timeline start [0..1].")
 
static DEFINE_double (t1, 1, "Timeline stop [0..1].")
 
static DEFINE_double (fps, 0, "Decode frames per second (default is animation native fps).")
 
static DEFINE_int (width, 800, "Render width.")
 
static DEFINE_int (height, 600, "Render height.")
 
static DEFINE_int (threads, 0, "Number of worker threads (0 -> cores count).")
 
static DEFINE_bool2 (gpu, g, false, "Enable GPU rasterization.")
 
int main (int argc, char **argv)
 

Variables

const char * formats_help = "Output format (png, skp, or null)"
 
bool gSkUseThreadLocalStrikeCaches_IAcknowledgeThisIsIncrediblyExperimental
 

Function Documentation

◆ DEFINE_bool2()

static DEFINE_bool2 ( gpu  ,
,
false  ,
"Enable GPU rasterization."   
)
static

◆ DEFINE_double() [1/3]

static DEFINE_double ( fps  ,
,
"Decode frames per second (default is animation native fps)."   
)
static

◆ DEFINE_double() [2/3]

static DEFINE_double ( t0  ,
,
"Timeline start ."  [0..1] 
)
static

◆ DEFINE_double() [3/3]

static DEFINE_double ( t1  ,
,
"Timeline stop ."  [0..1] 
)
static

◆ DEFINE_int() [1/3]

static DEFINE_int ( height  ,
600  ,
"Render height."   
)
static

◆ DEFINE_int() [2/3]

static DEFINE_int ( threads  ,
,
"Number of worker threads (0 -> cores count)."   
)
static

◆ DEFINE_int() [3/3]

static DEFINE_int ( width  ,
800  ,
"Render width."   
)
static

◆ DEFINE_string2() [1/3]

static DEFINE_string2 ( format  ,
,
"png"  ,
formats_help   
)
static

◆ DEFINE_string2() [2/3]

static DEFINE_string2 ( input  ,
i  ,
nullptr  ,
"Input .json file."   
)
static

◆ DEFINE_string2() [3/3]

static DEFINE_string2 ( writePath  ,
w  ,
nullptr  ,
"Output directory. Frames are names {6}.png."  [0-9] 
)
static

◆ main()

int main ( int  argc,
char **  argv 
)

Definition at line 472 of file SkottieTool.cpp.

472 {
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}
void SK_SPI SkDebugf(const char format[],...) SK_PRINTF_LIKE(1
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)
bool sk_mkdir(const char *path)
static std::vector< SkPDFIndirectReference > sort(const THashSet< SkPDFIndirectReference > &src)
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 void Parse(int argc, const char *const *argv)
static sk_sp< SkData > MakeFromFileName(const char path[])
Definition: SkData.cpp:148
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 Dirname(const char *fullPath)
Definition: SkOSPath.cpp:36
void batch(int N, std::function< void(int)> fn)
Definition: SkTaskGroup.cpp:24
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)
sk_sp< SkFontMgr > fontMgr
Definition: examples.cpp:32
double duration
Definition: examples.cpp:30
char ** argv
Definition: library.h:9
void SK_API Register(Decoder d)
Definition: SkCodec.cpp:135
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
std::string printf(const char *fmt,...) SK_PRINTF_LIKE(1
Definition: SkSLString.cpp:83
sk_sp< Factory > BestAvailable()
constexpr SkCodecs::Decoder Decoder()
Definition: SkWebpDecoder.h:38
DlVertices::Builder Builder
@ kNull
Definition: paint.cc:59
Definition: gen.py:1
static SkString fmt(SkColor4f c)
Definition: p3.cpp:43
static SkRect MakeIWH(int w, int h)
Definition: SkRect.h:623
static constexpr SkRect MakeSize(const SkSize &size)
Definition: SkRect.h:633
std::shared_ptr< const fml::Mapping > data
Definition: texture_gles.cc:63

Variable Documentation

◆ formats_help

const char* formats_help = "Output format (png, skp, or null)"

Definition at line 72 of file SkottieTool.cpp.

◆ gSkUseThreadLocalStrikeCaches_IAcknowledgeThisIsIncrediblyExperimental

bool gSkUseThreadLocalStrikeCaches_IAcknowledgeThisIsIncrediblyExperimental
extern

Definition at line 28 of file SkStrikeCache.cpp.