blob: d383713bb9477b9978aef3e8d057e245eb220244 [file] [log] [blame]
Leon Scroggins III07a722c2018-01-16 15:01:17 -05001/*
2 * Copyright 2018 Google Inc.
3 *
4 * Use of this source code is governed by a BSD-style license that can be
5 * found in the LICENSE file.
6 */
7
8#include "SkAndroidCodec.h"
9#include "SkCodec.h"
Leon Scroggins IIIda3e9ad2018-01-26 15:48:26 -050010#include "SkCodecImageGenerator.h"
Leon Scroggins III07a722c2018-01-16 15:01:17 -050011#include "SkEncodedImageFormat.h"
Leon Scroggins IIIda3e9ad2018-01-26 15:48:26 -050012#include "SkPixmapPriv.h"
Leon Scroggins III07a722c2018-01-16 15:01:17 -050013
14#include "Resources.h"
15#include "Test.h"
16
17static SkISize times(const SkISize& size, float factor) {
18 return { (int) (size.width() * factor), (int) (size.height() * factor) };
19}
20
21static SkISize plus(const SkISize& size, int term) {
22 return { size.width() + term, size.height() + term };
23}
24
25static bool invalid(const SkISize& size) {
26 return size.width() < 1 || size.height() < 1;
27}
28
29DEF_TEST(AndroidCodec_computeSampleSize, r) {
30 if (GetResourcePath().isEmpty()) {
31 return;
32 }
33 for (const char* file : { "images/color_wheel.webp",
34 "images/ship.png",
35 "images/dog.jpg",
36 "images/color_wheel.gif",
37 "images/rle.bmp",
38 "images/google_chrome.ico",
39 "images/mandrill.wbmp",
40#ifdef SK_CODEC_DECODES_RAW
41 "images/sample_1mp.dng",
42#endif
43 }) {
44 auto data = GetResourceAsData(file);
45 if (!data) {
46 ERRORF(r, "Could not get %s", file);
47 continue;
48 }
49
50 auto codec = SkAndroidCodec::MakeFromCodec(SkCodec::MakeFromData(std::move(data)));
51 if (!codec) {
52 ERRORF(r, "Could not create codec for %s", file);
53 continue;
54 }
55
56 const auto dims = codec->getInfo().dimensions();
57 const SkISize downscales[] = {
58 plus(dims, -1),
59 times(dims, .15f),
60 times(dims, .6f),
61 { (int32_t) (dims.width() * .25f), (int32_t) (dims.height() * .75f ) },
62 { 1, 1 },
63 { 1, 2 },
64 { 2, 1 },
65 { 0, -1 },
66 { dims.width(), dims.height() - 1 },
67 };
68 for (SkISize size : downscales) {
69 const auto requested = size;
70 const int computedSampleSize = codec->computeSampleSize(&size);
71 REPORTER_ASSERT(r, size.width() >= 1 && size.height() >= 1);
72 if (codec->getEncodedFormat() == SkEncodedImageFormat::kWEBP) {
73 // WebP supports arbitrary down-scaling.
74 REPORTER_ASSERT(r, size == requested || invalid(requested));
75 } else if (computedSampleSize == 1) {
76 REPORTER_ASSERT(r, size == dims);
77 } else {
78 REPORTER_ASSERT(r, computedSampleSize > 1);
79 if (size.width() >= dims.width() || size.height() >= dims.height()) {
80 ERRORF(r, "File %s's computed sample size (%i) is bigger than"
81 " original? original: %i x %i\tsampled: %i x %i",
82 file, computedSampleSize, dims.width(), dims.height(),
83 size.width(), size.height());
84 }
85 REPORTER_ASSERT(r, size.width() >= requested.width() &&
86 size.height() >= requested.height());
87 REPORTER_ASSERT(r, size.width() < dims.width() &&
88 size.height() < dims.height());
89 }
90 }
91
92 const SkISize upscales[] = {
93 dims, plus(dims, 5), times(dims, 2),
94 };
95 for (SkISize size : upscales) {
96 const int computedSampleSize = codec->computeSampleSize(&size);
97 REPORTER_ASSERT(r, computedSampleSize == 1);
98 REPORTER_ASSERT(r, dims == size);
99 }
100
101 // This mimics how Android's ImageDecoder uses SkAndroidCodec. A client
102 // can choose their dimensions based on calling getSampledDimensions,
103 // but the ImageDecoder API takes an arbitrary size. It then uses
104 // computeSampleSize to determine the best dimensions and sampleSize.
105 // It should return the same dimensions. the sampleSize may be different
106 // due to integer division.
107 for (int sampleSize : { 1, 2, 3, 4, 8, 16, 32 }) {
108 const SkISize sampledDims = codec->getSampledDimensions(sampleSize);
109 SkISize size = sampledDims;
110 const int computedSampleSize = codec->computeSampleSize(&size);
111 if (sampledDims != size) {
112 ERRORF(r, "File '%s'->getSampledDimensions(%i) yields computed"
113 " sample size of %i\n\tsampledDimensions: %i x %i\t"
114 "computed dimensions: %i x %i",
115 file, sampleSize, computedSampleSize,
116 sampledDims.width(), sampledDims.height(),
117 size.width(), size.height());
118 }
119 }
120 }
121}
Leon Scroggins IIIda3e9ad2018-01-26 15:48:26 -0500122
123DEF_TEST(AndroidCodec_orientation, r) {
124 if (GetResourcePath().isEmpty()) {
125 return;
126 }
127
128 for (const char* ext : { "jpg", "webp" })
129 for (char i = '1'; i <= '8'; ++i) {
130 SkString path = SkStringPrintf("images/orientation/%c.%s", i, ext);
131 auto data = GetResourceAsData(path.c_str());
132 auto gen = SkCodecImageGenerator::MakeFromEncodedCodec(data);
133 if (!gen) {
134 ERRORF(r, "failed to decode %s", path.c_str());
135 return;
136 }
137
138 // Dimensions after adjusting for the origin.
139 const SkISize expectedDims = { 100, 80 };
140
141 // SkCodecImageGenerator automatically adjusts for the origin.
142 REPORTER_ASSERT(r, gen->getInfo().dimensions() == expectedDims);
143
144 auto androidCodec = SkAndroidCodec::MakeFromCodec(SkCodec::MakeFromData(data));
145 if (!androidCodec) {
146 ERRORF(r, "failed to decode %s", path.c_str());
147 return;
148 }
149
150 // SkAndroidCodec does not adjust for the origin by default. Dimensions may be reversed.
151 if (SkPixmapPriv::ShouldSwapWidthHeight(androidCodec->codec()->getOrigin())) {
152 auto swappedDims = SkPixmapPriv::SwapWidthHeight(androidCodec->getInfo()).dimensions();
153 REPORTER_ASSERT(r, expectedDims == swappedDims);
154 } else {
155 REPORTER_ASSERT(r, expectedDims == androidCodec->getInfo().dimensions());
156 }
157
158 // Passing kRespect adjusts for the origin.
159 androidCodec = SkAndroidCodec::MakeFromCodec(SkCodec::MakeFromData(std::move(data)),
160 SkAndroidCodec::ExifOrientationBehavior::kRespect);
161 auto info = androidCodec->getInfo();
162 REPORTER_ASSERT(r, info.dimensions() == expectedDims);
163
164 SkBitmap fromGenerator;
165 fromGenerator.allocPixels(info);
166 REPORTER_ASSERT(r, gen->getPixels(info, fromGenerator.getPixels(),
167 fromGenerator.rowBytes()));
168
169 SkBitmap fromAndroidCodec;
170 fromAndroidCodec.allocPixels(info);
171 auto result = androidCodec->getPixels(info, fromAndroidCodec.getPixels(),
172 fromAndroidCodec.rowBytes());
173 REPORTER_ASSERT(r, result == SkCodec::kSuccess);
174
175 for (int i = 0; i < info.width(); ++i)
176 for (int j = 0; j < info.height(); ++j) {
177 SkColor c1 = *fromGenerator .getAddr32(i, j);
178 SkColor c2 = *fromAndroidCodec.getAddr32(i, j);
179 if (c1 != c2) {
180 ERRORF(r, "Bitmaps for %s do not match starting at position %i, %i\n"
181 "\tfromGenerator: %x\tfromAndroidCodec: %x", path.c_str(), i, j,
182 c1, c2);
183 return;
184 }
185 }
186 }
187}