xref: /aosp_15_r20/external/skia/tests/ExifTest.cpp (revision c8dee2aa9b3f27cf6c858bd81872bdeb2c07ed17)
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