1 /*
2 * Copyright 2016 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 "include/codec/SkCodec.h"
9 #include "include/codec/SkEncodedOrigin.h"
10 #include "include/codec/SkWebpDecoder.h"
11 #include "include/core/SkBitmap.h"
12 #include "include/core/SkImage.h"
13 #include "include/core/SkSize.h"
14 #include "include/core/SkStream.h"
15 #include "include/encode/SkJpegEncoder.h"
16 #include "include/private/SkExif.h"
17 #include "tests/Test.h"
18 #include "tools/Resources.h"
19
20 #include <memory>
21 #include <tuple>
22 #include <utility>
23
DEF_TEST(ExifOrientation,r)24 DEF_TEST(ExifOrientation, r) {
25 std::unique_ptr<SkStream> stream(GetResourceAsStream("images/exif-orientation-2-ur.jpg"));
26 REPORTER_ASSERT(r, nullptr != stream);
27 if (!stream) {
28 return;
29 }
30
31 std::unique_ptr<SkCodec> codec(SkCodec::MakeFromStream(std::move(stream)));
32 REPORTER_ASSERT(r, nullptr != codec);
33 SkEncodedOrigin origin = codec->getOrigin();
34 REPORTER_ASSERT(r, kTopRight_SkEncodedOrigin == origin);
35
36 codec = SkCodec::MakeFromStream(GetResourceAsStream("images/mandrill_512_q075.jpg"));
37 REPORTER_ASSERT(r, nullptr != codec);
38 origin = codec->getOrigin();
39 REPORTER_ASSERT(r, kTopLeft_SkEncodedOrigin == origin);
40 }
41
DEF_TEST(GetImageRespectsExif,r)42 DEF_TEST(GetImageRespectsExif, r) {
43 std::unique_ptr<SkStream> stream(GetResourceAsStream("images/orientation/6.webp"));
44 REPORTER_ASSERT(r, nullptr != stream);
45 if (!stream) {
46 return;
47 }
48
49 std::unique_ptr<SkCodec> codec(SkWebpDecoder::Decode(std::move(stream), nullptr));
50 REPORTER_ASSERT(r, nullptr != codec);
51 SkEncodedOrigin origin = codec->getOrigin();
52 REPORTER_ASSERT(r, kRightTop_SkEncodedOrigin == origin,
53 "Actual origin %d", origin);
54
55 auto result = codec->getImage();
56 REPORTER_ASSERT(r, std::get<1>(result) == SkCodec::Result::kSuccess,
57 "Not success %d", std::get<1>(result));
58 sk_sp<SkImage> frame = std::get<0>(result);
59 REPORTER_ASSERT(r, frame);
60 SkISize dims = frame->dimensions();
61 REPORTER_ASSERT(r, dims.fWidth == 100, "width %d != 100", dims.fWidth);
62 REPORTER_ASSERT(r, dims.fHeight == 80, "height %d != 80", dims.fHeight);
63 }
64
DEF_TEST(ExifOrientationInExif,r)65 DEF_TEST(ExifOrientationInExif, r) {
66 std::unique_ptr<SkStream> stream(GetResourceAsStream("images/orientation/exif.jpg"));
67
68 std::unique_ptr<SkCodec> codec = SkCodec::MakeFromStream(std::move(stream));
69 REPORTER_ASSERT(r, nullptr != codec);
70 SkEncodedOrigin origin = codec->getOrigin();
71 REPORTER_ASSERT(r, kLeftBottom_SkEncodedOrigin == origin);
72 }
73
DEF_TEST(ExifOrientationInSubIFD,r)74 DEF_TEST(ExifOrientationInSubIFD, r) {
75 std::unique_ptr<SkStream> stream(GetResourceAsStream("images/orientation/subifd.jpg"));
76
77 std::unique_ptr<SkCodec> codec = SkCodec::MakeFromStream(std::move(stream));
78 REPORTER_ASSERT(r, nullptr != codec);
79 SkEncodedOrigin origin = codec->getOrigin();
80 REPORTER_ASSERT(r, kLeftBottom_SkEncodedOrigin == origin);
81 }
82
approx_eq(float x,float y,float epsilon)83 static bool approx_eq(float x, float y, float epsilon) { return std::abs(x - y) < epsilon; }
84
DEF_TEST(ExifParse,r)85 DEF_TEST(ExifParse, r) {
86 const float kEpsilon = 0.001f;
87
88 {
89 sk_sp<SkData> data = GetResourceAsData("images/test0-hdr.exif");
90 REPORTER_ASSERT(r, nullptr != data);
91 SkExif::Metadata exif;
92 SkExif::Parse(exif, data.get());
93 REPORTER_ASSERT(r, exif.fHdrHeadroom.has_value());
94 REPORTER_ASSERT(r, approx_eq(exif.fHdrHeadroom.value(), 3.755296f, kEpsilon));
95
96 REPORTER_ASSERT(r, exif.fResolutionUnit.has_value());
97 REPORTER_ASSERT(r, 2 == exif.fResolutionUnit.value());
98 REPORTER_ASSERT(r, exif.fXResolution.has_value());
99 REPORTER_ASSERT(r, 72.f == exif.fXResolution.value());
100 REPORTER_ASSERT(r, exif.fYResolution.has_value());
101 REPORTER_ASSERT(r, 72.f == exif.fYResolution.value());
102
103 REPORTER_ASSERT(r, exif.fPixelXDimension.has_value());
104 REPORTER_ASSERT(r, 4032 == exif.fPixelXDimension.value());
105 REPORTER_ASSERT(r, exif.fPixelYDimension.has_value());
106 REPORTER_ASSERT(r, 3024 == exif.fPixelYDimension.value());
107 }
108
109 {
110 sk_sp<SkData> data = GetResourceAsData("images/test1-pixel32.exif");
111 REPORTER_ASSERT(r, nullptr != data);
112 SkExif::Metadata exif;
113 SkExif::Parse(exif, data.get());
114 REPORTER_ASSERT(r, !exif.fHdrHeadroom.has_value());
115
116 REPORTER_ASSERT(r, exif.fResolutionUnit.value());
117 REPORTER_ASSERT(r, 2 == exif.fResolutionUnit.value());
118 REPORTER_ASSERT(r, exif.fXResolution.has_value());
119 REPORTER_ASSERT(r, 72.f == exif.fXResolution.value());
120 REPORTER_ASSERT(r, exif.fYResolution.has_value());
121 REPORTER_ASSERT(r, 72.f == exif.fYResolution.value());
122
123 REPORTER_ASSERT(r, exif.fPixelXDimension.has_value());
124 REPORTER_ASSERT(r, 200 == exif.fPixelXDimension.value());
125 REPORTER_ASSERT(r, exif.fPixelYDimension.has_value());
126 REPORTER_ASSERT(r, 100 == exif.fPixelYDimension.value());
127 }
128
129 {
130 sk_sp<SkData> data = GetResourceAsData("images/test2-nonuniform.exif");
131 REPORTER_ASSERT(r, nullptr != data);
132 SkExif::Metadata exif;
133 SkExif::Parse(exif, data.get());
134 REPORTER_ASSERT(r, !exif.fHdrHeadroom.has_value());
135
136 REPORTER_ASSERT(r, exif.fResolutionUnit.value());
137 REPORTER_ASSERT(r, 2 == exif.fResolutionUnit.value());
138 REPORTER_ASSERT(r, exif.fXResolution.has_value());
139 REPORTER_ASSERT(r, 144.f == exif.fXResolution.value());
140 REPORTER_ASSERT(r, exif.fYResolution.has_value());
141 REPORTER_ASSERT(r, 36.f == exif.fYResolution.value());
142
143 REPORTER_ASSERT(r, exif.fPixelXDimension.has_value());
144 REPORTER_ASSERT(r, 50 == exif.fPixelXDimension.value());
145 REPORTER_ASSERT(r, exif.fPixelYDimension.has_value());
146 REPORTER_ASSERT(r, 100 == exif.fPixelYDimension.value());
147 }
148
149 {
150 sk_sp<SkData> data = GetResourceAsData("images/test3-little-endian.exif");
151 REPORTER_ASSERT(r, nullptr != data);
152 SkExif::Metadata exif;
153 SkExif::Parse(exif, data.get());
154 REPORTER_ASSERT(r, !exif.fHdrHeadroom.has_value());
155
156 REPORTER_ASSERT(r, exif.fResolutionUnit.value());
157 REPORTER_ASSERT(r, 2 == exif.fResolutionUnit.value());
158 REPORTER_ASSERT(r, exif.fXResolution.has_value());
159 REPORTER_ASSERT(r, 350.f == exif.fXResolution.value());
160 REPORTER_ASSERT(r, exif.fYResolution.has_value());
161 REPORTER_ASSERT(r, 350.f == exif.fYResolution.value());
162
163 REPORTER_ASSERT(r, !exif.fPixelXDimension.has_value());
164 REPORTER_ASSERT(r, !exif.fPixelYDimension.has_value());
165 }
166
167 {
168 sk_sp<SkData> data = GetResourceAsData("images/test0-hdr.exif");
169
170 // Zero out the denominators of signed and unsigned rationals, to verify that we do not
171 // divide by zero.
172 data = SkData::MakeWithCopy(data->bytes(), data->size());
173 memset(static_cast<uint8_t*>(data->writable_data()) + 186, 0, 4);
174 memset(static_cast<uint8_t*>(data->writable_data()) + 2171, 0, 4);
175 memset(static_cast<uint8_t*>(data->writable_data()) + 2240, 0, 4);
176
177 // Parse the corrupted Exif.
178 SkExif::Metadata exif;
179 SkExif::Parse(exif, data.get());
180
181 // HDR headroom signed denominators are destroyed.
182 REPORTER_ASSERT(r, exif.fHdrHeadroom.has_value());
183 REPORTER_ASSERT(r, approx_eq(exif.fHdrHeadroom.value(), 3.482202f, kEpsilon));
184
185 // The X resolution should be zero.
186 REPORTER_ASSERT(r, exif.fXResolution.has_value());
187 REPORTER_ASSERT(r, 0.f == exif.fXResolution.value());
188 REPORTER_ASSERT(r, exif.fYResolution.has_value());
189 REPORTER_ASSERT(r, 72.f == exif.fYResolution.value());
190 }
191 }
192
DEF_TEST(ExifTruncate,r)193 DEF_TEST(ExifTruncate, r) {
194 sk_sp<SkData> data = GetResourceAsData("images/test0-hdr.exif");
195
196 // At 545 bytes, we do not have either value yet.
197 {
198 SkExif::Metadata exif;
199 SkExif::Parse(exif, SkData::MakeWithCopy(data->bytes(), 545).get());
200 REPORTER_ASSERT(r, !exif.fPixelXDimension.has_value());
201 REPORTER_ASSERT(r, !exif.fPixelYDimension.has_value());
202 }
203
204 // At 546 bytes, we have one.
205 {
206 SkExif::Metadata exif;
207 SkExif::Parse(exif, SkData::MakeWithCopy(data->bytes(), 546).get());
208 REPORTER_ASSERT(r, exif.fPixelXDimension.has_value());
209 REPORTER_ASSERT(r, !exif.fPixelYDimension.has_value());
210 }
211
212 // At 558 bytes (12 bytes later, one tag), we have both.
213 {
214 SkExif::Metadata exif;
215 SkExif::Parse(exif, SkData::MakeWithCopy(data->bytes(), 558).get());
216 REPORTER_ASSERT(r, exif.fPixelXDimension.has_value());
217 REPORTER_ASSERT(r, exif.fPixelYDimension.has_value());
218 }
219 }
220
metadata_are_equal(const SkExif::Metadata & m1,const SkExif::Metadata & m2)221 bool metadata_are_equal(const SkExif::Metadata& m1, const SkExif::Metadata& m2) {
222 return m1.fOrigin == m2.fOrigin &&
223 m1.fHdrHeadroom == m2.fHdrHeadroom &&
224 m1.fResolutionUnit == m2.fResolutionUnit &&
225 m1.fXResolution == m2.fXResolution &&
226 m1.fYResolution == m2.fYResolution &&
227 m1.fPixelXDimension == m2.fPixelXDimension &&
228 m1.fPixelYDimension == m2.fPixelYDimension;
229 }
230
DEF_TEST(ExifWriteOrientation,r)231 DEF_TEST(ExifWriteOrientation, r) {
232 SkBitmap bm;
233 bm.allocPixels(SkImageInfo::MakeN32Premul(100, 100));
234 bm.eraseColor(SK_ColorBLUE);
235 SkPixmap pm;
236 if (!bm.peekPixels(&pm)) {
237 ERRORF(r, "failed to peek pixels");
238 return;
239 }
240 for (auto o : { kTopLeft_SkEncodedOrigin,
241 kTopRight_SkEncodedOrigin,
242 kBottomRight_SkEncodedOrigin,
243 kBottomLeft_SkEncodedOrigin,
244 kLeftTop_SkEncodedOrigin,
245 kRightTop_SkEncodedOrigin,
246 kRightBottom_SkEncodedOrigin,
247 kLeftBottom_SkEncodedOrigin }) {
248 SkDynamicMemoryWStream stream;
249 SkJpegEncoder::Options options;
250 options.fOrigin = o;
251 if (!SkJpegEncoder::Encode(&stream, pm, options)) {
252 ERRORF(r, "Failed to encode with orientation %i", o);
253 return;
254 }
255
256 auto data = stream.detachAsData();
257 auto codec = SkCodec::MakeFromData(std::move(data));
258 if (!codec) {
259 ERRORF(r, "Failed to create a codec with orientation %i", o);
260 return;
261 }
262 REPORTER_ASSERT(r, codec->getOrigin() == o);
263 }
264 }
265
DEF_TEST(ExifWriteTest,r)266 DEF_TEST(ExifWriteTest, r) {
267 {
268 // Parse exif data
269 sk_sp<SkData> data = GetResourceAsData("images/test2-nonuniform.exif");
270 REPORTER_ASSERT(r, nullptr != data);
271 SkExif::Metadata exif;
272 SkExif::Parse(exif, data.get());
273
274 // Write parsed data back to exif
275 sk_sp<SkData> writeData = SkExif::WriteExif(exif);
276 REPORTER_ASSERT(r, nullptr != writeData);
277
278 // Parse the new exif data that was written
279 SkExif::Metadata writeExif;
280 SkExif::Parse(writeExif, writeData.get());
281
282 REPORTER_ASSERT(r, metadata_are_equal(writeExif, exif));
283 }
284 }
285
286 // We are not able to write HDRheadroom data from a Metadata instance so WriteExif
287 // should fail and return nullptr when fHdrHeadroom is present.
DEF_TEST(ExifWriteFailsHDRheadroom,r)288 DEF_TEST(ExifWriteFailsHDRheadroom, r) {
289 sk_sp<SkData> data = GetResourceAsData("images/test0-hdr.exif");
290 REPORTER_ASSERT(r, nullptr != data);
291 SkExif::Metadata exif;
292 SkExif::Parse(exif, data.get());
293
294 // Write parsed data back to exif
295 sk_sp<SkData> writeData = SkExif::WriteExif(exif);
296 REPORTER_ASSERT(r, nullptr == writeData);
297 }
298
299