add motion blur params to skottie2movie
--motion_angle ... [default is 180] --motion_samples ... [default is 1, for no motion blur] Change-Id: Iec0f31655b3369f51e0b398efb2d5b156dcbaf2e Reviewed-on: https://skia-review.googlesource.com/c/skia/+/221416 Reviewed-by: Mike Reed <reed@google.com> Commit-Queue: Mike Reed <reed@google.com> Auto-Submit: Mike Reed <reed@google.com>
This commit is contained in:
parent
b500ef7a76
commit
4b203ad7ac
@ -432,6 +432,10 @@ public:
|
||||
*/
|
||||
int height() const { return fHeight; }
|
||||
|
||||
/** Returns an ImageInfo describing the surface.
|
||||
*/
|
||||
SkImageInfo imageInfo();
|
||||
|
||||
/** Returns unique value identifying the content of SkSurface. Returned value changes
|
||||
each time the content changes. Content is changed by drawing, or by calling
|
||||
notifyContentWillChange().
|
||||
@ -542,6 +546,11 @@ public:
|
||||
*/
|
||||
sk_sp<SkSurface> makeSurface(const SkImageInfo& imageInfo);
|
||||
|
||||
/** Calls makeSurface(ImageInfo) with the same ImageInfo as this surface, but with the
|
||||
* specified width and height.
|
||||
*/
|
||||
sk_sp<SkSurface> makeSurface(int width, int height);
|
||||
|
||||
/** Returns SkImage capturing SkSurface contents. Subsequent drawing to SkSurface contents
|
||||
are not captured. SkImage allocation is accounted for if SkSurface was created with
|
||||
SkBudgeted::kYes.
|
||||
|
@ -216,6 +216,11 @@ public:
|
||||
*/
|
||||
void seek(SkScalar t);
|
||||
|
||||
/** Update the animation state to match t, specifed in frame time
|
||||
* i.e. relative to duration().
|
||||
*/
|
||||
void seekFrameTime(double t);
|
||||
|
||||
/**
|
||||
* Returns the animation duration in seconds.
|
||||
*/
|
||||
|
@ -604,6 +604,12 @@ void Animation::seek(SkScalar t) {
|
||||
fScene->animate(SkTPin(fInPoint + t * (fOutPoint - fInPoint), fInPoint, kLastValidFrame));
|
||||
}
|
||||
|
||||
void Animation::seekFrameTime(double t) {
|
||||
if (double dur = this->duration()) {
|
||||
this->seek((SkScalar)(t / dur));
|
||||
}
|
||||
}
|
||||
|
||||
sk_sp<Animation> Animation::Make(const char* data, size_t length) {
|
||||
return Builder().make(data, length);
|
||||
}
|
||||
|
@ -274,6 +274,11 @@ SkSurface::SkSurface(const SkImageInfo& info, const SkSurfaceProps* props)
|
||||
fGenerationID = 0;
|
||||
}
|
||||
|
||||
SkImageInfo SkSurface::imageInfo() {
|
||||
// TODO: do we need to go through canvas for this?
|
||||
return this->getCanvas()->imageInfo();
|
||||
}
|
||||
|
||||
uint32_t SkSurface::generationID() {
|
||||
if (0 == fGenerationID) {
|
||||
fGenerationID = asSB(this)->newGenerationID();
|
||||
@ -311,6 +316,10 @@ sk_sp<SkSurface> SkSurface::makeSurface(const SkImageInfo& info) {
|
||||
return asSB(this)->onNewSurface(info);
|
||||
}
|
||||
|
||||
sk_sp<SkSurface> SkSurface::makeSurface(int width, int height) {
|
||||
return this->makeSurface(this->imageInfo().makeWH(width, height));
|
||||
}
|
||||
|
||||
void SkSurface::draw(SkCanvas* canvas, SkScalar x, SkScalar y,
|
||||
const SkPaint* paint) {
|
||||
return asSB(this)->onDraw(canvas, x, y, paint);
|
||||
|
@ -21,6 +21,37 @@ static DEFINE_string2(assetPath, a, "", "path to assets needed for json file");
|
||||
static DEFINE_int_2(fps, f, 25, "fps");
|
||||
static DEFINE_bool2(verbose, v, false, "verbose mode");
|
||||
static DEFINE_bool2(loop, l, false, "loop mode for profiling");
|
||||
static DEFINE_double(motion_angle, 180, "motion blur angle");
|
||||
static DEFINE_double(motion_slope, 0, "motion blur slope");
|
||||
static DEFINE_int(motion_samples, 1, "motion blur samples");
|
||||
|
||||
static void produce_frame(SkSurface* surf, skottie::Animation* anim, double frame_time) {
|
||||
anim->seekFrameTime(frame_time);
|
||||
surf->getCanvas()->clear(SK_ColorWHITE);
|
||||
anim->render(surf->getCanvas());
|
||||
}
|
||||
|
||||
static void produce_frame(SkSurface* surf, SkSurface* tmp, skottie::Animation* anim,
|
||||
double frame_time, double frame_duration, double motion_radius,
|
||||
int motion_samples) {
|
||||
double samples_duration = frame_duration * motion_radius * 2;
|
||||
double dt = samples_duration / (motion_samples - 1);
|
||||
double t = frame_time - samples_duration / 2;
|
||||
|
||||
SkPaint paint;
|
||||
paint.setAlphaf(1.0f / motion_samples);
|
||||
paint.setBlendMode(SkBlendMode::kPlus);
|
||||
surf->getCanvas()->clear(0);
|
||||
|
||||
for (int i = 0; i < motion_samples; ++i) {
|
||||
if (FLAGS_verbose) {
|
||||
SkDebugf("time %g sample_time %g\n", frame_time, t);
|
||||
}
|
||||
produce_frame(tmp, anim, t);
|
||||
t += dt;
|
||||
tmp->draw(surf->getCanvas(), 0, 0, &paint);
|
||||
}
|
||||
}
|
||||
|
||||
int main(int argc, char** argv) {
|
||||
CommandLineFlags::SetUsage("Converts skottie to a mp4");
|
||||
@ -31,6 +62,25 @@ int main(int argc, char** argv) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (FLAGS_motion_angle < 0 || FLAGS_motion_angle > 360) {
|
||||
SkDebugf("--motion_angle must be [0...360]\n");
|
||||
return -1;
|
||||
}
|
||||
if (FLAGS_motion_slope < -1 || FLAGS_motion_slope > 1) {
|
||||
SkDebugf("--motion_slope must be [-1...1]\n");
|
||||
return -1;
|
||||
}
|
||||
if (FLAGS_motion_samples < 1) {
|
||||
SkDebugf("--motion_samples must be >= 1\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
// map angle=180 to radius=1/4 (of a frame duration)
|
||||
double motion_radius = FLAGS_motion_angle * 0.25 / 180.0;
|
||||
if (FLAGS_motion_samples == 1) {
|
||||
motion_radius = 0; // no blur if we're only 1 sample
|
||||
}
|
||||
|
||||
SkString assetPath;
|
||||
if (FLAGS_assetPath.count() > 0) {
|
||||
assetPath.set(FLAGS_assetPath[0]);
|
||||
@ -56,53 +106,63 @@ int main(int argc, char** argv) {
|
||||
fps = 240;
|
||||
}
|
||||
|
||||
const int frames = SkScalarRoundToInt(duration * fps);
|
||||
const double frame_duration = 1.0 / fps;
|
||||
|
||||
if (FLAGS_verbose) {
|
||||
SkDebugf("size %dx%d duration %g\n", dim.width(), dim.height(), duration, fps);
|
||||
SkDebugf("size %dx%d duration %g, fps %d, frame_duration %g\n",
|
||||
dim.width(), dim.height(), duration, fps, frame_duration);
|
||||
}
|
||||
|
||||
SkVideoEncoder encoder;
|
||||
const int frames = SkScalarRoundToInt(duration * fps);
|
||||
|
||||
while (FLAGS_loop) {
|
||||
double start = SkTime::GetSecs();
|
||||
sk_sp<SkSurface> surf, tmp_surf;
|
||||
sk_sp<SkData> data;
|
||||
|
||||
do {
|
||||
double loop_start = SkTime::GetSecs();
|
||||
|
||||
encoder.beginRecording(dim, fps);
|
||||
// lazily allocate the surfaces
|
||||
if (!surf) {
|
||||
surf = SkSurface::MakeRaster(encoder.preferredInfo());
|
||||
tmp_surf = surf->makeSurface(surf->width(), surf->height());
|
||||
}
|
||||
|
||||
for (int i = 0; i <= frames; ++i) {
|
||||
animation->seek(i * 1.0 / frames); // normalized time
|
||||
animation->render(encoder.beginFrame());
|
||||
encoder.endFrame();
|
||||
double ts = i * 1.0 / fps;
|
||||
if (FLAGS_verbose) {
|
||||
SkDebugf("rendering frame %d ts %g\n", i, ts);
|
||||
}
|
||||
|
||||
double normal_time = i * 1.0 / frames;
|
||||
double frame_time = normal_time * duration;
|
||||
|
||||
if (motion_radius > 0) {
|
||||
produce_frame(surf.get(), tmp_surf.get(), animation.get(), frame_time, frame_duration,
|
||||
motion_radius, FLAGS_motion_samples);
|
||||
} else {
|
||||
produce_frame(surf.get(), animation.get(), frame_time);
|
||||
}
|
||||
|
||||
SkPixmap pm;
|
||||
SkAssertResult(surf->peekPixels(&pm));
|
||||
encoder.addFrame(pm);
|
||||
}
|
||||
(void)encoder.endRecording();
|
||||
if (FLAGS_verbose) {
|
||||
double dur = SkTime::GetSecs() - start;
|
||||
SkDebugf("%d frames, %g secs, %d fps\n",
|
||||
frames, dur, (int)floor(frames / dur + 0.5));
|
||||
data = encoder.endRecording();
|
||||
|
||||
if (FLAGS_loop) {
|
||||
double loop_dur = SkTime::GetSecs() - loop_start;
|
||||
SkDebugf("recording secs %g, frames %d, recording fps %d\n",
|
||||
loop_dur, frames, (int)(frames / loop_dur));
|
||||
}
|
||||
}
|
||||
|
||||
encoder.beginRecording(dim, fps);
|
||||
|
||||
auto surf = SkSurface::MakeRaster(encoder.preferredInfo());
|
||||
|
||||
for (int i = 0; i <= frames; ++i) {
|
||||
double ts = i * 1.0 / fps;
|
||||
if (FLAGS_verbose) {
|
||||
SkDebugf("rendering frame %d ts %g\n", i, ts);
|
||||
}
|
||||
animation->seek(i * 1.0 / frames); // normalized time
|
||||
surf->getCanvas()->clear(SK_ColorWHITE);
|
||||
animation->render(surf->getCanvas());
|
||||
|
||||
SkPixmap pm;
|
||||
SkAssertResult(surf->peekPixels(&pm));
|
||||
encoder.addFrame(pm);
|
||||
}
|
||||
} while (FLAGS_loop);
|
||||
|
||||
if (FLAGS_output.count() == 0) {
|
||||
SkDebugf("missing -o output_file.mp4 argument\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
auto data = encoder.endRecording();
|
||||
SkFILEWStream ostream(FLAGS_output[0]);
|
||||
if (!ostream.isValid()) {
|
||||
SkDebugf("Can't create output file %s\n", FLAGS_output[0]);
|
||||
|
Loading…
Reference in New Issue
Block a user