Skip to content

Commit

Permalink
[viewer] Add support for animated GIF, WEBP
Browse files Browse the repository at this point in the history
 - repurpose AnimatedImageSlide as a general animated image container
 - add support for loading gif and webp assets

E.g.: viewer -f image.webp

Change-Id: Iaf21f3b38a14728ba53edb6def050d0ede9d0300
Reviewed-on: https://skia-review.googlesource.com/c/skia/+/929776
Reviewed-by: Daniel Dilan <[email protected]>
Commit-Queue: Florin Malita <[email protected]>
  • Loading branch information
fmalita authored and SkCQ committed Dec 16, 2024
1 parent 7fc6934 commit 2c51595
Show file tree
Hide file tree
Showing 7 changed files with 119 additions and 111 deletions.
2 changes: 2 additions & 0 deletions BUILD.gn
Original file line number Diff line number Diff line change
Expand Up @@ -3118,6 +3118,7 @@ if (skia_enable_tools) {
"tools/viewer/AnimBlurSlide.cpp",
"tools/viewer/AnimTimer.h",
"tools/viewer/AnimatedImageSlide.cpp",
"tools/viewer/AnimatedImageSlide.h",
"tools/viewer/AnimatedRectsSlide.cpp",
"tools/viewer/AnimatedTextSlide.cpp",
"tools/viewer/ArcSlide.cpp",
Expand Down Expand Up @@ -3225,6 +3226,7 @@ if (skia_enable_tools) {
"modules/skottie",
"modules/skottie:utils",
"modules/skparagraph:slides",
"modules/skresources",
"modules/sksg:slides",
"modules/skshaper:skshaper",
"modules/skunicode",
Expand Down
4 changes: 3 additions & 1 deletion modules/skresources/include/SkResources.h
Original file line number Diff line number Diff line change
Expand Up @@ -108,9 +108,11 @@ class MultiFrameImageAsset final : public ImageAsset {
static sk_sp<MultiFrameImageAsset> Make(std::unique_ptr<SkCodec>,
ImageDecodeStrategy = ImageDecodeStrategy::kLazyDecode);


bool isMultiFrame() override;

// Animation duration, in ms.
float duration() const;

sk_sp<SkImage> getFrame(float t) override;

private:
Expand Down
6 changes: 3 additions & 3 deletions modules/skresources/src/SkResources.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -128,9 +128,9 @@ MultiFrameImageAsset::MultiFrameImageAsset(std::unique_ptr<SkAnimCodecPlayer> pl
SkASSERT(fPlayer);
}

bool MultiFrameImageAsset::isMultiFrame() {
return fPlayer->duration() > 0;
}
bool MultiFrameImageAsset::isMultiFrame() { return fPlayer->duration() > 0; }

float MultiFrameImageAsset::duration() const { return fPlayer->duration(); }

sk_sp<SkImage> MultiFrameImageAsset::generateFrame(float t) {
auto decode = [](sk_sp<SkImage> image) {
Expand Down
165 changes: 59 additions & 106 deletions tools/viewer/AnimatedImageSlide.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -5,119 +5,72 @@
* found in the LICENSE file.
*/

#include "include/android/SkAnimatedImage.h"
#include "include/codec/SkAndroidCodec.h"
#include "include/core/SkCanvas.h"
#include "include/core/SkFont.h"
#include "include/core/SkPaint.h"
#include "include/core/SkPictureRecorder.h"
#include "include/core/SkRect.h"
#include "include/core/SkScalar.h"
#include "include/core/SkString.h"
#include "include/core/SkData.h"
#include "include/core/SkImage.h"
#include "modules/skresources/include/SkResources.h"
#include "tools/Resources.h"
#include "tools/fonts/FontToolUtils.h"
#include "tools/timer/TimeUtils.h"
#include "tools/viewer/Slide.h"

static constexpr char kPauseKey = 'p';
static constexpr char kResetKey = 'r';

class AnimatedImageSlide : public Slide {
sk_sp<SkAnimatedImage> fImage;
sk_sp<SkDrawable> fDrawable;
SkScalar fYOffset = 0;
bool fRunning = false;
double fCurrentTime = 0.0;
double fLastWallTime = 0.0;
double fTimeToShowNextFrame = 0.0;

public:
AnimatedImageSlide() { fName = "AnimatedImage"; }

void draw(SkCanvas* canvas) override {
SkFont font = ToolUtils::DefaultFont();
font.setSize(20);

SkString str = SkStringPrintf("Press '%c' to start/pause; '%c' to reset.",
kPauseKey, kResetKey);
const char* text = str.c_str();
SkRect bounds;
font.measureText(text, strlen(text), SkTextEncoding::kUTF8, &bounds);
fYOffset = bounds.height();

canvas->drawSimpleText(text, strlen(text), SkTextEncoding::kUTF8, 5, fYOffset, font, SkPaint());
fYOffset *= 2;

if (!fImage) {
return;
}

canvas->translate(0, fYOffset);

canvas->drawDrawable(fImage.get());
canvas->drawDrawable(fDrawable.get(), fImage->getBounds().width(), 0);
#include "tools/viewer/AnimatedImageSlide.h"
#include <cmath>

AnimatedImageSlide::AnimatedImageSlide(const SkString& name, const SkString& path)
: fPath(path)
{
fName = name;
}

void AnimatedImageSlide::load(SkScalar w, SkScalar h) {
fWinSize = {w, h};

// Try loading both as a resource and as a regular file.
sk_sp<SkData> data = GetResourceAsData(fPath.c_str());
if (!data) {
data = SkData::MakeFromFileName(fPath.c_str());
}

bool animate(double nanos) override {
if (!fImage) {
return false;
}

const double lastWallTime = fLastWallTime;
fLastWallTime = TimeUtils::NanosToMSec(nanos);

if (fRunning) {
fCurrentTime += fLastWallTime - lastWallTime;
if (fCurrentTime > fTimeToShowNextFrame) {
fTimeToShowNextFrame += fImage->decodeNextFrame();
if (fImage->isFinished()) {
fRunning = false;
}
}
}

return true;
}
fImageAsset = skresources::MultiFrameImageAsset::Make(std::move(data));
}

void AnimatedImageSlide::unload() {
fImageAsset.reset();
fTimeBase = 0;
}

void load(SkScalar w, SkScalar h) override {
sk_sp<SkData> file(GetResourceAsData("images/alphabetAnim.gif"));
std::unique_ptr<SkCodec> codec(SkCodec::MakeFromData(file));
if (!codec) {
return;
}

fImage = SkAnimatedImage::Make(SkAndroidCodec::MakeFromCodec(std::move(codec)));
if (!fImage) {
return;
}

fTimeToShowNextFrame = fImage->currentFrameDuration();
SkPictureRecorder recorder;
auto canvas = recorder.beginRecording(fImage->getBounds());
canvas->drawDrawable(fImage.get());
fDrawable = recorder.finishRecordingAsDrawable();
void AnimatedImageSlide::draw(SkCanvas* canvas) {
if (!fImageAsset) {
return;
}

bool onChar(SkUnichar uni) override {
if (fImage) {
switch (uni) {
case kPauseKey:
fRunning = !fRunning;
if (!fImage->isFinished()) {
return true;
}
[[fallthrough]];
case kResetKey:
fImage->reset();
fCurrentTime = fLastWallTime;
fTimeToShowNextFrame = fCurrentTime + fImage->currentFrameDuration();
return true;
default:
break;
}
}
sk_sp<SkImage> frame = fImageAsset->getFrame(fFrameMs * 0.001f);

SkAutoCanvasRestore acr(canvas, true);
canvas->translate((fWinSize.width() - frame->width()) / 2,
(fWinSize.height() - frame->height()) / 2);

SkPaint outline_paint;
outline_paint.setAntiAlias(true);
outline_paint.setColor(0x80000000);
outline_paint.setStyle(SkPaint::kStroke_Style);

const SkRect outline = SkRect::Make(frame->bounds()).makeOutset(1, 1);
canvas->drawRect(outline, outline_paint);

canvas->drawImage(frame, 0, 0);
}

bool AnimatedImageSlide::animate(double nanos) {
if (!fImageAsset || !fImageAsset->isMultiFrame()) {
return false;
}
};

DEF_SLIDE( return new AnimatedImageSlide(); )
if (!fTimeBase) {
fTimeBase = nanos;
}

fFrameMs = std::fmod((nanos - fTimeBase) * 0.000001f, fImageAsset->duration());

return true;
}

DEF_SLIDE( return new AnimatedImageSlide(SkString("AnimatedImage"),
SkString("images/alphabetAnim.gif")); )
36 changes: 36 additions & 0 deletions tools/viewer/AnimatedImageSlide.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
/*
* Copyright 2024 Google Inc.
*
* Use of this source code is governed by a BSD-style license that can be
* found in the LICENSE file.
*/

#ifndef AnimatedImageSlide_DEFINED
#define AnimatedImageSlide_DEFINED

#include "include/core/SkString.h"
#include "modules/skresources/include/SkResources.h"
#include "tools/viewer/Slide.h"

class SkCanvas;

class AnimatedImageSlide final : public Slide {
public:
AnimatedImageSlide(const SkString& name, const SkString& path);

void load(SkScalar winWidth, SkScalar winHeight) override;
void unload() override;

void draw(SkCanvas*) override;
bool animate(double nanos) override;

private:
const SkString fPath;
sk_sp<skresources::MultiFrameImageAsset> fImageAsset;
SkSize fWinSize;

double fTimeBase = 0;
float fFrameMs = 0;
};

#endif // AnimatedImageSlide_DEFINED
3 changes: 3 additions & 0 deletions tools/viewer/BUILD.bazel
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@ skia_cc_binary(
srcs = [
"AnimBlurSlide.cpp",
"AnimTimer.h",
"AnimatedImageSlide.cpp",
"AnimatedImageSlide.h",
"AnimatedRectsSlide.cpp",
"AnimatedTextSlide.cpp",
"ArcSlide.cpp",
Expand Down Expand Up @@ -113,6 +115,7 @@ skia_cc_binary(
"//modules/skottie",
"//modules/skottie/utils:skottie_utils",
"//modules/skottie/utils:text_editor",
"//modules/skresources",
"//modules/sksg",
"//src/sksl/tracing:player",
"//tools:codec_utils",
Expand Down
14 changes: 13 additions & 1 deletion tools/viewer/Viewer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,7 @@
#include "tools/skui/Key.h"
#include "tools/skui/ModifierKey.h"
#include "tools/trace/EventTracingPriv.h"
#include "tools/viewer/AnimatedImageSlide.h"
#include "tools/viewer/BisectSlide.h"
#include "tools/viewer/GMSlide.h"
#include "tools/viewer/ImageSlide.h"
Expand Down Expand Up @@ -1004,6 +1005,12 @@ void Viewer::initSlides() {
[](const SkString& name, const SkString& path) -> sk_sp<Slide> {
return sk_make_sp<SKPSlide>(name, path);
}},
{".gif",
"gif-dir",
FLAGS_jpgs,
[](const SkString& name, const SkString& path) -> sk_sp<Slide> {
return sk_make_sp<AnimatedImageSlide>(name, path);
}},
{".jpg",
"jpg-dir",
FLAGS_jpgs,
Expand All @@ -1016,6 +1023,12 @@ void Viewer::initSlides() {
[](const SkString& name, const SkString& path) -> sk_sp<Slide> {
return sk_make_sp<ImageSlide>(name, path);
}},
{".webp",
"webp-dir",
FLAGS_jpgs,
[](const SkString& name, const SkString& path) -> sk_sp<Slide> {
return sk_make_sp<AnimatedImageSlide>(name, path);
}},
#if defined(SK_ENABLE_SKOTTIE)
{".json",
"skottie-dir",
Expand All @@ -1024,7 +1037,6 @@ void Viewer::initSlides() {
return sk_make_sp<SkottieSlide>(name, path);
}},
#endif

#if defined(SK_ENABLE_SVG)
{".svg",
"svg-dir",
Expand Down

0 comments on commit 2c51595

Please sign in to comment.