Flutter Engine
The Flutter Engine
SkExif.cpp
Go to the documentation of this file.
1/*
2 * Copyright 2023 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
9
10#include "include/core/SkData.h"
13
14#include <algorithm>
15#include <cmath>
16#include <cstring>
17#include <memory>
18#include <utility>
19
20namespace SkExif {
21
22constexpr uint16_t kSubIFDOffsetTag = 0x8769;
23constexpr uint16_t kMarkerNoteTag = 0x927c;
24
25static std::optional<float> get_maker_note_hdr_headroom(sk_sp<SkData> data) {
26 // No little endian images that specify this data have been observed. Do not add speculative
27 // support.
28 const bool kLittleEndian = false;
29 const uint8_t kSig[] = {
30 'A', 'p', 'p', 'l', 'e', ' ', 'i', 'O', 'S', 0, 0, 1, 'M', 'M', //
31 };
32 if (!data || data->size() < sizeof(kSig)) {
33 return std::nullopt;
34 }
35 if (memcmp(data->data(), kSig, sizeof(kSig)) != 0) {
36 return std::nullopt;
37 }
39 std::move(data), kLittleEndian, sizeof(kSig));
40 if (!ifd) {
41 return std::nullopt;
42 }
43
44 // See documentation at:
45 // https://developer.apple.com/documentation/appkit/images_and_pdf/applying_apple_hdr_effect_to_your_photos
46 bool hasMaker33 = false;
47 bool hasMaker48 = false;
48 float maker33 = 0.f;
49 float maker48 = 0.f;
50 for (uint32_t i = 0; i < ifd->getNumEntries(); ++i) {
51 switch (ifd->getEntryTag(i)) {
52 case 33:
53 if (!hasMaker33) {
54 hasMaker33 = ifd->getEntrySignedRational(i, 1, &maker33);
55 }
56 break;
57 case 48:
58 if (!hasMaker48) {
59 hasMaker48 = ifd->getEntrySignedRational(i, 1, &maker48);
60 }
61 break;
62 default:
63 break;
64 }
65 }
66 // Many images have a maker33 but not a maker48. Treat them as having maker48 of 0.
67 if (!hasMaker33) {
68 return std::nullopt;
69 }
70 float stops = 0.f;
71 if (maker33 < 1.0f) {
72 if (maker48 <= 0.01f) {
73 stops = -20.0f * maker48 + 1.8f;
74 } else {
75 stops = -0.101f * maker48 + 1.601f;
76 }
77 } else {
78 if (maker48 <= 0.01f) {
79 stops = -70.0f * maker48 + 3.0f;
80 } else {
81 stops = -0.303f * maker48 + 2.303f;
82 }
83 }
84 return std::pow(2.f, std::max(stops, 0.f));
85}
86
87static void parse_ifd(Metadata& exif,
89 std::unique_ptr<SkTiff::ImageFileDirectory> ifd,
90 bool littleEndian,
91 bool isRoot) {
92 if (!ifd) {
93 return;
94 }
95 for (uint32_t i = 0; i < ifd->getNumEntries(); ++i) {
96 switch (ifd->getEntryTag(i)) {
97 case kOriginTag: {
98 uint16_t value = 0;
99 if (!exif.fOrigin.has_value() && ifd->getEntryUnsignedShort(i, 1, &value)) {
100 if (0 < value && value <= kLast_SkEncodedOrigin) {
101 exif.fOrigin = static_cast<SkEncodedOrigin>(value);
102 }
103 }
104 break;
105 }
106 case kMarkerNoteTag:
107 if (!exif.fHdrHeadroom.has_value()) {
108 if (auto makerNoteData = ifd->getEntryUndefinedData(i)) {
109 exif.fHdrHeadroom = get_maker_note_hdr_headroom(std::move(makerNoteData));
110 }
111 }
112 break;
113 case kSubIFDOffsetTag: {
114 uint32_t subIfdOffset = 0;
115 if (isRoot && ifd->getEntryUnsignedLong(i, 1, &subIfdOffset)) {
117 data, littleEndian, subIfdOffset, /*allowTruncated=*/true);
118 parse_ifd(exif,
119 data,
120 std::move(subIfd),
121 littleEndian,
122 /*isRoot=*/false);
123 }
124 break;
125 }
126 case kXResolutionTag: {
127 float value = 0.f;
128 if (!exif.fXResolution.has_value() && ifd->getEntryUnsignedRational(i, 1, &value)) {
129 exif.fXResolution = value;
130 }
131 break;
132 }
133 case kYResolutionTag: {
134 float value = 0.f;
135 if (!exif.fYResolution.has_value() && ifd->getEntryUnsignedRational(i, 1, &value)) {
136 exif.fYResolution = value;
137 }
138 break;
139 }
140 case kResolutionUnitTag: {
141 uint16_t value = 0;
142 if (!exif.fResolutionUnit.has_value() && ifd->getEntryUnsignedShort(i, 1, &value)) {
143 exif.fResolutionUnit = value;
144 }
145 break;
146 }
147 case kPixelXDimensionTag: {
148 // The type for this tag can be unsigned short or unsigned long (as per the Exif 2.3
149 // spec, aka CIPA DC-008-2012). Support for unsigned long was added in
150 // https://crrev.com/817600.
151 uint16_t value16 = 0;
152 if (!exif.fPixelXDimension.has_value() &&
153 ifd->getEntryUnsignedShort(i, 1, &value16)) {
154 exif.fPixelXDimension = value16;
155 }
156 uint32_t value32 = 0;
157 if (!exif.fPixelXDimension.has_value() &&
158 ifd->getEntryUnsignedLong(i, 1, &value32)) {
159 exif.fPixelXDimension = value32;
160 }
161 break;
162 }
163 case kPixelYDimensionTag: {
164 uint16_t value16 = 0;
165 if (!exif.fPixelYDimension.has_value() &&
166 ifd->getEntryUnsignedShort(i, 1, &value16)) {
167 exif.fPixelYDimension = value16;
168 }
169 uint32_t value32 = 0;
170 if (!exif.fPixelYDimension.has_value() &&
171 ifd->getEntryUnsignedLong(i, 1, &value32)) {
172 exif.fPixelYDimension = value32;
173 }
174 break;
175 }
176 default:
177 break;
178 }
179 }
180}
181
182void Parse(Metadata& metadata, const SkData* data) {
183 bool littleEndian = false;
184 uint32_t ifdOffset = 0;
185 if (data && SkTiff::ImageFileDirectory::ParseHeader(data, &littleEndian, &ifdOffset)) {
186 auto dataRef = SkData::MakeWithoutCopy(data->data(), data->size());
188 dataRef, littleEndian, ifdOffset, /*allowTruncated=*/true);
189 parse_ifd(metadata, std::move(dataRef), std::move(ifd), littleEndian, /*isRoot=*/true);
190 }
191}
192
193} // namespace SkExif
SkEncodedOrigin
@ kLast_SkEncodedOrigin
Definition: SkData.h:25
static sk_sp< SkData > MakeWithoutCopy(const void *data, size_t length)
Definition: SkData.h:116
static bool ParseHeader(const SkData *data, bool *outLittleEndian, uint32_t *outIfdOffset)
static std::unique_ptr< ImageFileDirectory > MakeFromOffset(sk_sp< SkData > data, bool littleEndian, uint32_t ifdOffset, bool allowTruncated=false)
uint8_t value
static float max(float r, float g, float b)
Definition: hsl.cpp:49
Definition: SkExif.h:19
constexpr uint16_t kMarkerNoteTag
Definition: SkExif.cpp:23
static void parse_ifd(Metadata &exif, sk_sp< SkData > data, std::unique_ptr< SkTiff::ImageFileDirectory > ifd, bool littleEndian, bool isRoot)
Definition: SkExif.cpp:87
constexpr uint16_t kSubIFDOffsetTag
Definition: SkExif.cpp:22
static constexpr uint16_t kResolutionUnitTag
Definition: SkExif.h:23
static constexpr uint16_t kPixelXDimensionTag
Definition: SkExif.h:26
static constexpr uint16_t kPixelYDimensionTag
Definition: SkExif.h:27
static constexpr uint16_t kOriginTag
Definition: SkExif.h:22
static std::optional< float > get_maker_note_hdr_headroom(sk_sp< SkData > data)
Definition: SkExif.cpp:25
static constexpr uint16_t kXResolutionTag
Definition: SkExif.h:24
void SK_API Parse(Metadata &metadata, const SkData *data)
Definition: SkExif.cpp:182
static constexpr uint16_t kYResolutionTag
Definition: SkExif.h:25
std::optional< uint32_t > fPixelYDimension
Definition: SkExif.h:44
std::optional< uint32_t > fPixelXDimension
Definition: SkExif.h:43
std::optional< float > fHdrHeadroom
Definition: SkExif.h:35
std::optional< SkEncodedOrigin > fOrigin
Definition: SkExif.h:31
std::optional< float > fYResolution
Definition: SkExif.h:40
std::optional< uint16_t > fResolutionUnit
Definition: SkExif.h:38
std::optional< float > fXResolution
Definition: SkExif.h:39
std::shared_ptr< const fml::Mapping > data
Definition: texture_gles.cc:63