07a722cdcf
Bug: b/63909536 Android's ImageDecoder API takes as input an arbitrary width and height to scale the image to. Internally, this uses SkAndroidCodec to sample, and then (if not a perfect match) scales to the desired size with drawing. computeSampledSize is a modified version of what ImageDecoder currently does to convert from arbitrary dimensions to a sampleSize. Moving it here allows it to be shared by SkAnimatedImage. The modified version also corrects two bugs: - a client using the dimensions returned by getSampledDimensions previously may have resulted in ImageDecoder decoding to a larger size and then scaling it. (example found in tests: dog.jpg is 180 x 180. getSampledDimensions(8) returns 23 x 23, but the old method resulted in using sampleSize of 7 and downscaling the resulting 25 x 25 image.) - recompute the sampleSize based on the size returned by getSampledDimensions. Change-Id: I022040e8bac31c20988903a0452257f7ae902bc7 Reviewed-on: https://skia-review.googlesource.com/94620 Reviewed-by: Derek Sollenberger <djsollen@google.com> Commit-Queue: Leon Scroggins <scroggo@google.com>
120 lines
4.7 KiB
C++
120 lines
4.7 KiB
C++
/*
|
|
* Copyright 2018 Google Inc.
|
|
*
|
|
* Use of this source code is governed by a BSD-style license that can be
|
|
* found in the LICENSE file.
|
|
*/
|
|
|
|
#include "SkAndroidCodec.h"
|
|
#include "SkCodec.h"
|
|
#include "SkEncodedImageFormat.h"
|
|
|
|
#include "Resources.h"
|
|
#include "Test.h"
|
|
|
|
static SkISize times(const SkISize& size, float factor) {
|
|
return { (int) (size.width() * factor), (int) (size.height() * factor) };
|
|
}
|
|
|
|
static SkISize plus(const SkISize& size, int term) {
|
|
return { size.width() + term, size.height() + term };
|
|
}
|
|
|
|
static bool invalid(const SkISize& size) {
|
|
return size.width() < 1 || size.height() < 1;
|
|
}
|
|
|
|
DEF_TEST(AndroidCodec_computeSampleSize, r) {
|
|
if (GetResourcePath().isEmpty()) {
|
|
return;
|
|
}
|
|
for (const char* file : { "images/color_wheel.webp",
|
|
"images/ship.png",
|
|
"images/dog.jpg",
|
|
"images/color_wheel.gif",
|
|
"images/rle.bmp",
|
|
"images/google_chrome.ico",
|
|
"images/mandrill.wbmp",
|
|
#ifdef SK_CODEC_DECODES_RAW
|
|
"images/sample_1mp.dng",
|
|
#endif
|
|
}) {
|
|
auto data = GetResourceAsData(file);
|
|
if (!data) {
|
|
ERRORF(r, "Could not get %s", file);
|
|
continue;
|
|
}
|
|
|
|
auto codec = SkAndroidCodec::MakeFromCodec(SkCodec::MakeFromData(std::move(data)));
|
|
if (!codec) {
|
|
ERRORF(r, "Could not create codec for %s", file);
|
|
continue;
|
|
}
|
|
|
|
const auto dims = codec->getInfo().dimensions();
|
|
const SkISize downscales[] = {
|
|
plus(dims, -1),
|
|
times(dims, .15f),
|
|
times(dims, .6f),
|
|
{ (int32_t) (dims.width() * .25f), (int32_t) (dims.height() * .75f ) },
|
|
{ 1, 1 },
|
|
{ 1, 2 },
|
|
{ 2, 1 },
|
|
{ 0, -1 },
|
|
{ dims.width(), dims.height() - 1 },
|
|
};
|
|
for (SkISize size : downscales) {
|
|
const auto requested = size;
|
|
const int computedSampleSize = codec->computeSampleSize(&size);
|
|
REPORTER_ASSERT(r, size.width() >= 1 && size.height() >= 1);
|
|
if (codec->getEncodedFormat() == SkEncodedImageFormat::kWEBP) {
|
|
// WebP supports arbitrary down-scaling.
|
|
REPORTER_ASSERT(r, size == requested || invalid(requested));
|
|
} else if (computedSampleSize == 1) {
|
|
REPORTER_ASSERT(r, size == dims);
|
|
} else {
|
|
REPORTER_ASSERT(r, computedSampleSize > 1);
|
|
if (size.width() >= dims.width() || size.height() >= dims.height()) {
|
|
ERRORF(r, "File %s's computed sample size (%i) is bigger than"
|
|
" original? original: %i x %i\tsampled: %i x %i",
|
|
file, computedSampleSize, dims.width(), dims.height(),
|
|
size.width(), size.height());
|
|
}
|
|
REPORTER_ASSERT(r, size.width() >= requested.width() &&
|
|
size.height() >= requested.height());
|
|
REPORTER_ASSERT(r, size.width() < dims.width() &&
|
|
size.height() < dims.height());
|
|
}
|
|
}
|
|
|
|
const SkISize upscales[] = {
|
|
dims, plus(dims, 5), times(dims, 2),
|
|
};
|
|
for (SkISize size : upscales) {
|
|
const int computedSampleSize = codec->computeSampleSize(&size);
|
|
REPORTER_ASSERT(r, computedSampleSize == 1);
|
|
REPORTER_ASSERT(r, dims == size);
|
|
}
|
|
|
|
// This mimics how Android's ImageDecoder uses SkAndroidCodec. A client
|
|
// can choose their dimensions based on calling getSampledDimensions,
|
|
// but the ImageDecoder API takes an arbitrary size. It then uses
|
|
// computeSampleSize to determine the best dimensions and sampleSize.
|
|
// It should return the same dimensions. the sampleSize may be different
|
|
// due to integer division.
|
|
for (int sampleSize : { 1, 2, 3, 4, 8, 16, 32 }) {
|
|
const SkISize sampledDims = codec->getSampledDimensions(sampleSize);
|
|
SkISize size = sampledDims;
|
|
const int computedSampleSize = codec->computeSampleSize(&size);
|
|
if (sampledDims != size) {
|
|
ERRORF(r, "File '%s'->getSampledDimensions(%i) yields computed"
|
|
" sample size of %i\n\tsampledDimensions: %i x %i\t"
|
|
"computed dimensions: %i x %i",
|
|
file, sampleSize, computedSampleSize,
|
|
sampledDims.width(), sampledDims.height(),
|
|
size.width(), size.height());
|
|
}
|
|
}
|
|
}
|
|
}
|