1*c8dee2aaSAndroid Build Coastguard Worker /*
2*c8dee2aaSAndroid Build Coastguard Worker * Copyright 2024 Google LLC
3*c8dee2aaSAndroid Build Coastguard Worker *
4*c8dee2aaSAndroid Build Coastguard Worker * Use of this source code is governed by a BSD-style license that can be
5*c8dee2aaSAndroid Build Coastguard Worker * found in the LICENSE file.
6*c8dee2aaSAndroid Build Coastguard Worker */
7*c8dee2aaSAndroid Build Coastguard Worker
8*c8dee2aaSAndroid Build Coastguard Worker #include "include/private/SkGainmapInfo.h"
9*c8dee2aaSAndroid Build Coastguard Worker
10*c8dee2aaSAndroid Build Coastguard Worker #include "include/core/SkColor.h"
11*c8dee2aaSAndroid Build Coastguard Worker #include "include/core/SkData.h"
12*c8dee2aaSAndroid Build Coastguard Worker #include "include/core/SkRefCnt.h"
13*c8dee2aaSAndroid Build Coastguard Worker #include "include/core/SkStream.h"
14*c8dee2aaSAndroid Build Coastguard Worker #include "src/base/SkEndian.h"
15*c8dee2aaSAndroid Build Coastguard Worker #include "src/codec/SkCodecPriv.h"
16*c8dee2aaSAndroid Build Coastguard Worker #include "src/core/SkStreamPriv.h"
17*c8dee2aaSAndroid Build Coastguard Worker
18*c8dee2aaSAndroid Build Coastguard Worker #include <cmath>
19*c8dee2aaSAndroid Build Coastguard Worker #include <cstdint>
20*c8dee2aaSAndroid Build Coastguard Worker #include <memory>
21*c8dee2aaSAndroid Build Coastguard Worker
22*c8dee2aaSAndroid Build Coastguard Worker namespace {
23*c8dee2aaSAndroid Build Coastguard Worker constexpr uint8_t kIsMultiChannelMask = (1u << 7);
24*c8dee2aaSAndroid Build Coastguard Worker constexpr uint8_t kUseBaseColourSpaceMask = (1u << 6);
25*c8dee2aaSAndroid Build Coastguard Worker } // namespace
26*c8dee2aaSAndroid Build Coastguard Worker
write_rational_be(SkDynamicMemoryWStream & s,float x)27*c8dee2aaSAndroid Build Coastguard Worker static void write_rational_be(SkDynamicMemoryWStream& s, float x) {
28*c8dee2aaSAndroid Build Coastguard Worker // TODO(b/338342146): Select denominator to get maximum precision and robustness.
29*c8dee2aaSAndroid Build Coastguard Worker uint32_t denominator = 0x10000000;
30*c8dee2aaSAndroid Build Coastguard Worker if (std::abs(x) > 1.f) {
31*c8dee2aaSAndroid Build Coastguard Worker denominator = 0x1000;
32*c8dee2aaSAndroid Build Coastguard Worker }
33*c8dee2aaSAndroid Build Coastguard Worker int32_t numerator = static_cast<int32_t>(std::llround(static_cast<double>(x) * denominator));
34*c8dee2aaSAndroid Build Coastguard Worker SkWStreamWriteS32BE(&s, numerator);
35*c8dee2aaSAndroid Build Coastguard Worker SkWStreamWriteU32BE(&s, denominator);
36*c8dee2aaSAndroid Build Coastguard Worker }
37*c8dee2aaSAndroid Build Coastguard Worker
write_positive_rational_be(SkDynamicMemoryWStream & s,float x)38*c8dee2aaSAndroid Build Coastguard Worker static void write_positive_rational_be(SkDynamicMemoryWStream& s, float x) {
39*c8dee2aaSAndroid Build Coastguard Worker // TODO(b/338342146): Select denominator to get maximum precision and robustness.
40*c8dee2aaSAndroid Build Coastguard Worker uint32_t denominator = 0x10000000;
41*c8dee2aaSAndroid Build Coastguard Worker if (x > 1.f) {
42*c8dee2aaSAndroid Build Coastguard Worker denominator = 0x1000;
43*c8dee2aaSAndroid Build Coastguard Worker }
44*c8dee2aaSAndroid Build Coastguard Worker uint32_t numerator = static_cast<uint32_t>(std::llround(static_cast<double>(x) * denominator));
45*c8dee2aaSAndroid Build Coastguard Worker SkWStreamWriteU32BE(&s, numerator);
46*c8dee2aaSAndroid Build Coastguard Worker SkWStreamWriteU32BE(&s, denominator);
47*c8dee2aaSAndroid Build Coastguard Worker }
48*c8dee2aaSAndroid Build Coastguard Worker
read_u16_be(SkStream * s,uint16_t * value)49*c8dee2aaSAndroid Build Coastguard Worker static bool read_u16_be(SkStream* s, uint16_t* value) {
50*c8dee2aaSAndroid Build Coastguard Worker if (!s->readU16(value)) {
51*c8dee2aaSAndroid Build Coastguard Worker return false;
52*c8dee2aaSAndroid Build Coastguard Worker }
53*c8dee2aaSAndroid Build Coastguard Worker *value = SkEndian_SwapBE16(*value);
54*c8dee2aaSAndroid Build Coastguard Worker return true;
55*c8dee2aaSAndroid Build Coastguard Worker }
56*c8dee2aaSAndroid Build Coastguard Worker
read_u32_be(SkStream * s,uint32_t * value)57*c8dee2aaSAndroid Build Coastguard Worker static bool read_u32_be(SkStream* s, uint32_t* value) {
58*c8dee2aaSAndroid Build Coastguard Worker if (!s->readU32(value)) {
59*c8dee2aaSAndroid Build Coastguard Worker return false;
60*c8dee2aaSAndroid Build Coastguard Worker }
61*c8dee2aaSAndroid Build Coastguard Worker *value = SkEndian_SwapBE32(*value);
62*c8dee2aaSAndroid Build Coastguard Worker return true;
63*c8dee2aaSAndroid Build Coastguard Worker }
64*c8dee2aaSAndroid Build Coastguard Worker
read_s32_be(SkStream * s,int32_t * value)65*c8dee2aaSAndroid Build Coastguard Worker static bool read_s32_be(SkStream* s, int32_t* value) {
66*c8dee2aaSAndroid Build Coastguard Worker if (!s->readS32(value)) {
67*c8dee2aaSAndroid Build Coastguard Worker return false;
68*c8dee2aaSAndroid Build Coastguard Worker }
69*c8dee2aaSAndroid Build Coastguard Worker *value = SkEndian_SwapBE32(*value);
70*c8dee2aaSAndroid Build Coastguard Worker return true;
71*c8dee2aaSAndroid Build Coastguard Worker }
72*c8dee2aaSAndroid Build Coastguard Worker
read_rational_be(SkStream * s,float * value)73*c8dee2aaSAndroid Build Coastguard Worker static bool read_rational_be(SkStream* s, float* value) {
74*c8dee2aaSAndroid Build Coastguard Worker int32_t numerator = 0;
75*c8dee2aaSAndroid Build Coastguard Worker uint32_t denominator = 0;
76*c8dee2aaSAndroid Build Coastguard Worker if (!read_s32_be(s, &numerator)) {
77*c8dee2aaSAndroid Build Coastguard Worker return false;
78*c8dee2aaSAndroid Build Coastguard Worker }
79*c8dee2aaSAndroid Build Coastguard Worker if (!read_u32_be(s, &denominator)) {
80*c8dee2aaSAndroid Build Coastguard Worker return false;
81*c8dee2aaSAndroid Build Coastguard Worker }
82*c8dee2aaSAndroid Build Coastguard Worker *value = static_cast<float>(static_cast<double>(numerator) / static_cast<double>(denominator));
83*c8dee2aaSAndroid Build Coastguard Worker return true;
84*c8dee2aaSAndroid Build Coastguard Worker }
85*c8dee2aaSAndroid Build Coastguard Worker
read_positive_rational_be(SkStream * s,float * value)86*c8dee2aaSAndroid Build Coastguard Worker static bool read_positive_rational_be(SkStream* s, float* value) {
87*c8dee2aaSAndroid Build Coastguard Worker uint32_t numerator = 0;
88*c8dee2aaSAndroid Build Coastguard Worker uint32_t denominator = 0;
89*c8dee2aaSAndroid Build Coastguard Worker if (!read_u32_be(s, &numerator)) {
90*c8dee2aaSAndroid Build Coastguard Worker return false;
91*c8dee2aaSAndroid Build Coastguard Worker }
92*c8dee2aaSAndroid Build Coastguard Worker if (!read_u32_be(s, &denominator)) {
93*c8dee2aaSAndroid Build Coastguard Worker return false;
94*c8dee2aaSAndroid Build Coastguard Worker }
95*c8dee2aaSAndroid Build Coastguard Worker *value = static_cast<float>(static_cast<double>(numerator) / static_cast<double>(denominator));
96*c8dee2aaSAndroid Build Coastguard Worker return true;
97*c8dee2aaSAndroid Build Coastguard Worker }
98*c8dee2aaSAndroid Build Coastguard Worker
read_iso_gainmap_version(SkStream * s)99*c8dee2aaSAndroid Build Coastguard Worker static bool read_iso_gainmap_version(SkStream* s) {
100*c8dee2aaSAndroid Build Coastguard Worker // Ensure minimum version is 0.
101*c8dee2aaSAndroid Build Coastguard Worker uint16_t minimum_version = 0;
102*c8dee2aaSAndroid Build Coastguard Worker if (!read_u16_be(s, &minimum_version)) {
103*c8dee2aaSAndroid Build Coastguard Worker SkCodecPrintf("Failed to read ISO 21496-1 minimum version.\n");
104*c8dee2aaSAndroid Build Coastguard Worker return false;
105*c8dee2aaSAndroid Build Coastguard Worker }
106*c8dee2aaSAndroid Build Coastguard Worker if (minimum_version != 0) {
107*c8dee2aaSAndroid Build Coastguard Worker SkCodecPrintf("Unsupported ISO 21496-1 minimum version.\n");
108*c8dee2aaSAndroid Build Coastguard Worker return false;
109*c8dee2aaSAndroid Build Coastguard Worker }
110*c8dee2aaSAndroid Build Coastguard Worker
111*c8dee2aaSAndroid Build Coastguard Worker // Ensure writer version is present. No value is invalid.
112*c8dee2aaSAndroid Build Coastguard Worker uint16_t writer_version = 0;
113*c8dee2aaSAndroid Build Coastguard Worker if (!read_u16_be(s, &writer_version)) {
114*c8dee2aaSAndroid Build Coastguard Worker SkCodecPrintf("Failed to read ISO 21496-1 version.\n");
115*c8dee2aaSAndroid Build Coastguard Worker return false;
116*c8dee2aaSAndroid Build Coastguard Worker }
117*c8dee2aaSAndroid Build Coastguard Worker
118*c8dee2aaSAndroid Build Coastguard Worker return true;
119*c8dee2aaSAndroid Build Coastguard Worker }
120*c8dee2aaSAndroid Build Coastguard Worker
read_iso_gainmap_info(SkStream * s,SkGainmapInfo & info)121*c8dee2aaSAndroid Build Coastguard Worker static bool read_iso_gainmap_info(SkStream* s, SkGainmapInfo& info) {
122*c8dee2aaSAndroid Build Coastguard Worker if (!read_iso_gainmap_version(s)) {
123*c8dee2aaSAndroid Build Coastguard Worker SkCodecPrintf("Failed to read ISO 21496-1 version.\n");
124*c8dee2aaSAndroid Build Coastguard Worker return false;
125*c8dee2aaSAndroid Build Coastguard Worker }
126*c8dee2aaSAndroid Build Coastguard Worker
127*c8dee2aaSAndroid Build Coastguard Worker uint8_t flags = 0;
128*c8dee2aaSAndroid Build Coastguard Worker if (!s->readU8(&flags)) {
129*c8dee2aaSAndroid Build Coastguard Worker SkCodecPrintf("Failed to read ISO 21496-1 flags.\n");
130*c8dee2aaSAndroid Build Coastguard Worker return false;
131*c8dee2aaSAndroid Build Coastguard Worker }
132*c8dee2aaSAndroid Build Coastguard Worker bool isMultiChannel = (flags & kIsMultiChannelMask) != 0;
133*c8dee2aaSAndroid Build Coastguard Worker bool useBaseColourSpace = (flags & kUseBaseColourSpaceMask) != 0;
134*c8dee2aaSAndroid Build Coastguard Worker
135*c8dee2aaSAndroid Build Coastguard Worker float baseHdrHeadroom = 0.f;
136*c8dee2aaSAndroid Build Coastguard Worker if (!read_positive_rational_be(s, &baseHdrHeadroom)) {
137*c8dee2aaSAndroid Build Coastguard Worker SkCodecPrintf("Failed to read ISO 21496-1 base HDR headroom.\n");
138*c8dee2aaSAndroid Build Coastguard Worker return false;
139*c8dee2aaSAndroid Build Coastguard Worker }
140*c8dee2aaSAndroid Build Coastguard Worker float altrHdrHeadroom = 0.f;
141*c8dee2aaSAndroid Build Coastguard Worker if (!read_positive_rational_be(s, &altrHdrHeadroom)) {
142*c8dee2aaSAndroid Build Coastguard Worker SkCodecPrintf("Failed to read ISO 21496-1 altr HDR headroom.\n");
143*c8dee2aaSAndroid Build Coastguard Worker return false;
144*c8dee2aaSAndroid Build Coastguard Worker }
145*c8dee2aaSAndroid Build Coastguard Worker
146*c8dee2aaSAndroid Build Coastguard Worker float gainMapMin[3] = {0.f};
147*c8dee2aaSAndroid Build Coastguard Worker float gainMapMax[3] = {0.f};
148*c8dee2aaSAndroid Build Coastguard Worker float gamma[3] = {0.f};
149*c8dee2aaSAndroid Build Coastguard Worker float baseOffset[3] = {0.f};
150*c8dee2aaSAndroid Build Coastguard Worker float altrOffset[3] = {0.f};
151*c8dee2aaSAndroid Build Coastguard Worker
152*c8dee2aaSAndroid Build Coastguard Worker int channelCount = isMultiChannel ? 3 : 1;
153*c8dee2aaSAndroid Build Coastguard Worker for (int i = 0; i < channelCount; ++i) {
154*c8dee2aaSAndroid Build Coastguard Worker if (!read_rational_be(s, gainMapMin + i)) {
155*c8dee2aaSAndroid Build Coastguard Worker SkCodecPrintf("Failed to read ISO 21496-1 gainmap minimum.\n");
156*c8dee2aaSAndroid Build Coastguard Worker return false;
157*c8dee2aaSAndroid Build Coastguard Worker }
158*c8dee2aaSAndroid Build Coastguard Worker if (!read_rational_be(s, gainMapMax + i)) {
159*c8dee2aaSAndroid Build Coastguard Worker SkCodecPrintf("Failed to read ISO 21496-1 gainmap maximum.\n");
160*c8dee2aaSAndroid Build Coastguard Worker return false;
161*c8dee2aaSAndroid Build Coastguard Worker }
162*c8dee2aaSAndroid Build Coastguard Worker if (!read_positive_rational_be(s, gamma + i)) {
163*c8dee2aaSAndroid Build Coastguard Worker SkCodecPrintf("Failed to read ISO 21496-1 gamma.\n");
164*c8dee2aaSAndroid Build Coastguard Worker return false;
165*c8dee2aaSAndroid Build Coastguard Worker }
166*c8dee2aaSAndroid Build Coastguard Worker if (!read_rational_be(s, baseOffset + i)) {
167*c8dee2aaSAndroid Build Coastguard Worker SkCodecPrintf("Failed to read ISO 21496-1 base offset.\n");
168*c8dee2aaSAndroid Build Coastguard Worker return false;
169*c8dee2aaSAndroid Build Coastguard Worker }
170*c8dee2aaSAndroid Build Coastguard Worker if (!read_rational_be(s, altrOffset + i)) {
171*c8dee2aaSAndroid Build Coastguard Worker SkCodecPrintf("Failed to read ISO 21496-1 altr offset.\n");
172*c8dee2aaSAndroid Build Coastguard Worker return false;
173*c8dee2aaSAndroid Build Coastguard Worker }
174*c8dee2aaSAndroid Build Coastguard Worker }
175*c8dee2aaSAndroid Build Coastguard Worker
176*c8dee2aaSAndroid Build Coastguard Worker info = SkGainmapInfo();
177*c8dee2aaSAndroid Build Coastguard Worker if (!useBaseColourSpace) {
178*c8dee2aaSAndroid Build Coastguard Worker info.fGainmapMathColorSpace = SkColorSpace::MakeSRGB();
179*c8dee2aaSAndroid Build Coastguard Worker }
180*c8dee2aaSAndroid Build Coastguard Worker if (baseHdrHeadroom < altrHdrHeadroom) {
181*c8dee2aaSAndroid Build Coastguard Worker info.fBaseImageType = SkGainmapInfo::BaseImageType::kSDR;
182*c8dee2aaSAndroid Build Coastguard Worker info.fDisplayRatioSdr = std::exp2(baseHdrHeadroom);
183*c8dee2aaSAndroid Build Coastguard Worker info.fDisplayRatioHdr = std::exp2(altrHdrHeadroom);
184*c8dee2aaSAndroid Build Coastguard Worker } else {
185*c8dee2aaSAndroid Build Coastguard Worker info.fBaseImageType = SkGainmapInfo::BaseImageType::kHDR;
186*c8dee2aaSAndroid Build Coastguard Worker info.fDisplayRatioHdr = std::exp2(baseHdrHeadroom);
187*c8dee2aaSAndroid Build Coastguard Worker info.fDisplayRatioSdr = std::exp2(altrHdrHeadroom);
188*c8dee2aaSAndroid Build Coastguard Worker }
189*c8dee2aaSAndroid Build Coastguard Worker for (int i = 0; i < 3; ++i) {
190*c8dee2aaSAndroid Build Coastguard Worker int j = i >= channelCount ? 0 : i;
191*c8dee2aaSAndroid Build Coastguard Worker info.fGainmapRatioMin[i] = std::exp2(gainMapMin[j]);
192*c8dee2aaSAndroid Build Coastguard Worker info.fGainmapRatioMax[i] = std::exp2(gainMapMax[j]);
193*c8dee2aaSAndroid Build Coastguard Worker info.fGainmapGamma[i] = 1.f / gamma[j];
194*c8dee2aaSAndroid Build Coastguard Worker switch (info.fBaseImageType) {
195*c8dee2aaSAndroid Build Coastguard Worker case SkGainmapInfo::BaseImageType::kSDR:
196*c8dee2aaSAndroid Build Coastguard Worker info.fEpsilonSdr[i] = baseOffset[j];
197*c8dee2aaSAndroid Build Coastguard Worker info.fEpsilonHdr[i] = altrOffset[j];
198*c8dee2aaSAndroid Build Coastguard Worker break;
199*c8dee2aaSAndroid Build Coastguard Worker case SkGainmapInfo::BaseImageType::kHDR:
200*c8dee2aaSAndroid Build Coastguard Worker info.fEpsilonHdr[i] = baseOffset[j];
201*c8dee2aaSAndroid Build Coastguard Worker info.fEpsilonSdr[i] = altrOffset[j];
202*c8dee2aaSAndroid Build Coastguard Worker break;
203*c8dee2aaSAndroid Build Coastguard Worker }
204*c8dee2aaSAndroid Build Coastguard Worker }
205*c8dee2aaSAndroid Build Coastguard Worker return true;
206*c8dee2aaSAndroid Build Coastguard Worker }
207*c8dee2aaSAndroid Build Coastguard Worker
isUltraHDRv1Compatible() const208*c8dee2aaSAndroid Build Coastguard Worker bool SkGainmapInfo::isUltraHDRv1Compatible() const {
209*c8dee2aaSAndroid Build Coastguard Worker // UltraHDR v1 supports having the base image be HDR in theory, but it is largely
210*c8dee2aaSAndroid Build Coastguard Worker // untested.
211*c8dee2aaSAndroid Build Coastguard Worker if (fBaseImageType == BaseImageType::kHDR) {
212*c8dee2aaSAndroid Build Coastguard Worker return false;
213*c8dee2aaSAndroid Build Coastguard Worker }
214*c8dee2aaSAndroid Build Coastguard Worker // UltraHDR v1 doesn't support a non-base gainmap math color space.
215*c8dee2aaSAndroid Build Coastguard Worker if (fGainmapMathColorSpace) {
216*c8dee2aaSAndroid Build Coastguard Worker return false;
217*c8dee2aaSAndroid Build Coastguard Worker }
218*c8dee2aaSAndroid Build Coastguard Worker return true;
219*c8dee2aaSAndroid Build Coastguard Worker }
220*c8dee2aaSAndroid Build Coastguard Worker
ParseVersion(const SkData * data)221*c8dee2aaSAndroid Build Coastguard Worker bool SkGainmapInfo::ParseVersion(const SkData* data) {
222*c8dee2aaSAndroid Build Coastguard Worker if (!data) {
223*c8dee2aaSAndroid Build Coastguard Worker return false;
224*c8dee2aaSAndroid Build Coastguard Worker }
225*c8dee2aaSAndroid Build Coastguard Worker auto s = SkMemoryStream::MakeDirect(data->data(), data->size());
226*c8dee2aaSAndroid Build Coastguard Worker return read_iso_gainmap_version(s.get());
227*c8dee2aaSAndroid Build Coastguard Worker }
228*c8dee2aaSAndroid Build Coastguard Worker
Parse(const SkData * data,SkGainmapInfo & info)229*c8dee2aaSAndroid Build Coastguard Worker bool SkGainmapInfo::Parse(const SkData* data, SkGainmapInfo& info) {
230*c8dee2aaSAndroid Build Coastguard Worker if (!data) {
231*c8dee2aaSAndroid Build Coastguard Worker return false;
232*c8dee2aaSAndroid Build Coastguard Worker }
233*c8dee2aaSAndroid Build Coastguard Worker auto s = SkMemoryStream::MakeDirect(data->data(), data->size());
234*c8dee2aaSAndroid Build Coastguard Worker return read_iso_gainmap_info(s.get(), info);
235*c8dee2aaSAndroid Build Coastguard Worker }
236*c8dee2aaSAndroid Build Coastguard Worker
SerializeVersion()237*c8dee2aaSAndroid Build Coastguard Worker sk_sp<SkData> SkGainmapInfo::SerializeVersion() {
238*c8dee2aaSAndroid Build Coastguard Worker SkDynamicMemoryWStream s;
239*c8dee2aaSAndroid Build Coastguard Worker SkWStreamWriteU16BE(&s, 0); // Minimum reader version
240*c8dee2aaSAndroid Build Coastguard Worker SkWStreamWriteU16BE(&s, 0); // Writer version
241*c8dee2aaSAndroid Build Coastguard Worker return s.detachAsData();
242*c8dee2aaSAndroid Build Coastguard Worker }
243*c8dee2aaSAndroid Build Coastguard Worker
is_single_channel(SkColor4f c)244*c8dee2aaSAndroid Build Coastguard Worker static bool is_single_channel(SkColor4f c) { return c.fR == c.fG && c.fG == c.fB; };
245*c8dee2aaSAndroid Build Coastguard Worker
serialize() const246*c8dee2aaSAndroid Build Coastguard Worker sk_sp<SkData> SkGainmapInfo::serialize() const {
247*c8dee2aaSAndroid Build Coastguard Worker SkDynamicMemoryWStream s;
248*c8dee2aaSAndroid Build Coastguard Worker // Version.
249*c8dee2aaSAndroid Build Coastguard Worker SkWStreamWriteU16BE(&s, 0); // Minimum reader version
250*c8dee2aaSAndroid Build Coastguard Worker SkWStreamWriteU16BE(&s, 0); // Writer version
251*c8dee2aaSAndroid Build Coastguard Worker
252*c8dee2aaSAndroid Build Coastguard Worker // Flags.
253*c8dee2aaSAndroid Build Coastguard Worker bool all_single_channel = is_single_channel(fGainmapRatioMin) &&
254*c8dee2aaSAndroid Build Coastguard Worker is_single_channel(fGainmapRatioMax) &&
255*c8dee2aaSAndroid Build Coastguard Worker is_single_channel(fGainmapGamma) && is_single_channel(fEpsilonSdr) &&
256*c8dee2aaSAndroid Build Coastguard Worker is_single_channel(fEpsilonHdr);
257*c8dee2aaSAndroid Build Coastguard Worker uint8_t flags = 0;
258*c8dee2aaSAndroid Build Coastguard Worker if (!fGainmapMathColorSpace) {
259*c8dee2aaSAndroid Build Coastguard Worker flags |= kUseBaseColourSpaceMask;
260*c8dee2aaSAndroid Build Coastguard Worker }
261*c8dee2aaSAndroid Build Coastguard Worker if (!all_single_channel) {
262*c8dee2aaSAndroid Build Coastguard Worker flags |= kIsMultiChannelMask;
263*c8dee2aaSAndroid Build Coastguard Worker }
264*c8dee2aaSAndroid Build Coastguard Worker s.write8(flags);
265*c8dee2aaSAndroid Build Coastguard Worker
266*c8dee2aaSAndroid Build Coastguard Worker // Base and altr headroom.
267*c8dee2aaSAndroid Build Coastguard Worker switch (fBaseImageType) {
268*c8dee2aaSAndroid Build Coastguard Worker case SkGainmapInfo::BaseImageType::kSDR:
269*c8dee2aaSAndroid Build Coastguard Worker write_positive_rational_be(s, std::log2(fDisplayRatioSdr));
270*c8dee2aaSAndroid Build Coastguard Worker write_positive_rational_be(s, std::log2(fDisplayRatioHdr));
271*c8dee2aaSAndroid Build Coastguard Worker break;
272*c8dee2aaSAndroid Build Coastguard Worker case SkGainmapInfo::BaseImageType::kHDR:
273*c8dee2aaSAndroid Build Coastguard Worker write_positive_rational_be(s, std::log2(fDisplayRatioHdr));
274*c8dee2aaSAndroid Build Coastguard Worker write_positive_rational_be(s, std::log2(fDisplayRatioSdr));
275*c8dee2aaSAndroid Build Coastguard Worker break;
276*c8dee2aaSAndroid Build Coastguard Worker }
277*c8dee2aaSAndroid Build Coastguard Worker
278*c8dee2aaSAndroid Build Coastguard Worker // Per-channel information.
279*c8dee2aaSAndroid Build Coastguard Worker for (int i = 0; i < (all_single_channel ? 1 : 3); ++i) {
280*c8dee2aaSAndroid Build Coastguard Worker write_rational_be(s, std::log2(fGainmapRatioMin[i]));
281*c8dee2aaSAndroid Build Coastguard Worker write_rational_be(s, std::log2(fGainmapRatioMax[i]));
282*c8dee2aaSAndroid Build Coastguard Worker write_positive_rational_be(s, 1.f / fGainmapGamma[i]);
283*c8dee2aaSAndroid Build Coastguard Worker switch (fBaseImageType) {
284*c8dee2aaSAndroid Build Coastguard Worker case SkGainmapInfo::BaseImageType::kSDR:
285*c8dee2aaSAndroid Build Coastguard Worker write_rational_be(s, fEpsilonSdr[i]);
286*c8dee2aaSAndroid Build Coastguard Worker write_rational_be(s, fEpsilonHdr[i]);
287*c8dee2aaSAndroid Build Coastguard Worker break;
288*c8dee2aaSAndroid Build Coastguard Worker case SkGainmapInfo::BaseImageType::kHDR:
289*c8dee2aaSAndroid Build Coastguard Worker write_rational_be(s, fEpsilonHdr[i]);
290*c8dee2aaSAndroid Build Coastguard Worker write_rational_be(s, fEpsilonSdr[i]);
291*c8dee2aaSAndroid Build Coastguard Worker break;
292*c8dee2aaSAndroid Build Coastguard Worker }
293*c8dee2aaSAndroid Build Coastguard Worker }
294*c8dee2aaSAndroid Build Coastguard Worker return s.detachAsData();
295*c8dee2aaSAndroid Build Coastguard Worker }
296