Flutter Engine
The Flutter Engine
Loading...
Searching...
No Matches
AndroidCodecTest.cpp
Go to the documentation of this file.
1/*
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
12#include "include/core/SkData.h"
15#include "include/core/SkSize.h"
18#include "include/private/SkGainmapInfo.h" // IWYU pragma: keep
19#include "modules/skcms/skcms.h"
20#include "tests/Test.h"
21#include "tools/Resources.h"
22
23#include <cstdint>
24#include <cstring>
25#include <initializer_list>
26#include <memory>
27#include <utility>
28
29static SkISize times(const SkISize& size, float factor) {
30 return { (int) (size.width() * factor), (int) (size.height() * factor) };
31}
32
33static SkISize plus(const SkISize& size, int term) {
34 return { size.width() + term, size.height() + term };
35}
36
37static bool invalid(const SkISize& size) {
38 return size.width() < 1 || size.height() < 1;
39}
40
41DEF_TEST(AndroidCodec_computeSampleSize, r) {
42 if (GetResourcePath().isEmpty()) {
43 return;
44 }
45 for (const char* file : { "images/color_wheel.webp",
46 "images/ship.png",
47 "images/dog.jpg",
48 "images/color_wheel.gif",
49 "images/rle.bmp",
50 "images/google_chrome.ico",
51 "images/mandrill.wbmp",
52#ifdef SK_CODEC_DECODES_RAW
53 "images/sample_1mp.dng",
54#endif
55 }) {
56 auto data = GetResourceAsData(file);
57 if (!data) {
58 ERRORF(r, "Could not get %s", file);
59 continue;
60 }
61
62 auto codec = SkAndroidCodec::MakeFromCodec(SkCodec::MakeFromData(std::move(data)));
63 if (!codec) {
64 ERRORF(r, "Could not create codec for %s", file);
65 continue;
66 }
67
68 const auto dims = codec->getInfo().dimensions();
69 const SkISize downscales[] = {
70 plus(dims, -1),
71 times(dims, .15f),
72 times(dims, .6f),
73 { (int32_t) (dims.width() * .25f), (int32_t) (dims.height() * .75f ) },
74 { 1, 1 },
75 { 1, 2 },
76 { 2, 1 },
77 { 0, -1 },
78 { dims.width(), dims.height() - 1 },
79 };
80 for (SkISize size : downscales) {
81 const auto requested = size;
82 const int computedSampleSize = codec->computeSampleSize(&size);
83 REPORTER_ASSERT(r, size.width() >= 1 && size.height() >= 1);
84 if (codec->getEncodedFormat() == SkEncodedImageFormat::kWEBP) {
85 // WebP supports arbitrary down-scaling.
86 REPORTER_ASSERT(r, size == requested || invalid(requested));
87 } else if (computedSampleSize == 1) {
88 REPORTER_ASSERT(r, size == dims);
89 } else {
90 REPORTER_ASSERT(r, computedSampleSize > 1);
91 if (size.width() >= dims.width() || size.height() >= dims.height()) {
92 ERRORF(r, "File %s's computed sample size (%i) is bigger than"
93 " original? original: %i x %i\tsampled: %i x %i",
94 file, computedSampleSize, dims.width(), dims.height(),
95 size.width(), size.height());
96 }
97 REPORTER_ASSERT(r, size.width() >= requested.width() &&
98 size.height() >= requested.height());
99 REPORTER_ASSERT(r, size.width() < dims.width() &&
100 size.height() < dims.height());
101 }
102 }
103
104 const SkISize upscales[] = {
105 dims, plus(dims, 5), times(dims, 2),
106 };
107 for (SkISize size : upscales) {
108 const int computedSampleSize = codec->computeSampleSize(&size);
109 REPORTER_ASSERT(r, computedSampleSize == 1);
110 REPORTER_ASSERT(r, dims == size);
111 }
112
113 // This mimics how Android's ImageDecoder uses SkAndroidCodec. A client
114 // can choose their dimensions based on calling getSampledDimensions,
115 // but the ImageDecoder API takes an arbitrary size. It then uses
116 // computeSampleSize to determine the best dimensions and sampleSize.
117 // It should return the same dimensions. the sampleSize may be different
118 // due to integer division.
119 for (int sampleSize : { 1, 2, 3, 4, 8, 16, 32 }) {
120 const SkISize sampledDims = codec->getSampledDimensions(sampleSize);
121 SkISize size = sampledDims;
122 const int computedSampleSize = codec->computeSampleSize(&size);
123 if (sampledDims != size) {
124 ERRORF(r, "File '%s'->getSampledDimensions(%i) yields computed"
125 " sample size of %i\n\tsampledDimensions: %i x %i\t"
126 "computed dimensions: %i x %i",
127 file, sampleSize, computedSampleSize,
128 sampledDims.width(), sampledDims.height(),
129 size.width(), size.height());
130 }
131 }
132 }
133}
134
135DEF_TEST(AndroidCodec_wide, r) {
136 if (GetResourcePath().isEmpty()) {
137 return;
138 }
139
140 const char* path = "images/wide-gamut.png";
141 auto data = GetResourceAsData(path);
142 if (!data) {
143 ERRORF(r, "Missing file %s", path);
144 return;
145 }
146
147 auto codec = SkAndroidCodec::MakeFromCodec(SkCodec::MakeFromData(std::move(data)));
148 if (!codec) {
149 ERRORF(r, "Failed to create codec from %s", path);
150 return;
151 }
152
153 auto info = codec->getInfo();
154 auto cs = codec->computeOutputColorSpace(info.colorType(), nullptr);
155 if (!cs) {
156 ERRORF(r, "%s should have a color space", path);
157 return;
158 }
159
160 // This image has a gamut that is VERY close to sRGB, so SkColorSpace::MakeRGB snaps to sRGB.
161 auto expected = SkColorSpace::MakeSRGB();
162 REPORTER_ASSERT(r, SkColorSpace::Equals(cs.get(), expected.get()));
163}
164
165DEF_TEST(AndroidCodec_P3, r) {
166 if (GetResourcePath().isEmpty()) {
167 return;
168 }
169
170 const char* path = "images/purple-displayprofile.png";
171 auto data = GetResourceAsData(path);
172 if (!data) {
173 ERRORF(r, "Missing file %s", path);
174 return;
175 }
176
177 auto codec = SkAndroidCodec::MakeFromCodec(SkCodec::MakeFromData(std::move(data)));
178 if (!codec) {
179 ERRORF(r, "Failed to create codec from %s", path);
180 return;
181 }
182
183 auto info = codec->getInfo();
184 auto cs = codec->computeOutputColorSpace(info.colorType(), nullptr);
185 if (!cs) {
186 ERRORF(r, "%s should have a color space", path);
187 return;
188 }
189
190 REPORTER_ASSERT(r, !cs->isSRGB());
191 REPORTER_ASSERT(r, cs->gammaCloseToSRGB());
192
193 skcms_Matrix3x3 matrix;
194 cs->toXYZD50(&matrix);
195
196 static constexpr skcms_Matrix3x3 kExpected = {{
197 { 0.426254272f, 0.369018555f, 0.168914795f },
198 { 0.226013184f, 0.685974121f, 0.0880126953f },
199 { 0.0116729736f, 0.0950927734f, 0.71812439f },
200 }};
201 REPORTER_ASSERT(r, 0 == memcmp(&matrix, &kExpected, sizeof(skcms_Matrix3x3)));
202}
203
204DEF_TEST(AndroidCodec_HLG, r) {
205 if (GetResourcePath().isEmpty()) {
206 return;
207 }
208
209 const char* path = "images/red-hlg-profile.png";
210 auto data = GetResourceAsData(path);
211 if (!data) {
212 ERRORF(r, "Missing file %s", path);
213 return;
214 }
215
216 auto codec = SkAndroidCodec::MakeFromCodec(SkCodec::MakeFromData(std::move(data)));
217 if (!codec) {
218 ERRORF(r, "Failed to create codec from %s", path);
219 return;
220 }
221
222 auto info = codec->getInfo();
223 auto cs = codec->computeOutputColorSpace(info.colorType(), nullptr);
224 if (!cs) {
225 ERRORF(r, "%s should have a color space", path);
226 return;
227 }
228
230 cs->transferFn(&tf);
232
233 skcms_Matrix3x3 matrix;
234 cs->toXYZD50(&matrix);
235
236 static constexpr skcms_Matrix3x3 kExpected = SkNamedGamut::kRec2020;
237 REPORTER_ASSERT(r, 0 == memcmp(&matrix, &kExpected, sizeof(skcms_Matrix3x3)));
238}
239
240DEF_TEST(AndroidCodec_PQ, r) {
241 if (GetResourcePath().isEmpty()) {
242 return;
243 }
244
245 const char* path = "images/red-pq-profile.png";
246 auto data = GetResourceAsData(path);
247 if (!data) {
248 ERRORF(r, "Missing file %s", path);
249 return;
250 }
251
252 auto codec = SkAndroidCodec::MakeFromCodec(SkCodec::MakeFromData(std::move(data)));
253 if (!codec) {
254 ERRORF(r, "Failed to create codec from %s", path);
255 return;
256 }
257
258 auto info = codec->getInfo();
259 auto cs = codec->computeOutputColorSpace(info.colorType(), nullptr);
260 if (!cs) {
261 ERRORF(r, "%s should have a color space", path);
262 return;
263 }
264
266 cs->transferFn(&tf);
268
269 skcms_Matrix3x3 matrix;
270 cs->toXYZD50(&matrix);
271
272 static constexpr skcms_Matrix3x3 kExpected = SkNamedGamut::kRec2020;
273 REPORTER_ASSERT(r, 0 == memcmp(&matrix, &kExpected, sizeof(skcms_Matrix3x3)));
274}
static SkISize plus(const SkISize &size, int term)
static bool invalid(const SkISize &size)
static SkISize times(const SkISize &size, float factor)
static void info(const char *fmt,...) SK_PRINTF_LIKE(1
Definition DM.cpp:213
sk_sp< SkData > GetResourceAsData(const char *resource)
Definition Resources.cpp:42
SkString GetResourcePath(const char *resource)
Definition Resources.cpp:23
#define DEF_TEST(name, reporter)
Definition Test.h:312
#define REPORTER_ASSERT(r, cond,...)
Definition Test.h:286
#define ERRORF(r,...)
Definition Test.h:293
Type::kYUV Type::kRGBA() int(0.7 *637)
static std::unique_ptr< SkAndroidCodec > MakeFromCodec(std::unique_ptr< SkCodec >)
static std::unique_ptr< SkCodec > MakeFromData(sk_sp< SkData >, SkSpan< const SkCodecs::Decoder > decoders, SkPngChunkReader *=nullptr)
Definition SkCodec.cpp:241
static bool Equals(const SkColorSpace *, const SkColorSpace *)
static sk_sp< SkColorSpace > MakeSRGB()
static constexpr skcms_Matrix3x3 kRec2020
bool skcms_TransferFunction_isHLGish(const skcms_TransferFunction *tf)
Definition skcms.cc:192
bool skcms_TransferFunction_isPQish(const skcms_TransferFunction *tf)
Definition skcms.cc:189
constexpr int32_t width() const
Definition SkSize.h:36
constexpr int32_t height() const
Definition SkSize.h:37