Flutter Engine
The Flutter Engine
Loading...
Searching...
No Matches
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
20constexpr uint16_t kSubIFDOffsetTag = 0x8769;
21constexpr uint16_t kOriginTag = 0x112;
22constexpr uint16_t kMarkerNoteTag = 0x927c;
23
24// Physical resolution.
25constexpr uint16_t kXResolutionTag = 0x011a;
26constexpr uint16_t kYResolutionTag = 0x011b;
27constexpr uint16_t kResolutionUnitTag = 0x0128;
28
29// Size in pixels. Also sometimes called ImageWidth and ImageHeight.
30constexpr uint16_t kPixelXDimensionTag = 0xa002;
31constexpr uint16_t kPixelYDimensionTag = 0xa003;
32
33static bool get_maker_note_hdr_headroom(sk_sp<SkData> data, float* hdrHeadroom) {
34 // No little endian images that specify this data have been observed. Do not add speculative
35 // support.
36 const bool kLittleEndian = false;
37 const uint8_t kSig[] = {
38 'A', 'p', 'p', 'l', 'e', ' ', 'i', 'O', 'S', 0, 0, 1, 'M', 'M', //
39 };
40 if (!data || data->size() < sizeof(kSig)) {
41 return false;
42 }
43 if (memcmp(data->data(), kSig, sizeof(kSig)) != 0) {
44 return false;
45 }
46 auto ifd =
47 SkTiffImageFileDirectory::MakeFromOffset(std::move(data), kLittleEndian, sizeof(kSig));
48 if (!ifd) {
49 return false;
50 }
51
52 // See documentation at:
53 // https://developer.apple.com/documentation/appkit/images_and_pdf/applying_apple_hdr_effect_to_your_photos
54 bool hasMaker33 = false;
55 bool hasMaker48 = false;
56 float maker33 = 0.f;
57 float maker48 = 0.f;
58 for (uint32_t i = 0; i < ifd->getNumEntries(); ++i) {
59 switch (ifd->getEntryTag(i)) {
60 case 33:
61 if (!hasMaker33) {
62 hasMaker33 = ifd->getEntrySignedRational(i, 1, &maker33);
63 }
64 break;
65 case 48:
66 if (!hasMaker48) {
67 hasMaker48 = ifd->getEntrySignedRational(i, 1, &maker48);
68 }
69 break;
70 default:
71 break;
72 }
73 }
74 if (!hasMaker33 || !hasMaker48) {
75 return false;
76 }
77 float stops = 0.f;
78 if (maker33 < 1.0f) {
79 if (maker48 <= 0.01f) {
80 stops = -20.0f * maker48 + 1.8f;
81 } else {
82 stops = -0.101f * maker48 + 1.601f;
83 }
84 } else {
85 if (maker48 <= 0.01f) {
86 stops = -70.0f * maker48 + 3.0f;
87 } else {
88 stops = -0.303f * maker48 + 2.303f;
89 }
90 }
91 *hdrHeadroom = std::pow(2.f, std::max(stops, 0.f));
92 return true;
93}
94
96 bool littleEndian = false;
97 uint32_t ifdOffset = 0;
98 if (!SkTiffImageFileDirectory::ParseHeader(fData.get(), &littleEndian, &ifdOffset)) {
99 SkCodecPrintf("Failed to parse Exif header.\n");
100 return;
101 }
102 parseIfd(ifdOffset, littleEndian, /*isRoot=*/true);
103}
104
105void SkExifMetadata::parseIfd(uint32_t ifdOffset, bool littleEndian, bool isRoot) {
106 auto ifd = SkTiffImageFileDirectory::MakeFromOffset(fData, littleEndian, ifdOffset);
107 if (!ifd) {
108 SkCodecPrintf("Failed to make IFD\n");
109 return;
110 }
111 for (uint32_t i = 0; i < ifd->getNumEntries(); ++i) {
112 switch (ifd->getEntryTag(i)) {
113 case kOriginTag: {
114 uint16_t value = 0;
115 if (!fOriginPresent && ifd->getEntryUnsignedShort(i, 1, &value)) {
116 if (0 < value && value <= kLast_SkEncodedOrigin) {
117 fOriginValue = static_cast<SkEncodedOrigin>(value);
118 fOriginPresent = true;
119 }
120 }
121 break;
122 }
123 case kMarkerNoteTag:
124 if (!fHdrHeadroomPresent) {
125 if (auto makerNoteData = ifd->getEntryUndefinedData(i)) {
126 fHdrHeadroomPresent = get_maker_note_hdr_headroom(std::move(makerNoteData),
127 &fHdrHeadroomValue);
128 }
129 }
130 break;
131 case kSubIFDOffsetTag: {
132 uint32_t subIfdOffset = 0;
133 if (isRoot && ifd->getEntryUnsignedLong(i, 1, &subIfdOffset)) {
134 parseIfd(subIfdOffset, littleEndian, /*isRoot=*/false);
135 }
136 break;
137 }
138 case kXResolutionTag:
139 if (!fXResolutionPresent) {
140 fXResolutionPresent = ifd->getEntryUnsignedRational(i, 1, &fXResolutionValue);
141 }
142 break;
143 case kYResolutionTag:
144 if (!fYResolutionPresent) {
145 fYResolutionPresent = ifd->getEntryUnsignedRational(i, 1, &fYResolutionValue);
146 }
147 break;
149 if (!fResolutionUnitPresent) {
150 fResolutionUnitPresent =
151 ifd->getEntryUnsignedShort(i, 1, &fResolutionUnitValue);
152 }
153 break;
155 // The type for this tag can be unsigned short or unsigned long (as per the Exif 2.3
156 // spec, aka CIPA DC-008-2012). Support for unsigned long was added in
157 // https://crrev.com/817600.
158 if (!fPixelXDimensionPresent) {
159 uint16_t value16 = 0;
160 if (ifd->getEntryUnsignedShort(i, 1, &value16)) {
161 fPixelXDimensionValue = value16;
162 fPixelXDimensionPresent = true;
163 }
164 }
165 if (!fPixelXDimensionPresent) {
166 fPixelXDimensionPresent =
167 ifd->getEntryUnsignedLong(i, 1, &fPixelXDimensionValue);
168 }
169 break;
171 if (!fPixelYDimensionPresent) {
172 uint16_t value16 = 0;
173 if (ifd->getEntryUnsignedShort(i, 1, &value16)) {
174 fPixelYDimensionValue = value16;
175 fPixelYDimensionPresent = true;
176 }
177 }
178 if (!fPixelYDimensionPresent) {
179 fPixelYDimensionPresent =
180 ifd->getEntryUnsignedLong(i, 1, &fPixelYDimensionValue);
181 }
182 break;
183 default:
184 break;
185 }
186 }
187}
#define SkCodecPrintf(...)
Definition SkCodecPriv.h:23
SkEncodedOrigin
@ kLast_SkEncodedOrigin
static bool get_maker_note_hdr_headroom(sk_sp< SkData > data, float *hdrHeadroom)
Definition SkExif.cpp:33
constexpr uint16_t kOriginTag
Definition SkExif.cpp:21
constexpr uint16_t kResolutionUnitTag
Definition SkExif.cpp:27
constexpr uint16_t kYResolutionTag
Definition SkExif.cpp:26
constexpr uint16_t kSubIFDOffsetTag
Definition SkExif.cpp:20
constexpr uint16_t kMarkerNoteTag
Definition SkExif.cpp:22
constexpr uint16_t kPixelXDimensionTag
Definition SkExif.cpp:30
constexpr uint16_t kPixelYDimensionTag
Definition SkExif.cpp:31
constexpr uint16_t kXResolutionTag
Definition SkExif.cpp:25
SkExifMetadata(const sk_sp< SkData > data)
Definition SkExif.cpp:95
static std::unique_ptr< SkTiffImageFileDirectory > MakeFromOffset(sk_sp< SkData > data, bool littleEndian, uint32_t ifdOffset)
static bool ParseHeader(const SkData *data, bool *outLittleEndian, uint32_t *outIfdOffset)
T * get() const
Definition SkRefCnt.h:303
uint8_t value
Definition ref_ptr.h:256