blob: 13863f15cb2b8109c7fa78be8e444b300c21806d [file] [log] [blame]
/*
* Copyright 2020 Google LLC
*
* Use of this source code is governed by a BSD-style license that can be
* found in the LICENSE file.
*/
#include "gm/gm.h"
#include "include/android/SkAnimatedImage.h"
#include "include/codec/SkAndroidCodec.h"
#include "include/codec/SkCodec.h"
#include "include/core/SkBlendMode.h"
#include "include/core/SkCanvas.h"
#include "include/core/SkData.h"
#include "include/core/SkImage.h"
#include "include/core/SkPathBuilder.h"
#include "include/core/SkPathTypes.h"
#include "include/core/SkPicture.h"
#include "include/core/SkPictureRecorder.h"
#include "include/core/SkRRect.h"
#include "tools/Resources.h"
static sk_sp<SkPicture> post_processor(const SkRect& bounds) {
int radius = (bounds.width() + bounds.height()) / 6;
SkPathBuilder pathBuilder;
pathBuilder.setFillType(SkPathFillType::kInverseEvenOdd)
.addRRect(SkRRect::MakeRectXY(bounds, radius, radius));
SkPaint paint;
paint.setAntiAlias(true);
paint.setColor(SK_ColorTRANSPARENT);
paint.setBlendMode(SkBlendMode::kSrc);
SkPictureRecorder recorder;
auto* canvas = recorder.beginRecording(bounds);
canvas->drawPath(pathBuilder.detach(), paint);
return recorder.finishRecordingAsPicture();
}
class AnimatedImageGM : public skiagm::GM {
const char* fPath;
const char* fName;
const int fStep;
const SkIRect fCropRect;
SkISize fSize;
int fTranslate;
sk_sp<SkData> fData;
static const int kMaxFrames = 2;
void init() {
if (!fData) {
fData = GetResourceAsData(fPath);
auto codec = SkCodec::MakeFromData(fData);
auto dimensions = codec->dimensions();
fTranslate = std::max(dimensions.width(), dimensions.height()) // may be rotated
* 1.25f // will be scaled up
+ 2; // padding
fSize = { fTranslate * kMaxFrames
* 2 // crop and no-crop
* 2, // post-process and no post-process
fTranslate * 4 // 4 scales
* 2 }; // makePictureSnapshot and getCurrentFrame
}
}
public:
AnimatedImageGM(const char* path, const char* name, int step, SkIRect cropRect)
: fPath(path)
, fName(name)
, fStep(step)
, fCropRect(cropRect)
, fSize{0, 0}
, fTranslate(0)
{}
~AnimatedImageGM() override = default;
SkString getName() const override { return SkStringPrintf("%s_animated_image", fName); }
SkISize getISize() override {
this->init();
return fSize;
}
void onDraw(SkCanvas* canvas) override {
this->init();
for (bool usePic : { true, false }) {
auto drawProc = [canvas, usePic](const sk_sp<SkAnimatedImage>& animatedImage) {
if (usePic) {
sk_sp<SkPicture> pic = animatedImage->makePictureSnapshot();
canvas->drawPicture(pic);
} else {
auto image = animatedImage->getCurrentFrame();
canvas->drawImage(image, 0, 0);
}
};
for (float scale : { 1.25f, 1.0f, .75f, .5f }) {
canvas->save();
for (bool doCrop : { false, true }) {
for (bool doPostProcess : { false, true }) {
auto codec = SkCodec::MakeFromData(fData);
const auto origin = codec->getOrigin();
auto androidCodec = SkAndroidCodec::MakeFromCodec(std::move(codec));
auto info = androidCodec->getInfo();
const auto unscaledSize = SkEncodedOriginSwapsWidthHeight(origin)
? SkISize{ info.height(), info.width() } : info.dimensions();
SkISize scaledSize = { SkScalarFloorToInt(unscaledSize.width() * scale) ,
SkScalarFloorToInt(unscaledSize.height() * scale) };
info = info.makeDimensions(scaledSize);
auto cropRect = SkIRect::MakeSize(scaledSize);
if (doCrop) {
auto matrix = SkMatrix::RectToRect(SkRect::Make(unscaledSize),
SkRect::Make(scaledSize));
matrix.preConcat(SkEncodedOriginToMatrix(origin,
unscaledSize.width(), unscaledSize.height()));
SkRect cropRectFloat = SkRect::Make(fCropRect);
matrix.mapRect(&cropRectFloat);
cropRectFloat.roundOut(&cropRect);
}
sk_sp<SkPicture> postProcessor = doPostProcess
? post_processor(SkRect::Make(cropRect.size())) : nullptr;
auto animatedImage = SkAnimatedImage::Make(std::move(androidCodec),
info, cropRect, std::move(postProcessor));
animatedImage->setRepetitionCount(0);
for (int frame = 0; frame < kMaxFrames; frame++) {
{
SkAutoCanvasRestore acr(canvas, doCrop);
if (doCrop) {
canvas->translate(cropRect.left(), cropRect.top());
}
drawProc(animatedImage);
}
canvas->translate(fTranslate, 0);
const auto duration = animatedImage->currentFrameDuration();
if (duration == SkAnimatedImage::kFinished) {
break;
}
for (int i = 0; i < fStep; i++) {
animatedImage->decodeNextFrame();
}
}
}
}
canvas->restore();
canvas->translate(0, fTranslate);
}
}
}
};
DEF_GM( return new AnimatedImageGM("images/stoplight_h.webp", "stoplight", 2,
// Deliberately not centered in X or Y, and shows all three
// lights, but otherwise arbitrary.
SkIRect::MakeLTRB(5, 6, 11, 29)); )
DEF_GM( return new AnimatedImageGM("images/flightAnim.gif", "flight", 20,
// Deliberately starts in the upper left corner to exercise
// a special case, but otherwise arbitrary.
SkIRect::MakeLTRB(0, 0, 300, 200)); )