• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright 2024 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 #include "ultrahdr/gainmapmath.h"
18 #include "ultrahdr/gainmapmetadata.h"
19 
20 namespace ultrahdr {
21 
streamWriteU8(std::vector<uint8_t> & data,uint8_t value)22 status_t streamWriteU8(std::vector<uint8_t> &data, uint8_t value) {
23   data.push_back(value);
24   return JPEGR_NO_ERROR;
25 }
26 
streamWriteU32(std::vector<uint8_t> & data,uint32_t value)27 status_t streamWriteU32(std::vector<uint8_t> &data, uint32_t value) {
28   data.push_back((value >> 24) & 0xff);
29   data.push_back((value >> 16) & 0xff);
30   data.push_back((value >> 8) & 0xff);
31   data.push_back(value & 0xff);
32   return JPEGR_NO_ERROR;
33 }
34 
streamReadU8(const std::vector<uint8_t> & data,uint8_t & value,size_t & pos)35 status_t streamReadU8(const std::vector<uint8_t> &data, uint8_t &value, size_t &pos) {
36   if (pos >= data.size()) {
37     return ERROR_JPEGR_METADATA_ERROR;
38   }
39   value = data[pos++];
40   return JPEGR_NO_ERROR;
41 }
42 
streamReadU32(const std::vector<uint8_t> & data,uint32_t & value,size_t & pos)43 status_t streamReadU32(const std::vector<uint8_t> &data, uint32_t &value, size_t &pos) {
44   if (pos + 3 >= data.size()) {
45     return ERROR_JPEGR_METADATA_ERROR;
46   }
47   value = (data[pos] << 24 | data[pos + 1] << 16 | data[pos + 2] << 8 | data[pos + 3]);
48   pos += 4;
49   return JPEGR_NO_ERROR;
50 }
51 
encodeGainmapMetadata(const gain_map_metadata * metadata,std::vector<uint8_t> & out_data)52 status_t gain_map_metadata::encodeGainmapMetadata(const gain_map_metadata *metadata,
53                                                   std::vector<uint8_t> &out_data) {
54   if (metadata == nullptr) {
55     return ERROR_JPEGR_METADATA_ERROR;
56   }
57 
58   const uint8_t version = 0;
59   streamWriteU8(out_data, version);
60 
61   uint8_t flags = 0u;
62   // Always write three channels for now for simplicity.
63   // TODO(maryla): the draft says that this specifies the count of channels of the
64   // gain map. But tone mapping is done in RGB space so there are always three
65   // channels, even if the gain map is grayscale. Should this be revised?
66   const bool allChannelsIdentical =
67       metadata->gainMapMinN[0] == metadata->gainMapMinN[1] &&
68       metadata->gainMapMinN[0] == metadata->gainMapMinN[2] &&
69       metadata->gainMapMinD[0] == metadata->gainMapMinD[1] &&
70       metadata->gainMapMinD[0] == metadata->gainMapMinD[2] &&
71       metadata->gainMapMaxN[0] == metadata->gainMapMaxN[1] &&
72       metadata->gainMapMaxN[0] == metadata->gainMapMaxN[2] &&
73       metadata->gainMapMaxD[0] == metadata->gainMapMaxD[1] &&
74       metadata->gainMapMaxD[0] == metadata->gainMapMaxD[2] &&
75       metadata->gainMapGammaN[0] == metadata->gainMapGammaN[1] &&
76       metadata->gainMapGammaN[0] == metadata->gainMapGammaN[2] &&
77       metadata->gainMapGammaD[0] == metadata->gainMapGammaD[1] &&
78       metadata->gainMapGammaD[0] == metadata->gainMapGammaD[2] &&
79       metadata->baseOffsetN[0] == metadata->baseOffsetN[1] &&
80       metadata->baseOffsetN[0] == metadata->baseOffsetN[2] &&
81       metadata->baseOffsetD[0] == metadata->baseOffsetD[1] &&
82       metadata->baseOffsetD[0] == metadata->baseOffsetD[2] &&
83       metadata->alternateOffsetN[0] == metadata->alternateOffsetN[1] &&
84       metadata->alternateOffsetN[0] == metadata->alternateOffsetN[2] &&
85       metadata->alternateOffsetD[0] == metadata->alternateOffsetD[1] &&
86       metadata->alternateOffsetD[0] == metadata->alternateOffsetD[2];
87   const uint8_t channelCount = allChannelsIdentical ? 1u : 3u;
88 
89   if (channelCount == 3) {
90     flags |= 1;
91   }
92   if (metadata->useBaseColorSpace) {
93     flags |= 2;
94   }
95   if (metadata->backwardDirection) {
96     flags |= 4;
97   }
98 
99   const uint32_t denom = metadata->baseHdrHeadroomD;
100   bool useCommonDenominator = true;
101   if (metadata->baseHdrHeadroomD != denom || metadata->alternateHdrHeadroomD != denom) {
102     useCommonDenominator = false;
103   }
104   for (int c = 0; c < channelCount; ++c) {
105     if (metadata->gainMapMinD[c] != denom || metadata->gainMapMaxD[c] != denom ||
106         metadata->gainMapGammaD[c] != denom || metadata->baseOffsetD[c] != denom ||
107         metadata->alternateOffsetD[c] != denom) {
108       useCommonDenominator = false;
109     }
110   }
111   if (useCommonDenominator) {
112     flags |= 8;
113   }
114   streamWriteU8(out_data, flags);
115 
116   if (useCommonDenominator) {
117     streamWriteU32(out_data, denom);
118     streamWriteU32(out_data, metadata->baseHdrHeadroomN);
119     streamWriteU32(out_data, metadata->alternateHdrHeadroomN);
120     for (int c = 0; c < channelCount; ++c) {
121       streamWriteU32(out_data, (uint32_t)metadata->gainMapMinN[c]);
122       streamWriteU32(out_data, (uint32_t)metadata->gainMapMaxN[c]);
123       streamWriteU32(out_data, metadata->gainMapGammaN[c]);
124       streamWriteU32(out_data, (uint32_t)metadata->baseOffsetN[c]);
125       streamWriteU32(out_data, (uint32_t)metadata->alternateOffsetN[c]);
126     }
127   } else {
128     streamWriteU32(out_data, metadata->baseHdrHeadroomN);
129     streamWriteU32(out_data, metadata->baseHdrHeadroomD);
130     streamWriteU32(out_data, metadata->alternateHdrHeadroomN);
131     streamWriteU32(out_data, metadata->alternateHdrHeadroomD);
132     for (int c = 0; c < channelCount; ++c) {
133       streamWriteU32(out_data, (uint32_t)metadata->gainMapMinN[c]);
134       streamWriteU32(out_data, metadata->gainMapMinD[c]);
135       streamWriteU32(out_data, (uint32_t)metadata->gainMapMaxN[c]);
136       streamWriteU32(out_data, metadata->gainMapMaxD[c]);
137       streamWriteU32(out_data, metadata->gainMapGammaN[c]);
138       streamWriteU32(out_data, metadata->gainMapGammaD[c]);
139       streamWriteU32(out_data, (uint32_t)metadata->baseOffsetN[c]);
140       streamWriteU32(out_data, metadata->baseOffsetD[c]);
141       streamWriteU32(out_data, (uint32_t)metadata->alternateOffsetN[c]);
142       streamWriteU32(out_data, metadata->alternateOffsetD[c]);
143     }
144   }
145 
146   return JPEGR_NO_ERROR;
147 }
148 
decodeGainmapMetadata(const std::vector<uint8_t> & data,gain_map_metadata * out_metadata)149 status_t gain_map_metadata::decodeGainmapMetadata(const std::vector<uint8_t> &data,
150                                                   gain_map_metadata *out_metadata) {
151   if (out_metadata == nullptr) {
152     return ERROR_JPEGR_BAD_PTR;
153   }
154 
155   size_t pos = 0;
156   uint8_t version = 0xff;
157   JPEGR_CHECK(streamReadU8(data, version, pos))
158 
159   if (version != 0) {
160     return ERROR_JPEGR_UNSUPPORTED_FEATURE;
161   }
162 
163   uint8_t flags = 0xff;
164   JPEGR_CHECK(streamReadU8(data, flags, pos))
165 
166   uint8_t channelCount = (flags & 1) * 2 + 1;
167 
168   if (!(channelCount == 1 || channelCount == 3)) {
169     return ERROR_JPEGR_UNSUPPORTED_FEATURE;
170   }
171   out_metadata->useBaseColorSpace = (flags & 2) != 0;
172   out_metadata->backwardDirection = (flags & 4) != 0;
173   const bool useCommonDenominator = (flags & 8) != 0;
174 
175   if (useCommonDenominator) {
176     uint32_t commonDenominator;
177     JPEGR_CHECK(streamReadU32(data, commonDenominator, pos))
178 
179     JPEGR_CHECK(streamReadU32(data, out_metadata->baseHdrHeadroomN, pos))
180     out_metadata->baseHdrHeadroomD = commonDenominator;
181     JPEGR_CHECK(streamReadU32(data, out_metadata->alternateHdrHeadroomN, pos))
182     out_metadata->alternateHdrHeadroomD = commonDenominator;
183 
184     for (int c = 0; c < channelCount; ++c) {
185       JPEGR_CHECK(streamReadU32(data, out_metadata->gainMapMinN[c], pos))
186       out_metadata->gainMapMinD[c] = commonDenominator;
187       JPEGR_CHECK(streamReadU32(data, out_metadata->gainMapMaxN[c], pos))
188       out_metadata->gainMapMaxD[c] = commonDenominator;
189       JPEGR_CHECK(streamReadU32(data, out_metadata->gainMapGammaN[c], pos))
190       out_metadata->gainMapGammaD[c] = commonDenominator;
191       JPEGR_CHECK(streamReadU32(data, out_metadata->baseOffsetN[c], pos))
192       out_metadata->baseOffsetD[c] = commonDenominator;
193       JPEGR_CHECK(streamReadU32(data, out_metadata->alternateOffsetN[c], pos))
194       out_metadata->alternateOffsetD[c] = commonDenominator;
195     }
196   } else {
197     JPEGR_CHECK(streamReadU32(data, out_metadata->baseHdrHeadroomN, pos))
198     JPEGR_CHECK(streamReadU32(data, out_metadata->baseHdrHeadroomD, pos))
199     JPEGR_CHECK(streamReadU32(data, out_metadata->alternateHdrHeadroomN, pos))
200     JPEGR_CHECK(streamReadU32(data, out_metadata->alternateHdrHeadroomD, pos))
201     for (int c = 0; c < channelCount; ++c) {
202       JPEGR_CHECK(streamReadU32(data, out_metadata->gainMapMinN[c], pos))
203       JPEGR_CHECK(streamReadU32(data, out_metadata->gainMapMinD[c], pos))
204       JPEGR_CHECK(streamReadU32(data, out_metadata->gainMapMaxN[c], pos))
205       JPEGR_CHECK(streamReadU32(data, out_metadata->gainMapMaxD[c], pos))
206       JPEGR_CHECK(streamReadU32(data, out_metadata->gainMapGammaN[c], pos))
207       JPEGR_CHECK(streamReadU32(data, out_metadata->gainMapGammaD[c], pos))
208       JPEGR_CHECK(streamReadU32(data, out_metadata->baseOffsetN[c], pos))
209       JPEGR_CHECK(streamReadU32(data, out_metadata->baseOffsetD[c], pos))
210       JPEGR_CHECK(streamReadU32(data, out_metadata->alternateOffsetN[c], pos))
211       JPEGR_CHECK(streamReadU32(data, out_metadata->alternateOffsetD[c], pos))
212     }
213   }
214 
215   // Fill the remaining values by copying those from the first channel.
216   for (int c = channelCount; c < 3; ++c) {
217     out_metadata->gainMapMinN[c] = out_metadata->gainMapMinN[0];
218     out_metadata->gainMapMinD[c] = out_metadata->gainMapMinD[0];
219     out_metadata->gainMapMaxN[c] = out_metadata->gainMapMaxN[0];
220     out_metadata->gainMapMaxD[c] = out_metadata->gainMapMaxD[0];
221     out_metadata->gainMapGammaN[c] = out_metadata->gainMapGammaN[0];
222     out_metadata->gainMapGammaD[c] = out_metadata->gainMapGammaD[0];
223     out_metadata->baseOffsetN[c] = out_metadata->baseOffsetN[0];
224     out_metadata->baseOffsetD[c] = out_metadata->baseOffsetD[0];
225     out_metadata->alternateOffsetN[c] = out_metadata->alternateOffsetN[0];
226     out_metadata->alternateOffsetD[c] = out_metadata->alternateOffsetD[0];
227   }
228 
229   return JPEGR_NO_ERROR;
230 }
231 
232 #define CHECK_NOT_ZERO(x)                \
233   do {                                   \
234     if (x == 0) {                        \
235       return ERROR_JPEGR_METADATA_ERROR; \
236     }                                    \
237   } while (0)
238 
gainmapMetadataFractionToFloat(const gain_map_metadata * from,ultrahdr_metadata_ptr to)239 status_t gain_map_metadata::gainmapMetadataFractionToFloat(const gain_map_metadata *from,
240                                                            ultrahdr_metadata_ptr to) {
241   if (from == nullptr || to == nullptr) {
242     return ERROR_JPEGR_BAD_PTR;
243   }
244 
245   CHECK_NOT_ZERO(from->baseHdrHeadroomD);
246   CHECK_NOT_ZERO(from->alternateHdrHeadroomD);
247   for (int i = 0; i < 3; ++i) {
248     CHECK_NOT_ZERO(from->gainMapMaxD[i]);
249     CHECK_NOT_ZERO(from->gainMapGammaD[i]);
250     CHECK_NOT_ZERO(from->gainMapMinD[i]);
251     CHECK_NOT_ZERO(from->baseOffsetD[i]);
252     CHECK_NOT_ZERO(from->alternateOffsetD[i]);
253   }
254   to->version = kGainMapVersion;
255   to->maxContentBoost = (float)from->gainMapMaxN[0] / from->gainMapMaxD[0];
256   to->minContentBoost = (float)from->gainMapMinN[0] / from->gainMapMinD[0];
257   to->gamma = (float)from->gainMapGammaN[0] / from->gainMapGammaD[0];
258 
259   // BaseRenditionIsHDR is false
260   to->offsetSdr = (float)from->baseOffsetN[0] / from->baseOffsetD[0];
261   to->offsetHdr = (float)from->alternateOffsetN[0] / from->alternateOffsetD[0];
262   to->hdrCapacityMax = (float)from->alternateHdrHeadroomN / from->alternateHdrHeadroomD;
263   to->hdrCapacityMin = (float)from->baseHdrHeadroomN / from->baseHdrHeadroomD;
264 
265   return JPEGR_NO_ERROR;
266 }
267 
gainmapMetadataFloatToFraction(const ultrahdr_metadata_ptr from,gain_map_metadata * to)268 status_t gain_map_metadata::gainmapMetadataFloatToFraction(const ultrahdr_metadata_ptr from,
269                                                            gain_map_metadata *to) {
270   if (from == nullptr || to == nullptr) {
271     return ERROR_JPEGR_BAD_PTR;
272   }
273 
274   to->backwardDirection = false;
275   to->useBaseColorSpace = true;
276 
277   floatToUnsignedFraction(from->maxContentBoost, &to->gainMapMaxN[0], &to->gainMapMaxD[0]);
278   to->gainMapMaxN[2] = to->gainMapMaxN[1] = to->gainMapMaxN[0];
279   to->gainMapMaxD[2] = to->gainMapMaxD[1] = to->gainMapMaxD[0];
280 
281   floatToUnsignedFraction(from->minContentBoost, &to->gainMapMinN[0], &to->gainMapMinD[0]);
282   to->gainMapMinN[2] = to->gainMapMinN[1] = to->gainMapMinN[0];
283   to->gainMapMinD[2] = to->gainMapMinD[1] = to->gainMapMinD[0];
284 
285   floatToUnsignedFraction(from->gamma, &to->gainMapGammaN[0], &to->gainMapGammaD[0]);
286   to->gainMapGammaN[2] = to->gainMapGammaN[1] = to->gainMapGammaN[0];
287   to->gainMapGammaD[2] = to->gainMapGammaD[1] = to->gainMapGammaD[0];
288 
289   floatToUnsignedFraction(from->offsetSdr, &to->baseOffsetN[0], &to->baseOffsetD[0]);
290   to->baseOffsetN[2] = to->baseOffsetN[1] = to->baseOffsetN[0];
291   to->baseOffsetD[2] = to->baseOffsetD[1] = to->baseOffsetD[0];
292 
293   floatToUnsignedFraction(from->offsetHdr, &to->alternateOffsetN[0], &to->alternateOffsetD[0]);
294   to->alternateOffsetN[2] = to->alternateOffsetN[1] = to->alternateOffsetN[0];
295   to->alternateOffsetD[2] = to->alternateOffsetD[1] = to->alternateOffsetD[0];
296 
297   floatToUnsignedFraction(from->hdrCapacityMin, &to->baseHdrHeadroomN, &to->baseHdrHeadroomD);
298 
299   floatToUnsignedFraction(from->hdrCapacityMax, &to->alternateHdrHeadroomN,
300                           &to->alternateHdrHeadroomD);
301 
302   return JPEGR_NO_ERROR;
303 }
304 
305 }  // namespace ultrahdr
306