Expose more info in SkCodec::FrameInfo
Bug: b/160984428 Add more fields to SkCodec::FrameInfo, which describes the properties of an individual frame in an animated image. This allows a client that wishes to seek to determine frame dependencies so that they can decode an arbitrary frame, which in turn will allow SkCodec to remove SkCodec::FrameInfo::fRequiredFrame. Currently, SkCodec seeks through the stream to determine frame dependencies, but this is unnecessary work (and storage) for a client that does not want to seek. These fields also support the proposed APIs in go/animated-ndk. Move SkCodecAnimation::Blend from SkCodecAnimationPriv (and delete that file) into SkCodecAnimation.h. Rename its values to be more clear. Merge common code for populating SkCodec::FrameInfo. Add a test for a GIF with offsets outside the range of the image. Note that libwebp rejects such an image. Update libgifcodec. Change-Id: Ie27e0531e7d62eaae153eccb3105bf2121b5aac4 Reviewed-on: https://skia-review.googlesource.com/c/skia/+/339857 Commit-Queue: Leon Scroggins <scroggo@google.com> Reviewed-by: Derek Sollenberger <djsollen@google.com> Reviewed-by: Nigel Tao <nigeltao@google.com>
This commit is contained in:
parent
bb1933e0d6
commit
469d67e7d9
2
DEPS
2
DEPS
@ -24,7 +24,7 @@ deps = {
|
||||
"third_party/externals/harfbuzz" : "https://chromium.googlesource.com/external/github.com/harfbuzz/harfbuzz.git@3a74ee528255cc027d84b204a87b5c25e47bff79",
|
||||
"third_party/externals/icu" : "https://chromium.googlesource.com/chromium/deps/icu.git@dbd3825b31041d782c5b504c59dcfb5ac7dda08c",
|
||||
"third_party/externals/imgui" : "https://skia.googlesource.com/external/github.com/ocornut/imgui.git@9418dcb69355558f70de260483424412c5ca2fce",
|
||||
"third_party/externals/libgifcodec" : "https://skia.googlesource.com/libgifcodec@b89e0a4edd6c0158b24730845e0e498969e22a16",
|
||||
"third_party/externals/libgifcodec" : "https://skia.googlesource.com/libgifcodec@e13b82fac077383d9f93631a177573509be44f38",
|
||||
"third_party/externals/libjpeg-turbo" : "https://chromium.googlesource.com/chromium/deps/libjpeg_turbo.git@64fc43d52351ed52143208ce6a656c03db56462b",
|
||||
"third_party/externals/libpng" : "https://skia.googlesource.com/third_party/libpng.git@386707c6d19b974ca2e3db7f5c61873813c6fe44",
|
||||
"third_party/externals/libwebp" : "https://chromium.googlesource.com/webm/libwebp.git@55a080e50af655d1fbe0a5c22954835cdd59ff92",
|
||||
|
@ -6,6 +6,9 @@ This file includes a list of high level updates for each milestone release.
|
||||
|
||||
Milestone 89
|
||||
------------
|
||||
* Expose more info in SkCodec::FrameInfo
|
||||
https://review.skia.org/339857
|
||||
|
||||
* Added dither control to the SkImageFilters::Shader factory.
|
||||
https://review.skia.org/338156
|
||||
|
||||
|
@ -644,10 +644,33 @@ public:
|
||||
*/
|
||||
SkAlphaType fAlphaType;
|
||||
|
||||
/**
|
||||
* Whether the updated rectangle contains alpha.
|
||||
*
|
||||
* This is conservative; it will still be set to true if e.g. a color
|
||||
* index-based frame has a color with alpha but does not use it. In
|
||||
* addition, it may be set to true, even if the final frame, after
|
||||
* blending, is opaque.
|
||||
*/
|
||||
bool fHasAlphaWithinBounds;
|
||||
|
||||
/**
|
||||
* How this frame should be modified before decoding the next one.
|
||||
*/
|
||||
SkCodecAnimation::DisposalMethod fDisposalMethod;
|
||||
|
||||
/**
|
||||
* How this frame should blend with the prior frame.
|
||||
*/
|
||||
SkCodecAnimation::Blend fBlend;
|
||||
|
||||
/**
|
||||
* The rectangle updated by this frame.
|
||||
*
|
||||
* It may be empty, if the frame does not change the image. It will
|
||||
* always be contained by SkCodec::dimensions().
|
||||
*/
|
||||
SkIRect fFrameRect;
|
||||
};
|
||||
|
||||
/**
|
||||
|
@ -39,5 +39,23 @@ namespace SkCodecAnimation {
|
||||
*/
|
||||
kRestorePrevious = 3,
|
||||
};
|
||||
|
||||
/**
|
||||
* How to blend the current frame.
|
||||
*/
|
||||
enum class Blend {
|
||||
/**
|
||||
* Blend with the prior frame as if using SkBlendMode::kSrcOver.
|
||||
*/
|
||||
kSrcOver,
|
||||
|
||||
/**
|
||||
* Blend with the prior frame as if using SkBlendMode::kSrc.
|
||||
*
|
||||
* This frame's pixels replace the destination pixels.
|
||||
*/
|
||||
kSrc,
|
||||
};
|
||||
|
||||
} // namespace SkCodecAnimation
|
||||
#endif // SkCodecAnimation_DEFINED
|
||||
|
BIN
resources/images/xOffsetTooBig.gif
Normal file
BIN
resources/images/xOffsetTooBig.gif
Normal file
Binary file not shown.
After Width: | Height: | Size: 258 B |
@ -765,6 +765,20 @@ const char* SkCodec::ResultToString(Result result) {
|
||||
}
|
||||
}
|
||||
|
||||
void SkFrame::fillIn(SkCodec::FrameInfo* frameInfo, bool fullyReceived) const {
|
||||
SkASSERT(frameInfo);
|
||||
|
||||
frameInfo->fRequiredFrame = fRequiredFrame;
|
||||
frameInfo->fDuration = fDuration;
|
||||
frameInfo->fFullyReceived = fullyReceived;
|
||||
frameInfo->fAlphaType = fHasAlpha ? kUnpremul_SkAlphaType
|
||||
: kOpaque_SkAlphaType;
|
||||
frameInfo->fHasAlphaWithinBounds = this->reportedAlpha() != SkEncodedInfo::kOpaque_Alpha;
|
||||
frameInfo->fDisposalMethod = fDisposalMethod;
|
||||
frameInfo->fBlend = fBlend;
|
||||
frameInfo->fFrameRect = fRect;
|
||||
}
|
||||
|
||||
static bool independent(const SkFrame& frame) {
|
||||
return frame.getRequiredFrame() == SkCodec::kNoFrame;
|
||||
}
|
||||
@ -828,7 +842,7 @@ void SkFrameHolder::setAlphaAndRequiredFrame(SkFrame* frame) {
|
||||
}
|
||||
|
||||
|
||||
const bool blendWithPrevFrame = frame->getBlend() == SkCodecAnimation::Blend::kPriorFrame;
|
||||
const bool blendWithPrevFrame = frame->getBlend() == SkCodecAnimation::Blend::kSrcOver;
|
||||
if ((!reportsAlpha || !blendWithPrevFrame) && frameRect == screenRect) {
|
||||
frame->setHasAlpha(reportsAlpha);
|
||||
frame->setRequiredFrame(SkCodec::kNoFrame); // IND2
|
||||
|
@ -1,32 +0,0 @@
|
||||
/*
|
||||
* Copyright 2016 Google Inc.
|
||||
*
|
||||
* Use of this source code is governed by a BSD-style license that can be
|
||||
* found in the LICENSE file.
|
||||
*/
|
||||
|
||||
#ifndef SkCodecAnimationPriv_DEFINED
|
||||
#define SkCodecAnimationPriv_DEFINED
|
||||
|
||||
namespace SkCodecAnimation {
|
||||
/**
|
||||
* How to blend the current frame.
|
||||
*/
|
||||
enum class Blend {
|
||||
/**
|
||||
* Blend with the prior frame. This is the typical case, supported
|
||||
* by all animated image types.
|
||||
*/
|
||||
kPriorFrame,
|
||||
|
||||
/**
|
||||
* Do not blend.
|
||||
*
|
||||
* This frames pixels overwrite previous pixels "blending" with
|
||||
* the background color of transparent.
|
||||
*/
|
||||
kBG,
|
||||
};
|
||||
|
||||
} // namespace SkCodecAnimation
|
||||
#endif // SkCodecAnimationPriv_DEFINED
|
@ -8,12 +8,12 @@
|
||||
#ifndef SkFrameHolder_DEFINED
|
||||
#define SkFrameHolder_DEFINED
|
||||
|
||||
#include "include/codec/SkCodec.h"
|
||||
#include "include/codec/SkCodecAnimation.h"
|
||||
#include "include/core/SkRect.h"
|
||||
#include "include/core/SkTypes.h"
|
||||
#include "include/private/SkEncodedInfo.h"
|
||||
#include "include/private/SkNoncopyable.h"
|
||||
#include "src/codec/SkCodecAnimationPriv.h"
|
||||
|
||||
/**
|
||||
* Base class for a single frame of an animated image.
|
||||
@ -29,7 +29,7 @@ public:
|
||||
, fRequiredFrame(kUninitialized)
|
||||
, fDisposalMethod(SkCodecAnimation::DisposalMethod::kKeep)
|
||||
, fDuration(0)
|
||||
, fBlend(SkCodecAnimation::Blend::kPriorFrame)
|
||||
, fBlend(SkCodecAnimation::Blend::kSrcOver)
|
||||
{
|
||||
fRect.setEmpty();
|
||||
}
|
||||
@ -142,6 +142,11 @@ public:
|
||||
return fBlend;
|
||||
}
|
||||
|
||||
/**
|
||||
* Fill in the FrameInfo with details from this object.
|
||||
*/
|
||||
void fillIn(SkCodec::FrameInfo*, bool fullyReceived) const;
|
||||
|
||||
protected:
|
||||
virtual SkEncodedInfo::Alpha onReportedAlpha() const = 0;
|
||||
|
||||
|
@ -348,11 +348,7 @@ bool SkHeifCodec::onGetFrameInfo(int i, FrameInfo* frameInfo) const {
|
||||
}
|
||||
|
||||
if (frameInfo) {
|
||||
frameInfo->fRequiredFrame = SkCodec::kNoFrame;
|
||||
frameInfo->fDuration = frame->getDuration();
|
||||
frameInfo->fFullyReceived = true;
|
||||
frameInfo->fAlphaType = kOpaque_SkAlphaType;
|
||||
frameInfo->fDisposalMethod = SkCodecAnimation::DisposalMethod::kKeep;
|
||||
frame->fillIn(frameInfo, true);
|
||||
}
|
||||
|
||||
return true;
|
||||
|
@ -12,7 +12,6 @@
|
||||
#include "include/core/SkCanvas.h"
|
||||
#include "include/private/SkTemplates.h"
|
||||
#include "include/private/SkTo.h"
|
||||
#include "src/codec/SkCodecAnimationPriv.h"
|
||||
#include "src/codec/SkCodecPriv.h"
|
||||
#include "src/codec/SkParseEncodedOrigin.h"
|
||||
#include "src/codec/SkSampler.h"
|
||||
@ -266,7 +265,7 @@ int SkWebpCodec::onGetFrameCount() {
|
||||
SkCodecAnimation::DisposalMethod::kKeep);
|
||||
frame->setDuration(iter.duration);
|
||||
if (WEBP_MUX_BLEND != iter.blend_method) {
|
||||
frame->setBlend(SkCodecAnimation::Blend::kBG);
|
||||
frame->setBlend(SkCodecAnimation::Blend::kSrc);
|
||||
}
|
||||
fFrameHolder.setAlphaAndRequiredFrame(frame);
|
||||
}
|
||||
@ -295,14 +294,9 @@ bool SkWebpCodec::onGetFrameInfo(int i, FrameInfo* frameInfo) const {
|
||||
}
|
||||
|
||||
if (frameInfo) {
|
||||
frameInfo->fRequiredFrame = frame->getRequiredFrame();
|
||||
frameInfo->fDuration = frame->getDuration();
|
||||
// libwebp only reports fully received frames for an
|
||||
// animated image.
|
||||
frameInfo->fFullyReceived = true;
|
||||
frameInfo->fAlphaType = frame->hasAlpha() ? kUnpremul_SkAlphaType
|
||||
: kOpaque_SkAlphaType;
|
||||
frameInfo->fDisposalMethod = frame->getDisposalMethod();
|
||||
frame->fillIn(frameInfo, true);
|
||||
}
|
||||
|
||||
return true;
|
||||
|
@ -156,7 +156,6 @@ class SkWuffsFrame final : public SkFrame {
|
||||
public:
|
||||
SkWuffsFrame(wuffs_base__frame_config* fc);
|
||||
|
||||
SkCodec::FrameInfo frameInfo(bool fullyReceived) const;
|
||||
uint64_t ioPosition() const;
|
||||
|
||||
// SkFrame overrides.
|
||||
@ -332,18 +331,8 @@ SkWuffsFrame::SkWuffsFrame(wuffs_base__frame_config* fc)
|
||||
this->setXYWH(r.min_incl_x, r.min_incl_y, r.width(), r.height());
|
||||
this->setDisposalMethod(wuffs_disposal_to_skia_disposal(fc->disposal()));
|
||||
this->setDuration(fc->duration() / WUFFS_BASE__FLICKS_PER_MILLISECOND);
|
||||
this->setBlend(fc->overwrite_instead_of_blend() ? SkCodecAnimation::Blend::kBG
|
||||
: SkCodecAnimation::Blend::kPriorFrame);
|
||||
}
|
||||
|
||||
SkCodec::FrameInfo SkWuffsFrame::frameInfo(bool fullyReceived) const {
|
||||
SkCodec::FrameInfo ret;
|
||||
ret.fRequiredFrame = getRequiredFrame();
|
||||
ret.fDuration = getDuration();
|
||||
ret.fFullyReceived = fullyReceived;
|
||||
ret.fAlphaType = hasAlpha() ? kUnpremul_SkAlphaType : kOpaque_SkAlphaType;
|
||||
ret.fDisposalMethod = getDisposalMethod();
|
||||
return ret;
|
||||
this->setBlend(fc->overwrite_instead_of_blend() ? SkCodecAnimation::Blend::kSrc
|
||||
: SkCodecAnimation::Blend::kSrcOver);
|
||||
}
|
||||
|
||||
uint64_t SkWuffsFrame::ioPosition() const {
|
||||
@ -854,7 +843,7 @@ bool SkWuffsCodec::onGetFrameInfo(int i, SkCodec::FrameInfo* frameInfo) const {
|
||||
return false;
|
||||
}
|
||||
if (frameInfo) {
|
||||
*frameInfo = f->frameInfo(static_cast<uint64_t>(i) < this->fNumFullyReceivedFrames);
|
||||
f->fillIn(frameInfo, static_cast<uint64_t>(i) < this->fNumFullyReceivedFrames);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
@ -86,7 +86,7 @@ sk_sp<SkImage> SkAnimCodecPlayer::getFrameAt(int index) {
|
||||
// of transparent black, and then draw that through the origin matrix
|
||||
// onto the required frame. To do that, SkCodec needs to expose the
|
||||
// rectangle of the delta and the blend mode, so we can handle
|
||||
// kRestoreBGColor frames and Blend::kBG.
|
||||
// kRestoreBGColor frames and Blend::kSrc.
|
||||
SkMatrix inverse;
|
||||
SkAssertResult(originMatrix.invert(&inverse));
|
||||
canvas->concat(inverse);
|
||||
|
@ -67,6 +67,31 @@ static bool restore_previous(const SkCodec::FrameInfo& info) {
|
||||
return info.fDisposalMethod == SkCodecAnimation::DisposalMethod::kRestorePrevious;
|
||||
}
|
||||
|
||||
namespace {
|
||||
SkString to_string(bool boolean) { return boolean ? SkString("true") : SkString("false"); }
|
||||
SkString to_string(SkCodecAnimation::Blend blend) {
|
||||
switch (blend) {
|
||||
case SkCodecAnimation::Blend::kSrcOver:
|
||||
return SkString("kSrcOver");
|
||||
case SkCodecAnimation::Blend::kSrc:
|
||||
return SkString("kSrc");
|
||||
default:
|
||||
return SkString();
|
||||
}
|
||||
}
|
||||
SkString to_string(SkIRect rect) {
|
||||
return SkStringPrintf("{ %i, %i, %i, %i }", rect.fLeft, rect.fTop, rect.fRight, rect.fBottom);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
void reporter_assert_equals(skiatest::Reporter* r, const char* name, int i, const char* prop,
|
||||
T expected, T actual) {
|
||||
REPORTER_ASSERT(r, expected == actual, "%s's frame %i has wrong %s! expected:"
|
||||
" %s\tactual: %s", name, i, prop, to_string(expected).c_str(),
|
||||
to_string(actual).c_str());
|
||||
}
|
||||
} // namespace
|
||||
|
||||
DEF_TEST(Codec_frames, r) {
|
||||
constexpr int kNoFrame = SkCodec::kNoFrame;
|
||||
constexpr SkAlphaType kOpaque = kOpaque_SkAlphaType;
|
||||
@ -77,6 +102,8 @@ DEF_TEST(Codec_frames, r) {
|
||||
SkCodecAnimation::DisposalMethod::kRestoreBGColor;
|
||||
constexpr SkCodecAnimation::DisposalMethod kRestorePrev =
|
||||
SkCodecAnimation::DisposalMethod::kRestorePrevious;
|
||||
constexpr auto kSrcOver = SkCodecAnimation::Blend::kSrcOver;
|
||||
constexpr auto kSrc = SkCodecAnimation::Blend::kSrc;
|
||||
|
||||
static const struct {
|
||||
const char* fName;
|
||||
@ -91,13 +118,22 @@ DEF_TEST(Codec_frames, r) {
|
||||
std::vector<int> fDurations;
|
||||
int fRepetitionCount;
|
||||
std::vector<SkCodecAnimation::DisposalMethod> fDisposalMethods;
|
||||
std::vector<bool> fAlphaWithinBounds;
|
||||
std::vector<SkCodecAnimation::Blend> fBlends;
|
||||
std::vector<SkIRect> fFrameRects;
|
||||
} gRecs[] = {
|
||||
{ "images/required.gif", 7,
|
||||
{ 0, 1, 2, 3, 4, 5 },
|
||||
{ kOpaque, kUnpremul, kUnpremul, kUnpremul, kUnpremul, kUnpremul },
|
||||
{ 100, 100, 100, 100, 100, 100, 100 },
|
||||
0,
|
||||
{ kKeep, kRestoreBG, kKeep, kKeep, kKeep, kRestoreBG, kKeep } },
|
||||
{ kKeep, kRestoreBG, kKeep, kKeep, kKeep, kRestoreBG, kKeep },
|
||||
{ false, true, true, true, true, true, true },
|
||||
{ kSrcOver, kSrcOver, kSrcOver, kSrcOver, kSrcOver, kSrcOver,
|
||||
kSrcOver },
|
||||
{ {0, 0, 100, 100}, {0, 0, 75, 75}, {0, 0, 50, 50}, {0, 0, 60, 60},
|
||||
{0, 0, 100, 100}, {0, 0, 50, 50}, {0, 0, 75, 75}},
|
||||
},
|
||||
{ "images/alphabetAnim.gif", 13,
|
||||
{ kNoFrame, 0, 0, 0, 0, 5, 6, kNoFrame, kNoFrame, 9, 10, 11 },
|
||||
{ kUnpremul, kUnpremul, kUnpremul, kUnpremul, kUnpremul, kUnpremul,
|
||||
@ -106,7 +142,16 @@ DEF_TEST(Codec_frames, r) {
|
||||
0,
|
||||
{ kKeep, kRestorePrev, kRestorePrev, kRestorePrev, kRestorePrev,
|
||||
kRestoreBG, kKeep, kRestoreBG, kRestoreBG, kKeep, kKeep,
|
||||
kRestoreBG, kKeep } },
|
||||
kRestoreBG, kKeep },
|
||||
{ true, false, true, false, true, true, true, true, true, true, true, true, true },
|
||||
{ kSrcOver, kSrcOver, kSrcOver, kSrcOver, kSrcOver, kSrcOver,
|
||||
kSrcOver, kSrcOver, kSrcOver, kSrcOver, kSrcOver, kSrcOver,
|
||||
kSrcOver },
|
||||
{ {25, 25, 75, 75}, {25, 25, 75, 75}, {25, 25, 75, 75}, {37, 37, 62, 62},
|
||||
{37, 37, 62, 62}, {25, 25, 75, 75}, {0, 0, 50, 50}, {0, 0, 100, 100},
|
||||
{25, 25, 75, 75}, {25, 25, 75, 75}, {0, 0, 100, 100}, {25, 25, 75, 75},
|
||||
{37, 37, 62, 62}},
|
||||
},
|
||||
{ "images/randPixelsAnim2.gif", 4,
|
||||
// required frames
|
||||
{ 0, 0, 1 },
|
||||
@ -116,7 +161,11 @@ DEF_TEST(Codec_frames, r) {
|
||||
{ 0, 1000, 170, 40 },
|
||||
// repetition count
|
||||
0,
|
||||
{ kKeep, kKeep, kRestorePrev, kKeep } },
|
||||
{ kKeep, kKeep, kRestorePrev, kKeep },
|
||||
{ false, true, false, false },
|
||||
{ kSrcOver, kSrcOver, kSrcOver, kSrcOver },
|
||||
{ {0, 0, 8, 8}, {6, 6, 8, 8}, {4, 4, 8, 8}, {7, 0, 8, 8} },
|
||||
},
|
||||
{ "images/randPixelsAnim.gif", 13,
|
||||
// required frames
|
||||
{ 0, 1, 2, 3, 4, 3, 6, 7, 7, 7, 9, 9 },
|
||||
@ -128,41 +177,70 @@ DEF_TEST(Codec_frames, r) {
|
||||
0,
|
||||
{ kKeep, kKeep, kKeep, kKeep, kRestoreBG, kRestoreBG, kRestoreBG,
|
||||
kRestoreBG, kRestorePrev, kRestoreBG, kRestorePrev, kRestorePrev,
|
||||
kRestorePrev, } },
|
||||
{ "images/box.gif", 1, {}, {}, {}, 0, { kKeep } },
|
||||
{ "images/color_wheel.gif", 1, {}, {}, {}, 0, { kKeep } },
|
||||
kRestorePrev, },
|
||||
{ false, true, true, false, true, true, false, false, true, true, false, false,
|
||||
true },
|
||||
{ kSrcOver, kSrcOver, kSrcOver, kSrcOver, kSrcOver, kSrcOver,
|
||||
kSrcOver, kSrcOver, kSrcOver, kSrcOver, kSrcOver, kSrcOver,
|
||||
kSrcOver },
|
||||
{ {4, 4, 12, 12}, {4, 4, 12, 12}, {4, 4, 12, 12}, {0, 0, 8, 8}, {8, 8, 16, 16},
|
||||
{8, 8, 16, 16}, {8, 8, 16, 16}, {2, 2, 10, 10}, {7, 7, 15, 15}, {7, 7, 15, 15},
|
||||
{7, 7, 15, 15}, {0, 0, 8, 8}, {14, 14, 16, 16} },
|
||||
},
|
||||
{ "images/box.gif", 1, {}, {}, {}, 0, { kKeep }, {}, {}, {} },
|
||||
{ "images/color_wheel.gif", 1, {}, {}, {}, 0, { kKeep }, {}, {}, {} },
|
||||
{ "images/test640x479.gif", 4, { 0, 1, 2 },
|
||||
{ kOpaque, kOpaque, kOpaque },
|
||||
{ 200, 200, 200, 200 },
|
||||
SkCodec::kRepetitionCountInfinite,
|
||||
{ kKeep, kKeep, kKeep, kKeep } },
|
||||
{ kKeep, kKeep, kKeep, kKeep },
|
||||
{ false, true, true, true },
|
||||
{ kSrcOver, kSrcOver, kSrcOver, kSrcOver },
|
||||
{ {0, 0, 640, 479}, {0, 0, 640, 479}, {0, 0, 640, 479}, {0, 0, 640, 479} },
|
||||
},
|
||||
{ "images/colorTables.gif", 2, { 0 }, { kOpaque }, { 1000, 1000 }, 5,
|
||||
{ kKeep, kKeep } },
|
||||
{ kKeep, kKeep }, {false, true}, { kSrcOver, kSrcOver },
|
||||
{ {0, 0, 640, 400}, {0, 0, 640, 200}},
|
||||
},
|
||||
|
||||
{ "images/arrow.png", 1, {}, {}, {}, 0, {} },
|
||||
{ "images/google_chrome.ico", 1, {}, {}, {}, 0, {} },
|
||||
{ "images/brickwork-texture.jpg", 1, {}, {}, {}, 0, {} },
|
||||
{ "images/arrow.png", 1, {}, {}, {}, 0, {}, {}, {}, {} },
|
||||
{ "images/google_chrome.ico", 1, {}, {}, {}, 0, {}, {}, {}, {} },
|
||||
{ "images/brickwork-texture.jpg", 1, {}, {}, {}, 0, {}, {}, {}, {} },
|
||||
#if defined(SK_CODEC_DECODES_RAW) && (!defined(_WIN32))
|
||||
{ "images/dng_with_preview.dng", 1, {}, {}, {}, 0, {} },
|
||||
{ "images/dng_with_preview.dng", 1, {}, {}, {}, 0, {}, {}, {}, {} },
|
||||
#endif
|
||||
{ "images/mandrill.wbmp", 1, {}, {}, {}, 0, {} },
|
||||
{ "images/randPixels.bmp", 1, {}, {}, {}, 0, {} },
|
||||
{ "images/yellow_rose.webp", 1, {}, {}, {}, 0, {} },
|
||||
{ "images/mandrill.wbmp", 1, {}, {}, {}, 0, {}, {}, {}, {} },
|
||||
{ "images/randPixels.bmp", 1, {}, {}, {}, 0, {}, {}, {}, {} },
|
||||
{ "images/yellow_rose.webp", 1, {}, {}, {}, 0, {}, {}, {}, {} },
|
||||
{ "images/stoplight.webp", 3, { 0, 1 }, { kOpaque, kOpaque },
|
||||
{ 1000, 500, 1000 }, SkCodec::kRepetitionCountInfinite,
|
||||
{ kKeep, kKeep, kKeep } },
|
||||
{ kKeep, kKeep, kKeep }, {false, false, false},
|
||||
{kSrcOver, kSrcOver, kSrcOver},
|
||||
{ {0, 0, 11, 29}, {2, 10, 9, 27}, {2, 2, 9, 18}},
|
||||
},
|
||||
{ "images/blendBG.webp", 7,
|
||||
{ 0, kNoFrame, kNoFrame, kNoFrame, 4, 4 },
|
||||
{ kOpaque, kOpaque, kUnpremul, kOpaque, kUnpremul, kUnpremul },
|
||||
{ 525, 500, 525, 437, 609, 729, 444 },
|
||||
6,
|
||||
{ kKeep, kKeep, kKeep, kKeep, kKeep, kKeep, kKeep } },
|
||||
{ kKeep, kKeep, kKeep, kKeep, kKeep, kKeep, kKeep },
|
||||
{ false, true, false, true, false, true, true },
|
||||
{ kSrc, kSrcOver, kSrc, kSrc, kSrc, kSrc, kSrc },
|
||||
{ {0, 0, 200, 200}, {0, 0, 200, 200}, {0, 0, 200, 200}, {0, 0, 200, 200},
|
||||
{0, 0, 200, 200}, {100, 100, 200, 200}, {100, 100, 200, 200} },
|
||||
},
|
||||
{ "images/required.webp", 7,
|
||||
{ 0, 1, 1, kNoFrame, 4, 4 },
|
||||
{ kOpaque, kUnpremul, kUnpremul, kOpaque, kOpaque, kOpaque },
|
||||
{ 100, 100, 100, 100, 100, 100, 100 },
|
||||
0,
|
||||
{ kKeep, kRestoreBG, kKeep, kKeep, kKeep, kRestoreBG, kKeep } },
|
||||
{ kKeep, kRestoreBG, kKeep, kKeep, kKeep, kRestoreBG, kKeep },
|
||||
{ false, false, false, false, false, false, false },
|
||||
{ kSrc, kSrcOver, kSrcOver, kSrcOver, kSrc, kSrcOver,
|
||||
kSrcOver },
|
||||
{ {0, 0, 100, 100}, {0, 0, 75, 75}, {0, 0, 50, 50}, {0, 0, 60, 60},
|
||||
{0, 0, 100, 100}, {0, 0, 50, 50}, {0, 0, 75, 75}},
|
||||
},
|
||||
};
|
||||
|
||||
for (const auto& rec : gRecs) {
|
||||
@ -296,6 +374,16 @@ DEF_TEST(Codec_frames, r) {
|
||||
}
|
||||
|
||||
REPORTER_ASSERT(r, frameInfo.fDisposalMethod == rec.fDisposalMethods[i]);
|
||||
|
||||
reporter_assert_equals<bool>(r, rec.fName, i, "alpha within bounds",
|
||||
rec.fAlphaWithinBounds[i],
|
||||
frameInfo.fHasAlphaWithinBounds);
|
||||
|
||||
reporter_assert_equals(r, rec.fName, i, "blend mode", rec.fBlends[i],
|
||||
frameInfo.fBlend);
|
||||
|
||||
reporter_assert_equals(r, rec.fName, i, "frame rect", rec.fFrameRects[i],
|
||||
frameInfo.fFrameRect);
|
||||
}
|
||||
|
||||
if (TestMode::kIndividual == mode) {
|
||||
|
@ -601,3 +601,46 @@ DEF_TEST(Codec_AnimatedTransparentGif, r) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// This test verifies that a GIF frame outside the image dimensions is handled
|
||||
// as desired:
|
||||
// - The image reports a size of 0 x 0, but the first frame is 100 x 90. The
|
||||
// image (or "canvas") is expanded to fit the first frame. The first frame is red.
|
||||
// - The second frame is a green 75 x 75 rectangle, reporting its x-offset and
|
||||
// y-offset to be 105, placing it off screen. The decoder interprets this as no
|
||||
// change from the first frame.
|
||||
DEF_TEST(Codec_xOffsetTooBig, r) {
|
||||
const char* path = "images/xOffsetTooBig.gif";
|
||||
auto data = GetResourceAsData(path);
|
||||
if (!data) {
|
||||
ERRORF(r, "failed to find %s", path);
|
||||
return;
|
||||
}
|
||||
|
||||
auto codec = SkCodec::MakeFromData(std::move(data));
|
||||
if (!codec) {
|
||||
ERRORF(r, "Could not create codec from %s", path);
|
||||
return;
|
||||
}
|
||||
|
||||
REPORTER_ASSERT(r, codec->getFrameCount() == 2);
|
||||
|
||||
auto info = codec->getInfo();
|
||||
REPORTER_ASSERT(r, info.width() == 100 && info.height() == 90);
|
||||
|
||||
SkBitmap bm;
|
||||
bm.allocPixels(info);
|
||||
for (int i = 0; i < 2; i++) {
|
||||
SkCodec::FrameInfo frameInfo;
|
||||
REPORTER_ASSERT(r, codec->getFrameInfo(i, &frameInfo));
|
||||
|
||||
SkIRect expectedRect = i == 0 ? SkIRect{0, 0, 100, 90} : SkIRect{100, 90, 100, 90};
|
||||
REPORTER_ASSERT(r, expectedRect == frameInfo.fFrameRect);
|
||||
|
||||
SkCodec::Options options;
|
||||
options.fFrameIndex = i;
|
||||
REPORTER_ASSERT(r, SkCodec::kSuccess == codec->getPixels(bm.pixmap(), &options));
|
||||
|
||||
REPORTER_ASSERT(r, bm.getColor(0, 0) == SK_ColorRED);
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user