123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301 |
- /*
- * 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 "include/codec/SkAndroidCodec.h"
- #include "include/codec/SkCodec.h"
- #include "include/core/SkBitmap.h"
- #include "include/core/SkColor.h"
- #include "include/core/SkColorSpace.h"
- #include "include/core/SkData.h"
- #include "include/core/SkEncodedImageFormat.h"
- #include "include/core/SkImageGenerator.h"
- #include "include/core/SkImageInfo.h"
- #include "include/core/SkRefCnt.h"
- #include "include/core/SkSize.h"
- #include "include/core/SkString.h"
- #include "include/core/SkTypes.h"
- #include "include/third_party/skcms/skcms.h"
- #include "src/codec/SkCodecImageGenerator.h"
- #include "src/core/SkPixmapPriv.h"
- #include "tests/Test.h"
- #include "tools/Resources.h"
- #include <string.h>
- #include <initializer_list>
- #include <memory>
- #include <utility>
- 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());
- }
- }
- }
- }
- DEF_TEST(AndroidCodec_wide, r) {
- if (GetResourcePath().isEmpty()) {
- return;
- }
- const char* path = "images/wide-gamut.png";
- auto data = GetResourceAsData(path);
- if (!data) {
- ERRORF(r, "Missing file %s", path);
- return;
- }
- auto codec = SkAndroidCodec::MakeFromCodec(SkCodec::MakeFromData(std::move(data)));
- if (!codec) {
- ERRORF(r, "Failed to create codec from %s", path);
- return;
- }
- auto info = codec->getInfo();
- auto cs = codec->computeOutputColorSpace(info.colorType(), nullptr);
- if (!cs) {
- ERRORF(r, "%s should have a color space", path);
- return;
- }
- auto expected = SkColorSpace::MakeRGB(SkNamedTransferFn::kSRGB, SkNamedGamut::kDCIP3);
- REPORTER_ASSERT(r, SkColorSpace::Equals(cs.get(), expected.get()));
- }
- DEF_TEST(AndroidCodec_P3, r) {
- if (GetResourcePath().isEmpty()) {
- return;
- }
- const char* path = "images/purple-displayprofile.png";
- auto data = GetResourceAsData(path);
- if (!data) {
- ERRORF(r, "Missing file %s", path);
- return;
- }
- auto codec = SkAndroidCodec::MakeFromCodec(SkCodec::MakeFromData(std::move(data)));
- if (!codec) {
- ERRORF(r, "Failed to create codec from %s", path);
- return;
- }
- auto info = codec->getInfo();
- auto cs = codec->computeOutputColorSpace(info.colorType(), nullptr);
- if (!cs) {
- ERRORF(r, "%s should have a color space", path);
- return;
- }
- REPORTER_ASSERT(r, !cs->isSRGB());
- REPORTER_ASSERT(r, cs->gammaCloseToSRGB());
- skcms_Matrix3x3 matrix;
- cs->toXYZD50(&matrix);
- static constexpr skcms_Matrix3x3 kExpected = {{
- { 0.426254272f, 0.369018555f, 0.168914795f },
- { 0.226013184f, 0.685974121f, 0.0880126953f },
- { 0.0116729736f, 0.0950927734f, 0.71812439f },
- }};
- REPORTER_ASSERT(r, 0 == memcmp(&matrix, &kExpected, sizeof(skcms_Matrix3x3)));
- }
- DEF_TEST(AndroidCodec_orientation, r) {
- if (GetResourcePath().isEmpty()) {
- return;
- }
- for (const char* ext : { "jpg", "webp" })
- for (char i = '1'; i <= '8'; ++i) {
- SkString path = SkStringPrintf("images/orientation/%c.%s", i, ext);
- auto data = GetResourceAsData(path.c_str());
- auto gen = SkCodecImageGenerator::MakeFromEncodedCodec(data);
- if (!gen) {
- ERRORF(r, "failed to decode %s", path.c_str());
- return;
- }
- // Dimensions after adjusting for the origin.
- const SkISize expectedDims = { 100, 80 };
- // SkCodecImageGenerator automatically adjusts for the origin.
- REPORTER_ASSERT(r, gen->getInfo().dimensions() == expectedDims);
- auto androidCodec = SkAndroidCodec::MakeFromCodec(SkCodec::MakeFromData(data));
- if (!androidCodec) {
- ERRORF(r, "failed to decode %s", path.c_str());
- return;
- }
- // SkAndroidCodec does not adjust for the origin by default. Dimensions may be reversed.
- if (SkPixmapPriv::ShouldSwapWidthHeight(androidCodec->codec()->getOrigin())) {
- auto swappedDims = SkPixmapPriv::SwapWidthHeight(androidCodec->getInfo()).dimensions();
- REPORTER_ASSERT(r, expectedDims == swappedDims);
- } else {
- REPORTER_ASSERT(r, expectedDims == androidCodec->getInfo().dimensions());
- }
- // Passing kRespect adjusts for the origin.
- androidCodec = SkAndroidCodec::MakeFromCodec(SkCodec::MakeFromData(std::move(data)),
- SkAndroidCodec::ExifOrientationBehavior::kRespect);
- auto info = androidCodec->getInfo();
- REPORTER_ASSERT(r, info.dimensions() == expectedDims);
- SkBitmap fromGenerator;
- fromGenerator.allocPixels(info);
- REPORTER_ASSERT(r, gen->getPixels(info, fromGenerator.getPixels(),
- fromGenerator.rowBytes()));
- SkBitmap fromAndroidCodec;
- fromAndroidCodec.allocPixels(info);
- auto result = androidCodec->getPixels(info, fromAndroidCodec.getPixels(),
- fromAndroidCodec.rowBytes());
- REPORTER_ASSERT(r, result == SkCodec::kSuccess);
- for (int i = 0; i < info.width(); ++i)
- for (int j = 0; j < info.height(); ++j) {
- SkColor c1 = *fromGenerator .getAddr32(i, j);
- SkColor c2 = *fromAndroidCodec.getAddr32(i, j);
- if (c1 != c2) {
- ERRORF(r, "Bitmaps for %s do not match starting at position %i, %i\n"
- "\tfromGenerator: %x\tfromAndroidCodec: %x", path.c_str(), i, j,
- c1, c2);
- return;
- }
- }
- }
- }
- DEF_TEST(AndroidCodec_sampledOrientation, r) {
- if (GetResourcePath().isEmpty()) {
- return;
- }
- // kRightTop_SkEncodedOrigin = 6, // Rotated 90 CW
- auto path = "images/orientation/6.jpg";
- auto data = GetResourceAsData(path);
- if (!data) {
- ERRORF(r, "Failed to get resource %s", path);
- return;
- }
- auto androidCodec = SkAndroidCodec::MakeFromCodec(SkCodec::MakeFromData(std::move(data)),
- SkAndroidCodec::ExifOrientationBehavior::kRespect);
- constexpr int sampleSize = 7;
- auto sampledDims = androidCodec->getSampledDimensions(sampleSize);
- SkAndroidCodec::AndroidOptions options;
- options.fSampleSize = sampleSize;
- SkBitmap bm;
- auto info = androidCodec->getInfo().makeWH(sampledDims.width(), sampledDims.height());
- bm.allocPixels(info);
- auto result = androidCodec->getAndroidPixels(info, bm.getPixels(), bm.rowBytes(), &options);
- if (result != SkCodec::kSuccess) {
- ERRORF(r, "got result \"%s\"\n", SkCodec::ResultToString(result));
- }
- }
|