xref: /aosp_15_r20/external/libultrahdr/lib/src/jpegr.cpp (revision 89a0ef05262152531a00a15832a2d3b1e3990773)
1*89a0ef05SAndroid Build Coastguard Worker /*
2*89a0ef05SAndroid Build Coastguard Worker  * Copyright 2022 The Android Open Source Project
3*89a0ef05SAndroid Build Coastguard Worker  *
4*89a0ef05SAndroid Build Coastguard Worker  * Licensed under the Apache License, Version 2.0 (the "License");
5*89a0ef05SAndroid Build Coastguard Worker  * you may not use this file except in compliance with the License.
6*89a0ef05SAndroid Build Coastguard Worker  * You may obtain a copy of the License at
7*89a0ef05SAndroid Build Coastguard Worker  *
8*89a0ef05SAndroid Build Coastguard Worker  *      http://www.apache.org/licenses/LICENSE-2.0
9*89a0ef05SAndroid Build Coastguard Worker  *
10*89a0ef05SAndroid Build Coastguard Worker  * Unless required by applicable law or agreed to in writing, software
11*89a0ef05SAndroid Build Coastguard Worker  * distributed under the License is distributed on an "AS IS" BASIS,
12*89a0ef05SAndroid Build Coastguard Worker  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13*89a0ef05SAndroid Build Coastguard Worker  * See the License for the specific language governing permissions and
14*89a0ef05SAndroid Build Coastguard Worker  * limitations under the License.
15*89a0ef05SAndroid Build Coastguard Worker  */
16*89a0ef05SAndroid Build Coastguard Worker 
17*89a0ef05SAndroid Build Coastguard Worker #ifdef _WIN32
18*89a0ef05SAndroid Build Coastguard Worker #include <windows.h>
19*89a0ef05SAndroid Build Coastguard Worker #include <sysinfoapi.h>
20*89a0ef05SAndroid Build Coastguard Worker #else
21*89a0ef05SAndroid Build Coastguard Worker #include <unistd.h>
22*89a0ef05SAndroid Build Coastguard Worker #endif
23*89a0ef05SAndroid Build Coastguard Worker 
24*89a0ef05SAndroid Build Coastguard Worker #include <condition_variable>
25*89a0ef05SAndroid Build Coastguard Worker #include <deque>
26*89a0ef05SAndroid Build Coastguard Worker #include <functional>
27*89a0ef05SAndroid Build Coastguard Worker #include <mutex>
28*89a0ef05SAndroid Build Coastguard Worker #include <thread>
29*89a0ef05SAndroid Build Coastguard Worker 
30*89a0ef05SAndroid Build Coastguard Worker #include "ultrahdr/editorhelper.h"
31*89a0ef05SAndroid Build Coastguard Worker #include "ultrahdr/gainmapmetadata.h"
32*89a0ef05SAndroid Build Coastguard Worker #include "ultrahdr/ultrahdrcommon.h"
33*89a0ef05SAndroid Build Coastguard Worker #include "ultrahdr/jpegr.h"
34*89a0ef05SAndroid Build Coastguard Worker #include "ultrahdr/icc.h"
35*89a0ef05SAndroid Build Coastguard Worker #include "ultrahdr/multipictureformat.h"
36*89a0ef05SAndroid Build Coastguard Worker 
37*89a0ef05SAndroid Build Coastguard Worker #include "image_io/base/data_segment_data_source.h"
38*89a0ef05SAndroid Build Coastguard Worker #include "image_io/jpeg/jpeg_info.h"
39*89a0ef05SAndroid Build Coastguard Worker #include "image_io/jpeg/jpeg_info_builder.h"
40*89a0ef05SAndroid Build Coastguard Worker #include "image_io/jpeg/jpeg_marker.h"
41*89a0ef05SAndroid Build Coastguard Worker #include "image_io/jpeg/jpeg_scanner.h"
42*89a0ef05SAndroid Build Coastguard Worker 
43*89a0ef05SAndroid Build Coastguard Worker using namespace std;
44*89a0ef05SAndroid Build Coastguard Worker using namespace photos_editing_formats::image_io;
45*89a0ef05SAndroid Build Coastguard Worker 
46*89a0ef05SAndroid Build Coastguard Worker namespace ultrahdr {
47*89a0ef05SAndroid Build Coastguard Worker 
48*89a0ef05SAndroid Build Coastguard Worker #ifdef UHDR_ENABLE_GLES
49*89a0ef05SAndroid Build Coastguard Worker uhdr_error_info_t applyGainMapGLES(uhdr_raw_image_t* sdr_intent, uhdr_raw_image_t* gainmap_img,
50*89a0ef05SAndroid Build Coastguard Worker                                    uhdr_gainmap_metadata_ext_t* gainmap_metadata,
51*89a0ef05SAndroid Build Coastguard Worker                                    uhdr_color_transfer_t output_ct, float display_boost,
52*89a0ef05SAndroid Build Coastguard Worker                                    uhdr_raw_image_t* dest, uhdr_opengl_ctxt_t* opengl_ctxt);
53*89a0ef05SAndroid Build Coastguard Worker #endif
54*89a0ef05SAndroid Build Coastguard Worker 
55*89a0ef05SAndroid Build Coastguard Worker // Gain map metadata
56*89a0ef05SAndroid Build Coastguard Worker static const bool kWriteXmpMetadata = true;
57*89a0ef05SAndroid Build Coastguard Worker static const bool kWriteIso21496_1Metadata = false;
58*89a0ef05SAndroid Build Coastguard Worker 
59*89a0ef05SAndroid Build Coastguard Worker static const string kXmpNameSpace = "http://ns.adobe.com/xap/1.0/";
60*89a0ef05SAndroid Build Coastguard Worker static const string kIsoNameSpace = "urn:iso:std:iso:ts:21496:-1";
61*89a0ef05SAndroid Build Coastguard Worker 
62*89a0ef05SAndroid Build Coastguard Worker static_assert(kWriteXmpMetadata || kWriteIso21496_1Metadata,
63*89a0ef05SAndroid Build Coastguard Worker               "Must write gain map metadata in XMP format, or iso 21496-1 format, or both.");
64*89a0ef05SAndroid Build Coastguard Worker 
65*89a0ef05SAndroid Build Coastguard Worker class JobQueue {
66*89a0ef05SAndroid Build Coastguard Worker  public:
67*89a0ef05SAndroid Build Coastguard Worker   bool dequeueJob(unsigned int& rowStart, unsigned int& rowEnd);
68*89a0ef05SAndroid Build Coastguard Worker   void enqueueJob(unsigned int rowStart, unsigned int rowEnd);
69*89a0ef05SAndroid Build Coastguard Worker   void markQueueForEnd();
70*89a0ef05SAndroid Build Coastguard Worker   void reset();
71*89a0ef05SAndroid Build Coastguard Worker 
72*89a0ef05SAndroid Build Coastguard Worker  private:
73*89a0ef05SAndroid Build Coastguard Worker   bool mQueuedAllJobs = false;
74*89a0ef05SAndroid Build Coastguard Worker   std::deque<std::tuple<unsigned int, unsigned int>> mJobs;
75*89a0ef05SAndroid Build Coastguard Worker   std::mutex mMutex;
76*89a0ef05SAndroid Build Coastguard Worker   std::condition_variable mCv;
77*89a0ef05SAndroid Build Coastguard Worker };
78*89a0ef05SAndroid Build Coastguard Worker 
dequeueJob(unsigned int & rowStart,unsigned int & rowEnd)79*89a0ef05SAndroid Build Coastguard Worker bool JobQueue::dequeueJob(unsigned int& rowStart, unsigned int& rowEnd) {
80*89a0ef05SAndroid Build Coastguard Worker   std::unique_lock<std::mutex> lock{mMutex};
81*89a0ef05SAndroid Build Coastguard Worker   while (true) {
82*89a0ef05SAndroid Build Coastguard Worker     if (mJobs.empty()) {
83*89a0ef05SAndroid Build Coastguard Worker       if (mQueuedAllJobs) {
84*89a0ef05SAndroid Build Coastguard Worker         return false;
85*89a0ef05SAndroid Build Coastguard Worker       } else {
86*89a0ef05SAndroid Build Coastguard Worker         mCv.wait_for(lock, std::chrono::milliseconds(100));
87*89a0ef05SAndroid Build Coastguard Worker       }
88*89a0ef05SAndroid Build Coastguard Worker     } else {
89*89a0ef05SAndroid Build Coastguard Worker       auto it = mJobs.begin();
90*89a0ef05SAndroid Build Coastguard Worker       rowStart = std::get<0>(*it);
91*89a0ef05SAndroid Build Coastguard Worker       rowEnd = std::get<1>(*it);
92*89a0ef05SAndroid Build Coastguard Worker       mJobs.erase(it);
93*89a0ef05SAndroid Build Coastguard Worker       return true;
94*89a0ef05SAndroid Build Coastguard Worker     }
95*89a0ef05SAndroid Build Coastguard Worker   }
96*89a0ef05SAndroid Build Coastguard Worker   return false;
97*89a0ef05SAndroid Build Coastguard Worker }
98*89a0ef05SAndroid Build Coastguard Worker 
enqueueJob(unsigned int rowStart,unsigned int rowEnd)99*89a0ef05SAndroid Build Coastguard Worker void JobQueue::enqueueJob(unsigned int rowStart, unsigned int rowEnd) {
100*89a0ef05SAndroid Build Coastguard Worker   std::unique_lock<std::mutex> lock{mMutex};
101*89a0ef05SAndroid Build Coastguard Worker   mJobs.push_back(std::make_tuple(rowStart, rowEnd));
102*89a0ef05SAndroid Build Coastguard Worker   lock.unlock();
103*89a0ef05SAndroid Build Coastguard Worker   mCv.notify_one();
104*89a0ef05SAndroid Build Coastguard Worker }
105*89a0ef05SAndroid Build Coastguard Worker 
markQueueForEnd()106*89a0ef05SAndroid Build Coastguard Worker void JobQueue::markQueueForEnd() {
107*89a0ef05SAndroid Build Coastguard Worker   std::unique_lock<std::mutex> lock{mMutex};
108*89a0ef05SAndroid Build Coastguard Worker   mQueuedAllJobs = true;
109*89a0ef05SAndroid Build Coastguard Worker   lock.unlock();
110*89a0ef05SAndroid Build Coastguard Worker   mCv.notify_all();
111*89a0ef05SAndroid Build Coastguard Worker }
112*89a0ef05SAndroid Build Coastguard Worker 
reset()113*89a0ef05SAndroid Build Coastguard Worker void JobQueue::reset() {
114*89a0ef05SAndroid Build Coastguard Worker   std::unique_lock<std::mutex> lock{mMutex};
115*89a0ef05SAndroid Build Coastguard Worker   mJobs.clear();
116*89a0ef05SAndroid Build Coastguard Worker   mQueuedAllJobs = false;
117*89a0ef05SAndroid Build Coastguard Worker }
118*89a0ef05SAndroid Build Coastguard Worker 
119*89a0ef05SAndroid Build Coastguard Worker /*
120*89a0ef05SAndroid Build Coastguard Worker  * MessageWriter implementation for ALOG functions.
121*89a0ef05SAndroid Build Coastguard Worker  */
122*89a0ef05SAndroid Build Coastguard Worker class AlogMessageWriter : public MessageWriter {
123*89a0ef05SAndroid Build Coastguard Worker  public:
WriteMessage(const Message & message)124*89a0ef05SAndroid Build Coastguard Worker   void WriteMessage(const Message& message) override {
125*89a0ef05SAndroid Build Coastguard Worker     std::string log = GetFormattedMessage(message);
126*89a0ef05SAndroid Build Coastguard Worker     ALOGD("%s", log.c_str());
127*89a0ef05SAndroid Build Coastguard Worker   }
128*89a0ef05SAndroid Build Coastguard Worker };
129*89a0ef05SAndroid Build Coastguard Worker 
GetCPUCoreCount()130*89a0ef05SAndroid Build Coastguard Worker unsigned int GetCPUCoreCount() { return (std::max)(1u, std::thread::hardware_concurrency()); }
131*89a0ef05SAndroid Build Coastguard Worker 
JpegR(void * uhdrGLESCtxt,int mapDimensionScaleFactor,int mapCompressQuality,bool useMultiChannelGainMap,float gamma,uhdr_enc_preset_t preset,float minContentBoost,float maxContentBoost,float targetDispPeakBrightness)132*89a0ef05SAndroid Build Coastguard Worker JpegR::JpegR(void* uhdrGLESCtxt, int mapDimensionScaleFactor, int mapCompressQuality,
133*89a0ef05SAndroid Build Coastguard Worker              bool useMultiChannelGainMap, float gamma, uhdr_enc_preset_t preset,
134*89a0ef05SAndroid Build Coastguard Worker              float minContentBoost, float maxContentBoost, float targetDispPeakBrightness) {
135*89a0ef05SAndroid Build Coastguard Worker   mUhdrGLESCtxt = uhdrGLESCtxt;
136*89a0ef05SAndroid Build Coastguard Worker   mMapDimensionScaleFactor = mapDimensionScaleFactor;
137*89a0ef05SAndroid Build Coastguard Worker   mMapCompressQuality = mapCompressQuality;
138*89a0ef05SAndroid Build Coastguard Worker   mUseMultiChannelGainMap = useMultiChannelGainMap;
139*89a0ef05SAndroid Build Coastguard Worker   mGamma = gamma;
140*89a0ef05SAndroid Build Coastguard Worker   mEncPreset = preset;
141*89a0ef05SAndroid Build Coastguard Worker   mMinContentBoost = minContentBoost;
142*89a0ef05SAndroid Build Coastguard Worker   mMaxContentBoost = maxContentBoost;
143*89a0ef05SAndroid Build Coastguard Worker   mTargetDispPeakBrightness = targetDispPeakBrightness;
144*89a0ef05SAndroid Build Coastguard Worker }
145*89a0ef05SAndroid Build Coastguard Worker 
146*89a0ef05SAndroid Build Coastguard Worker /*
147*89a0ef05SAndroid Build Coastguard Worker  * Helper function copies the JPEG image from without EXIF.
148*89a0ef05SAndroid Build Coastguard Worker  *
149*89a0ef05SAndroid Build Coastguard Worker  * @param pDest destination of the data to be written.
150*89a0ef05SAndroid Build Coastguard Worker  * @param pSource source of data being written.
151*89a0ef05SAndroid Build Coastguard Worker  * @param exif_pos position of the EXIF package, which is aligned with jpegdecoder.getEXIFPos().
152*89a0ef05SAndroid Build Coastguard Worker  *                 (4 bytes offset to FF sign, the byte after FF E1 XX XX <this byte>).
153*89a0ef05SAndroid Build Coastguard Worker  * @param exif_size exif size without the initial 4 bytes, aligned with jpegdecoder.getEXIFSize().
154*89a0ef05SAndroid Build Coastguard Worker  */
copyJpegWithoutExif(uhdr_compressed_image_t * pDest,uhdr_compressed_image_t * pSource,size_t exif_pos,size_t exif_size)155*89a0ef05SAndroid Build Coastguard Worker static void copyJpegWithoutExif(uhdr_compressed_image_t* pDest, uhdr_compressed_image_t* pSource,
156*89a0ef05SAndroid Build Coastguard Worker                                 size_t exif_pos, size_t exif_size) {
157*89a0ef05SAndroid Build Coastguard Worker   const size_t exif_offset = 4;  // exif_pos has 4 bytes offset to the FF sign
158*89a0ef05SAndroid Build Coastguard Worker   pDest->data_sz = pSource->data_sz - exif_size - exif_offset;
159*89a0ef05SAndroid Build Coastguard Worker   pDest->data = new uint8_t[pDest->data_sz];
160*89a0ef05SAndroid Build Coastguard Worker   pDest->capacity = pDest->data_sz;
161*89a0ef05SAndroid Build Coastguard Worker   pDest->cg = pSource->cg;
162*89a0ef05SAndroid Build Coastguard Worker   pDest->ct = pSource->ct;
163*89a0ef05SAndroid Build Coastguard Worker   pDest->range = pSource->range;
164*89a0ef05SAndroid Build Coastguard Worker   memcpy(pDest->data, pSource->data, exif_pos - exif_offset);
165*89a0ef05SAndroid Build Coastguard Worker   memcpy((uint8_t*)pDest->data + exif_pos - exif_offset,
166*89a0ef05SAndroid Build Coastguard Worker          (uint8_t*)pSource->data + exif_pos + exif_size, pSource->data_sz - exif_pos - exif_size);
167*89a0ef05SAndroid Build Coastguard Worker }
168*89a0ef05SAndroid Build Coastguard Worker 
169*89a0ef05SAndroid Build Coastguard Worker /* Encode API-0 */
encodeJPEGR(uhdr_raw_image_t * hdr_intent,uhdr_compressed_image_t * dest,int quality,uhdr_mem_block_t * exif)170*89a0ef05SAndroid Build Coastguard Worker uhdr_error_info_t JpegR::encodeJPEGR(uhdr_raw_image_t* hdr_intent, uhdr_compressed_image_t* dest,
171*89a0ef05SAndroid Build Coastguard Worker                                      int quality, uhdr_mem_block_t* exif) {
172*89a0ef05SAndroid Build Coastguard Worker   uhdr_img_fmt_t sdr_intent_fmt;
173*89a0ef05SAndroid Build Coastguard Worker   if (hdr_intent->fmt == UHDR_IMG_FMT_24bppYCbCrP010) {
174*89a0ef05SAndroid Build Coastguard Worker     sdr_intent_fmt = UHDR_IMG_FMT_12bppYCbCr420;
175*89a0ef05SAndroid Build Coastguard Worker   } else if (hdr_intent->fmt == UHDR_IMG_FMT_30bppYCbCr444) {
176*89a0ef05SAndroid Build Coastguard Worker     sdr_intent_fmt = UHDR_IMG_FMT_24bppYCbCr444;
177*89a0ef05SAndroid Build Coastguard Worker   } else if (hdr_intent->fmt == UHDR_IMG_FMT_32bppRGBA1010102 ||
178*89a0ef05SAndroid Build Coastguard Worker              hdr_intent->fmt == UHDR_IMG_FMT_64bppRGBAHalfFloat) {
179*89a0ef05SAndroid Build Coastguard Worker     sdr_intent_fmt = UHDR_IMG_FMT_32bppRGBA8888;
180*89a0ef05SAndroid Build Coastguard Worker   } else {
181*89a0ef05SAndroid Build Coastguard Worker     uhdr_error_info_t status;
182*89a0ef05SAndroid Build Coastguard Worker     status.error_code = UHDR_CODEC_INVALID_PARAM;
183*89a0ef05SAndroid Build Coastguard Worker     status.has_detail = 1;
184*89a0ef05SAndroid Build Coastguard Worker     snprintf(status.detail, sizeof status.detail, "unsupported hdr intent color format %d",
185*89a0ef05SAndroid Build Coastguard Worker              hdr_intent->fmt);
186*89a0ef05SAndroid Build Coastguard Worker     return status;
187*89a0ef05SAndroid Build Coastguard Worker   }
188*89a0ef05SAndroid Build Coastguard Worker   std::unique_ptr<uhdr_raw_image_ext_t> sdr_intent = std::make_unique<uhdr_raw_image_ext_t>(
189*89a0ef05SAndroid Build Coastguard Worker       sdr_intent_fmt, UHDR_CG_UNSPECIFIED, UHDR_CT_UNSPECIFIED, UHDR_CR_UNSPECIFIED, hdr_intent->w,
190*89a0ef05SAndroid Build Coastguard Worker       hdr_intent->h, 64);
191*89a0ef05SAndroid Build Coastguard Worker 
192*89a0ef05SAndroid Build Coastguard Worker   // tone map
193*89a0ef05SAndroid Build Coastguard Worker   UHDR_ERR_CHECK(toneMap(hdr_intent, sdr_intent.get()));
194*89a0ef05SAndroid Build Coastguard Worker 
195*89a0ef05SAndroid Build Coastguard Worker   // If hdr intent is tonemapped internally, it is observed from quality pov,
196*89a0ef05SAndroid Build Coastguard Worker   // generateGainMapOnePass() is sufficient
197*89a0ef05SAndroid Build Coastguard Worker   mEncPreset = UHDR_USAGE_REALTIME;  // overriding the config option
198*89a0ef05SAndroid Build Coastguard Worker 
199*89a0ef05SAndroid Build Coastguard Worker   // generate gain map
200*89a0ef05SAndroid Build Coastguard Worker   uhdr_gainmap_metadata_ext_t metadata(kJpegrVersion);
201*89a0ef05SAndroid Build Coastguard Worker   std::unique_ptr<uhdr_raw_image_ext_t> gainmap;
202*89a0ef05SAndroid Build Coastguard Worker   UHDR_ERR_CHECK(generateGainMap(sdr_intent.get(), hdr_intent, &metadata, gainmap,
203*89a0ef05SAndroid Build Coastguard Worker                                  /* sdr_is_601 */ false,
204*89a0ef05SAndroid Build Coastguard Worker                                  /* use_luminance */ false));
205*89a0ef05SAndroid Build Coastguard Worker 
206*89a0ef05SAndroid Build Coastguard Worker   // compress gain map
207*89a0ef05SAndroid Build Coastguard Worker   JpegEncoderHelper jpeg_enc_obj_gm;
208*89a0ef05SAndroid Build Coastguard Worker   UHDR_ERR_CHECK(compressGainMap(gainmap.get(), &jpeg_enc_obj_gm));
209*89a0ef05SAndroid Build Coastguard Worker   uhdr_compressed_image_t gainmap_compressed = jpeg_enc_obj_gm.getCompressedImage();
210*89a0ef05SAndroid Build Coastguard Worker 
211*89a0ef05SAndroid Build Coastguard Worker   std::shared_ptr<DataStruct> icc = IccHelper::writeIccProfile(UHDR_CT_SRGB, sdr_intent->cg);
212*89a0ef05SAndroid Build Coastguard Worker 
213*89a0ef05SAndroid Build Coastguard Worker   // compress sdr image
214*89a0ef05SAndroid Build Coastguard Worker   std::unique_ptr<uhdr_raw_image_ext_t> sdr_intent_yuv_ext;
215*89a0ef05SAndroid Build Coastguard Worker   uhdr_raw_image_t* sdr_intent_yuv = sdr_intent.get();
216*89a0ef05SAndroid Build Coastguard Worker   if (isPixelFormatRgb(sdr_intent->fmt)) {
217*89a0ef05SAndroid Build Coastguard Worker #if (defined(UHDR_ENABLE_INTRINSICS) && (defined(__ARM_NEON__) || defined(__ARM_NEON)))
218*89a0ef05SAndroid Build Coastguard Worker     sdr_intent_yuv_ext = convert_raw_input_to_ycbcr_neon(sdr_intent.get());
219*89a0ef05SAndroid Build Coastguard Worker #else
220*89a0ef05SAndroid Build Coastguard Worker     sdr_intent_yuv_ext = convert_raw_input_to_ycbcr(sdr_intent.get());
221*89a0ef05SAndroid Build Coastguard Worker #endif
222*89a0ef05SAndroid Build Coastguard Worker     sdr_intent_yuv = sdr_intent_yuv_ext.get();
223*89a0ef05SAndroid Build Coastguard Worker   }
224*89a0ef05SAndroid Build Coastguard Worker 
225*89a0ef05SAndroid Build Coastguard Worker   JpegEncoderHelper jpeg_enc_obj_sdr;
226*89a0ef05SAndroid Build Coastguard Worker   UHDR_ERR_CHECK(
227*89a0ef05SAndroid Build Coastguard Worker       jpeg_enc_obj_sdr.compressImage(sdr_intent_yuv, quality, icc->getData(), icc->getLength()));
228*89a0ef05SAndroid Build Coastguard Worker   uhdr_compressed_image_t sdr_intent_compressed = jpeg_enc_obj_sdr.getCompressedImage();
229*89a0ef05SAndroid Build Coastguard Worker   sdr_intent_compressed.cg = sdr_intent_yuv->cg;
230*89a0ef05SAndroid Build Coastguard Worker 
231*89a0ef05SAndroid Build Coastguard Worker   // append gain map, no ICC since JPEG encode already did it
232*89a0ef05SAndroid Build Coastguard Worker   UHDR_ERR_CHECK(appendGainMap(&sdr_intent_compressed, &gainmap_compressed, exif, /* icc */ nullptr,
233*89a0ef05SAndroid Build Coastguard Worker                                /* icc size */ 0, &metadata, dest));
234*89a0ef05SAndroid Build Coastguard Worker   return g_no_error;
235*89a0ef05SAndroid Build Coastguard Worker }
236*89a0ef05SAndroid Build Coastguard Worker 
237*89a0ef05SAndroid Build Coastguard Worker /* Encode API-1 */
encodeJPEGR(uhdr_raw_image_t * hdr_intent,uhdr_raw_image_t * sdr_intent,uhdr_compressed_image_t * dest,int quality,uhdr_mem_block_t * exif)238*89a0ef05SAndroid Build Coastguard Worker uhdr_error_info_t JpegR::encodeJPEGR(uhdr_raw_image_t* hdr_intent, uhdr_raw_image_t* sdr_intent,
239*89a0ef05SAndroid Build Coastguard Worker                                      uhdr_compressed_image_t* dest, int quality,
240*89a0ef05SAndroid Build Coastguard Worker                                      uhdr_mem_block_t* exif) {
241*89a0ef05SAndroid Build Coastguard Worker   // generate gain map
242*89a0ef05SAndroid Build Coastguard Worker   uhdr_gainmap_metadata_ext_t metadata(kJpegrVersion);
243*89a0ef05SAndroid Build Coastguard Worker   std::unique_ptr<uhdr_raw_image_ext_t> gainmap;
244*89a0ef05SAndroid Build Coastguard Worker   UHDR_ERR_CHECK(generateGainMap(sdr_intent, hdr_intent, &metadata, gainmap));
245*89a0ef05SAndroid Build Coastguard Worker 
246*89a0ef05SAndroid Build Coastguard Worker   // compress gain map
247*89a0ef05SAndroid Build Coastguard Worker   JpegEncoderHelper jpeg_enc_obj_gm;
248*89a0ef05SAndroid Build Coastguard Worker   UHDR_ERR_CHECK(compressGainMap(gainmap.get(), &jpeg_enc_obj_gm));
249*89a0ef05SAndroid Build Coastguard Worker   uhdr_compressed_image_t gainmap_compressed = jpeg_enc_obj_gm.getCompressedImage();
250*89a0ef05SAndroid Build Coastguard Worker 
251*89a0ef05SAndroid Build Coastguard Worker   std::shared_ptr<DataStruct> icc = IccHelper::writeIccProfile(UHDR_CT_SRGB, sdr_intent->cg);
252*89a0ef05SAndroid Build Coastguard Worker 
253*89a0ef05SAndroid Build Coastguard Worker   std::unique_ptr<uhdr_raw_image_ext_t> sdr_intent_yuv_ext;
254*89a0ef05SAndroid Build Coastguard Worker   uhdr_raw_image_t* sdr_intent_yuv = sdr_intent;
255*89a0ef05SAndroid Build Coastguard Worker   if (isPixelFormatRgb(sdr_intent->fmt)) {
256*89a0ef05SAndroid Build Coastguard Worker #if (defined(UHDR_ENABLE_INTRINSICS) && (defined(__ARM_NEON__) || defined(__ARM_NEON)))
257*89a0ef05SAndroid Build Coastguard Worker     sdr_intent_yuv_ext = convert_raw_input_to_ycbcr_neon(sdr_intent);
258*89a0ef05SAndroid Build Coastguard Worker #else
259*89a0ef05SAndroid Build Coastguard Worker     sdr_intent_yuv_ext = convert_raw_input_to_ycbcr(sdr_intent);
260*89a0ef05SAndroid Build Coastguard Worker #endif
261*89a0ef05SAndroid Build Coastguard Worker     sdr_intent_yuv = sdr_intent_yuv_ext.get();
262*89a0ef05SAndroid Build Coastguard Worker   }
263*89a0ef05SAndroid Build Coastguard Worker 
264*89a0ef05SAndroid Build Coastguard Worker   // convert to bt601 YUV encoding for JPEG encode
265*89a0ef05SAndroid Build Coastguard Worker #if (defined(UHDR_ENABLE_INTRINSICS) && (defined(__ARM_NEON__) || defined(__ARM_NEON)))
266*89a0ef05SAndroid Build Coastguard Worker   UHDR_ERR_CHECK(convertYuv_neon(sdr_intent_yuv, sdr_intent_yuv->cg, UHDR_CG_DISPLAY_P3));
267*89a0ef05SAndroid Build Coastguard Worker #else
268*89a0ef05SAndroid Build Coastguard Worker   UHDR_ERR_CHECK(convertYuv(sdr_intent_yuv, sdr_intent_yuv->cg, UHDR_CG_DISPLAY_P3));
269*89a0ef05SAndroid Build Coastguard Worker #endif
270*89a0ef05SAndroid Build Coastguard Worker 
271*89a0ef05SAndroid Build Coastguard Worker   // compress sdr image
272*89a0ef05SAndroid Build Coastguard Worker   JpegEncoderHelper jpeg_enc_obj_sdr;
273*89a0ef05SAndroid Build Coastguard Worker   UHDR_ERR_CHECK(
274*89a0ef05SAndroid Build Coastguard Worker       jpeg_enc_obj_sdr.compressImage(sdr_intent_yuv, quality, icc->getData(), icc->getLength()));
275*89a0ef05SAndroid Build Coastguard Worker   uhdr_compressed_image_t sdr_intent_compressed = jpeg_enc_obj_sdr.getCompressedImage();
276*89a0ef05SAndroid Build Coastguard Worker   sdr_intent_compressed.cg = sdr_intent_yuv->cg;
277*89a0ef05SAndroid Build Coastguard Worker 
278*89a0ef05SAndroid Build Coastguard Worker   // append gain map, no ICC since JPEG encode already did it
279*89a0ef05SAndroid Build Coastguard Worker   UHDR_ERR_CHECK(appendGainMap(&sdr_intent_compressed, &gainmap_compressed, exif, /* icc */ nullptr,
280*89a0ef05SAndroid Build Coastguard Worker                                /* icc size */ 0, &metadata, dest));
281*89a0ef05SAndroid Build Coastguard Worker   return g_no_error;
282*89a0ef05SAndroid Build Coastguard Worker }
283*89a0ef05SAndroid Build Coastguard Worker 
284*89a0ef05SAndroid Build Coastguard Worker /* Encode API-2 */
encodeJPEGR(uhdr_raw_image_t * hdr_intent,uhdr_raw_image_t * sdr_intent,uhdr_compressed_image_t * sdr_intent_compressed,uhdr_compressed_image_t * dest)285*89a0ef05SAndroid Build Coastguard Worker uhdr_error_info_t JpegR::encodeJPEGR(uhdr_raw_image_t* hdr_intent, uhdr_raw_image_t* sdr_intent,
286*89a0ef05SAndroid Build Coastguard Worker                                      uhdr_compressed_image_t* sdr_intent_compressed,
287*89a0ef05SAndroid Build Coastguard Worker                                      uhdr_compressed_image_t* dest) {
288*89a0ef05SAndroid Build Coastguard Worker   JpegDecoderHelper jpeg_dec_obj_sdr;
289*89a0ef05SAndroid Build Coastguard Worker   UHDR_ERR_CHECK(jpeg_dec_obj_sdr.decompressImage(sdr_intent_compressed->data,
290*89a0ef05SAndroid Build Coastguard Worker                                                   sdr_intent_compressed->data_sz, PARSE_STREAM));
291*89a0ef05SAndroid Build Coastguard Worker   if (hdr_intent->w != jpeg_dec_obj_sdr.getDecompressedImageWidth() ||
292*89a0ef05SAndroid Build Coastguard Worker       hdr_intent->h != jpeg_dec_obj_sdr.getDecompressedImageHeight()) {
293*89a0ef05SAndroid Build Coastguard Worker     uhdr_error_info_t status;
294*89a0ef05SAndroid Build Coastguard Worker     status.error_code = UHDR_CODEC_INVALID_PARAM;
295*89a0ef05SAndroid Build Coastguard Worker     status.has_detail = 1;
296*89a0ef05SAndroid Build Coastguard Worker     snprintf(
297*89a0ef05SAndroid Build Coastguard Worker         status.detail, sizeof status.detail,
298*89a0ef05SAndroid Build Coastguard Worker         "sdr intent resolution %dx%d and compressed image sdr intent resolution %dx%d do not match",
299*89a0ef05SAndroid Build Coastguard Worker         sdr_intent->w, sdr_intent->h, (int)jpeg_dec_obj_sdr.getDecompressedImageWidth(),
300*89a0ef05SAndroid Build Coastguard Worker         (int)jpeg_dec_obj_sdr.getDecompressedImageHeight());
301*89a0ef05SAndroid Build Coastguard Worker     return status;
302*89a0ef05SAndroid Build Coastguard Worker   }
303*89a0ef05SAndroid Build Coastguard Worker 
304*89a0ef05SAndroid Build Coastguard Worker   // generate gain map
305*89a0ef05SAndroid Build Coastguard Worker   uhdr_gainmap_metadata_ext_t metadata(kJpegrVersion);
306*89a0ef05SAndroid Build Coastguard Worker   std::unique_ptr<uhdr_raw_image_ext_t> gainmap;
307*89a0ef05SAndroid Build Coastguard Worker   UHDR_ERR_CHECK(generateGainMap(sdr_intent, hdr_intent, &metadata, gainmap));
308*89a0ef05SAndroid Build Coastguard Worker 
309*89a0ef05SAndroid Build Coastguard Worker   // compress gain map
310*89a0ef05SAndroid Build Coastguard Worker   JpegEncoderHelper jpeg_enc_obj_gm;
311*89a0ef05SAndroid Build Coastguard Worker   UHDR_ERR_CHECK(compressGainMap(gainmap.get(), &jpeg_enc_obj_gm));
312*89a0ef05SAndroid Build Coastguard Worker   uhdr_compressed_image_t gainmap_compressed = jpeg_enc_obj_gm.getCompressedImage();
313*89a0ef05SAndroid Build Coastguard Worker 
314*89a0ef05SAndroid Build Coastguard Worker   return encodeJPEGR(sdr_intent_compressed, &gainmap_compressed, &metadata, dest);
315*89a0ef05SAndroid Build Coastguard Worker }
316*89a0ef05SAndroid Build Coastguard Worker 
317*89a0ef05SAndroid Build Coastguard Worker /* Encode API-3 */
encodeJPEGR(uhdr_raw_image_t * hdr_intent,uhdr_compressed_image_t * sdr_intent_compressed,uhdr_compressed_image_t * dest)318*89a0ef05SAndroid Build Coastguard Worker uhdr_error_info_t JpegR::encodeJPEGR(uhdr_raw_image_t* hdr_intent,
319*89a0ef05SAndroid Build Coastguard Worker                                      uhdr_compressed_image_t* sdr_intent_compressed,
320*89a0ef05SAndroid Build Coastguard Worker                                      uhdr_compressed_image_t* dest) {
321*89a0ef05SAndroid Build Coastguard Worker   // decode input jpeg, gamut is going to be bt601.
322*89a0ef05SAndroid Build Coastguard Worker   JpegDecoderHelper jpeg_dec_obj_sdr;
323*89a0ef05SAndroid Build Coastguard Worker   UHDR_ERR_CHECK(jpeg_dec_obj_sdr.decompressImage(sdr_intent_compressed->data,
324*89a0ef05SAndroid Build Coastguard Worker                                                   sdr_intent_compressed->data_sz));
325*89a0ef05SAndroid Build Coastguard Worker 
326*89a0ef05SAndroid Build Coastguard Worker   uhdr_raw_image_t sdr_intent = jpeg_dec_obj_sdr.getDecompressedImage();
327*89a0ef05SAndroid Build Coastguard Worker   if (jpeg_dec_obj_sdr.getICCSize() > 0) {
328*89a0ef05SAndroid Build Coastguard Worker     uhdr_color_gamut_t cg =
329*89a0ef05SAndroid Build Coastguard Worker         IccHelper::readIccColorGamut(jpeg_dec_obj_sdr.getICCPtr(), jpeg_dec_obj_sdr.getICCSize());
330*89a0ef05SAndroid Build Coastguard Worker     if (cg == UHDR_CG_UNSPECIFIED ||
331*89a0ef05SAndroid Build Coastguard Worker         (sdr_intent_compressed->cg != UHDR_CG_UNSPECIFIED && sdr_intent_compressed->cg != cg)) {
332*89a0ef05SAndroid Build Coastguard Worker       uhdr_error_info_t status;
333*89a0ef05SAndroid Build Coastguard Worker       status.error_code = UHDR_CODEC_INVALID_PARAM;
334*89a0ef05SAndroid Build Coastguard Worker       status.has_detail = 1;
335*89a0ef05SAndroid Build Coastguard Worker       snprintf(status.detail, sizeof status.detail,
336*89a0ef05SAndroid Build Coastguard Worker                "configured color gamut %d does not match with color gamut specified in icc box %d",
337*89a0ef05SAndroid Build Coastguard Worker                sdr_intent_compressed->cg, cg);
338*89a0ef05SAndroid Build Coastguard Worker       return status;
339*89a0ef05SAndroid Build Coastguard Worker     }
340*89a0ef05SAndroid Build Coastguard Worker     sdr_intent.cg = cg;
341*89a0ef05SAndroid Build Coastguard Worker   } else {
342*89a0ef05SAndroid Build Coastguard Worker     if (sdr_intent_compressed->cg <= UHDR_CG_UNSPECIFIED ||
343*89a0ef05SAndroid Build Coastguard Worker         sdr_intent_compressed->cg > UHDR_CG_BT_2100) {
344*89a0ef05SAndroid Build Coastguard Worker       uhdr_error_info_t status;
345*89a0ef05SAndroid Build Coastguard Worker       status.error_code = UHDR_CODEC_INVALID_PARAM;
346*89a0ef05SAndroid Build Coastguard Worker       status.has_detail = 1;
347*89a0ef05SAndroid Build Coastguard Worker       snprintf(status.detail, sizeof status.detail, "Unrecognized 420 color gamut %d",
348*89a0ef05SAndroid Build Coastguard Worker                sdr_intent_compressed->cg);
349*89a0ef05SAndroid Build Coastguard Worker       return status;
350*89a0ef05SAndroid Build Coastguard Worker     }
351*89a0ef05SAndroid Build Coastguard Worker     sdr_intent.cg = sdr_intent_compressed->cg;
352*89a0ef05SAndroid Build Coastguard Worker   }
353*89a0ef05SAndroid Build Coastguard Worker 
354*89a0ef05SAndroid Build Coastguard Worker   if (hdr_intent->w != sdr_intent.w || hdr_intent->h != sdr_intent.h) {
355*89a0ef05SAndroid Build Coastguard Worker     uhdr_error_info_t status;
356*89a0ef05SAndroid Build Coastguard Worker     status.error_code = UHDR_CODEC_INVALID_PARAM;
357*89a0ef05SAndroid Build Coastguard Worker     status.has_detail = 1;
358*89a0ef05SAndroid Build Coastguard Worker     snprintf(status.detail, sizeof status.detail,
359*89a0ef05SAndroid Build Coastguard Worker              "sdr intent resolution %dx%d and hdr intent resolution %dx%d do not match",
360*89a0ef05SAndroid Build Coastguard Worker              sdr_intent.w, sdr_intent.h, hdr_intent->w, hdr_intent->h);
361*89a0ef05SAndroid Build Coastguard Worker     return status;
362*89a0ef05SAndroid Build Coastguard Worker   }
363*89a0ef05SAndroid Build Coastguard Worker 
364*89a0ef05SAndroid Build Coastguard Worker   // generate gain map
365*89a0ef05SAndroid Build Coastguard Worker   uhdr_gainmap_metadata_ext_t metadata(kJpegrVersion);
366*89a0ef05SAndroid Build Coastguard Worker   std::unique_ptr<uhdr_raw_image_ext_t> gainmap;
367*89a0ef05SAndroid Build Coastguard Worker   UHDR_ERR_CHECK(
368*89a0ef05SAndroid Build Coastguard Worker       generateGainMap(&sdr_intent, hdr_intent, &metadata, gainmap, true /* sdr_is_601 */));
369*89a0ef05SAndroid Build Coastguard Worker 
370*89a0ef05SAndroid Build Coastguard Worker   // compress gain map
371*89a0ef05SAndroid Build Coastguard Worker   JpegEncoderHelper jpeg_enc_obj_gm;
372*89a0ef05SAndroid Build Coastguard Worker   UHDR_ERR_CHECK(compressGainMap(gainmap.get(), &jpeg_enc_obj_gm));
373*89a0ef05SAndroid Build Coastguard Worker   uhdr_compressed_image_t gainmap_compressed = jpeg_enc_obj_gm.getCompressedImage();
374*89a0ef05SAndroid Build Coastguard Worker 
375*89a0ef05SAndroid Build Coastguard Worker   return encodeJPEGR(sdr_intent_compressed, &gainmap_compressed, &metadata, dest);
376*89a0ef05SAndroid Build Coastguard Worker }
377*89a0ef05SAndroid Build Coastguard Worker 
378*89a0ef05SAndroid Build Coastguard Worker /* Encode API-4 */
encodeJPEGR(uhdr_compressed_image_t * base_img_compressed,uhdr_compressed_image_t * gainmap_img_compressed,uhdr_gainmap_metadata_ext_t * metadata,uhdr_compressed_image_t * dest)379*89a0ef05SAndroid Build Coastguard Worker uhdr_error_info_t JpegR::encodeJPEGR(uhdr_compressed_image_t* base_img_compressed,
380*89a0ef05SAndroid Build Coastguard Worker                                      uhdr_compressed_image_t* gainmap_img_compressed,
381*89a0ef05SAndroid Build Coastguard Worker                                      uhdr_gainmap_metadata_ext_t* metadata,
382*89a0ef05SAndroid Build Coastguard Worker                                      uhdr_compressed_image_t* dest) {
383*89a0ef05SAndroid Build Coastguard Worker   // We just want to check if ICC is present, so don't do a full decode. Note,
384*89a0ef05SAndroid Build Coastguard Worker   // this doesn't verify that the ICC is valid.
385*89a0ef05SAndroid Build Coastguard Worker   JpegDecoderHelper decoder;
386*89a0ef05SAndroid Build Coastguard Worker   UHDR_ERR_CHECK(decoder.parseImage(base_img_compressed->data, base_img_compressed->data_sz));
387*89a0ef05SAndroid Build Coastguard Worker 
388*89a0ef05SAndroid Build Coastguard Worker   // Add ICC if not already present.
389*89a0ef05SAndroid Build Coastguard Worker   if (decoder.getICCSize() > 0) {
390*89a0ef05SAndroid Build Coastguard Worker     UHDR_ERR_CHECK(appendGainMap(base_img_compressed, gainmap_img_compressed, /* exif */ nullptr,
391*89a0ef05SAndroid Build Coastguard Worker                                  /* icc */ nullptr, /* icc size */ 0, metadata, dest));
392*89a0ef05SAndroid Build Coastguard Worker   } else {
393*89a0ef05SAndroid Build Coastguard Worker     if (base_img_compressed->cg <= UHDR_CG_UNSPECIFIED ||
394*89a0ef05SAndroid Build Coastguard Worker         base_img_compressed->cg > UHDR_CG_BT_2100) {
395*89a0ef05SAndroid Build Coastguard Worker       uhdr_error_info_t status;
396*89a0ef05SAndroid Build Coastguard Worker       status.error_code = UHDR_CODEC_INVALID_PARAM;
397*89a0ef05SAndroid Build Coastguard Worker       status.has_detail = 1;
398*89a0ef05SAndroid Build Coastguard Worker       snprintf(status.detail, sizeof status.detail, "Unrecognized 420 color gamut %d",
399*89a0ef05SAndroid Build Coastguard Worker                base_img_compressed->cg);
400*89a0ef05SAndroid Build Coastguard Worker       return status;
401*89a0ef05SAndroid Build Coastguard Worker     }
402*89a0ef05SAndroid Build Coastguard Worker     std::shared_ptr<DataStruct> newIcc =
403*89a0ef05SAndroid Build Coastguard Worker         IccHelper::writeIccProfile(UHDR_CT_SRGB, base_img_compressed->cg);
404*89a0ef05SAndroid Build Coastguard Worker     UHDR_ERR_CHECK(appendGainMap(base_img_compressed, gainmap_img_compressed, /* exif */ nullptr,
405*89a0ef05SAndroid Build Coastguard Worker                                  newIcc->getData(), newIcc->getLength(), metadata, dest));
406*89a0ef05SAndroid Build Coastguard Worker   }
407*89a0ef05SAndroid Build Coastguard Worker 
408*89a0ef05SAndroid Build Coastguard Worker   return g_no_error;
409*89a0ef05SAndroid Build Coastguard Worker }
410*89a0ef05SAndroid Build Coastguard Worker 
convertYuv(uhdr_raw_image_t * image,uhdr_color_gamut_t src_encoding,uhdr_color_gamut_t dst_encoding)411*89a0ef05SAndroid Build Coastguard Worker uhdr_error_info_t JpegR::convertYuv(uhdr_raw_image_t* image, uhdr_color_gamut_t src_encoding,
412*89a0ef05SAndroid Build Coastguard Worker                                     uhdr_color_gamut_t dst_encoding) {
413*89a0ef05SAndroid Build Coastguard Worker   const std::array<float, 9>* coeffs_ptr = nullptr;
414*89a0ef05SAndroid Build Coastguard Worker   uhdr_error_info_t status = g_no_error;
415*89a0ef05SAndroid Build Coastguard Worker 
416*89a0ef05SAndroid Build Coastguard Worker   switch (src_encoding) {
417*89a0ef05SAndroid Build Coastguard Worker     case UHDR_CG_BT_709:
418*89a0ef05SAndroid Build Coastguard Worker       switch (dst_encoding) {
419*89a0ef05SAndroid Build Coastguard Worker         case UHDR_CG_BT_709:
420*89a0ef05SAndroid Build Coastguard Worker           return status;
421*89a0ef05SAndroid Build Coastguard Worker         case UHDR_CG_DISPLAY_P3:
422*89a0ef05SAndroid Build Coastguard Worker           coeffs_ptr = &kYuvBt709ToBt601;
423*89a0ef05SAndroid Build Coastguard Worker           break;
424*89a0ef05SAndroid Build Coastguard Worker         case UHDR_CG_BT_2100:
425*89a0ef05SAndroid Build Coastguard Worker           coeffs_ptr = &kYuvBt709ToBt2100;
426*89a0ef05SAndroid Build Coastguard Worker           break;
427*89a0ef05SAndroid Build Coastguard Worker         default:
428*89a0ef05SAndroid Build Coastguard Worker           status.error_code = UHDR_CODEC_INVALID_PARAM;
429*89a0ef05SAndroid Build Coastguard Worker           status.has_detail = 1;
430*89a0ef05SAndroid Build Coastguard Worker           snprintf(status.detail, sizeof status.detail, "Unrecognized dest color gamut %d",
431*89a0ef05SAndroid Build Coastguard Worker                    dst_encoding);
432*89a0ef05SAndroid Build Coastguard Worker           return status;
433*89a0ef05SAndroid Build Coastguard Worker       }
434*89a0ef05SAndroid Build Coastguard Worker       break;
435*89a0ef05SAndroid Build Coastguard Worker     case UHDR_CG_DISPLAY_P3:
436*89a0ef05SAndroid Build Coastguard Worker       switch (dst_encoding) {
437*89a0ef05SAndroid Build Coastguard Worker         case UHDR_CG_BT_709:
438*89a0ef05SAndroid Build Coastguard Worker           coeffs_ptr = &kYuvBt601ToBt709;
439*89a0ef05SAndroid Build Coastguard Worker           break;
440*89a0ef05SAndroid Build Coastguard Worker         case UHDR_CG_DISPLAY_P3:
441*89a0ef05SAndroid Build Coastguard Worker           return status;
442*89a0ef05SAndroid Build Coastguard Worker         case UHDR_CG_BT_2100:
443*89a0ef05SAndroid Build Coastguard Worker           coeffs_ptr = &kYuvBt601ToBt2100;
444*89a0ef05SAndroid Build Coastguard Worker           break;
445*89a0ef05SAndroid Build Coastguard Worker         default:
446*89a0ef05SAndroid Build Coastguard Worker           status.error_code = UHDR_CODEC_INVALID_PARAM;
447*89a0ef05SAndroid Build Coastguard Worker           status.has_detail = 1;
448*89a0ef05SAndroid Build Coastguard Worker           snprintf(status.detail, sizeof status.detail, "Unrecognized dest color gamut %d",
449*89a0ef05SAndroid Build Coastguard Worker                    dst_encoding);
450*89a0ef05SAndroid Build Coastguard Worker           return status;
451*89a0ef05SAndroid Build Coastguard Worker       }
452*89a0ef05SAndroid Build Coastguard Worker       break;
453*89a0ef05SAndroid Build Coastguard Worker     case UHDR_CG_BT_2100:
454*89a0ef05SAndroid Build Coastguard Worker       switch (dst_encoding) {
455*89a0ef05SAndroid Build Coastguard Worker         case UHDR_CG_BT_709:
456*89a0ef05SAndroid Build Coastguard Worker           coeffs_ptr = &kYuvBt2100ToBt709;
457*89a0ef05SAndroid Build Coastguard Worker           break;
458*89a0ef05SAndroid Build Coastguard Worker         case UHDR_CG_DISPLAY_P3:
459*89a0ef05SAndroid Build Coastguard Worker           coeffs_ptr = &kYuvBt2100ToBt601;
460*89a0ef05SAndroid Build Coastguard Worker           break;
461*89a0ef05SAndroid Build Coastguard Worker         case UHDR_CG_BT_2100:
462*89a0ef05SAndroid Build Coastguard Worker           return status;
463*89a0ef05SAndroid Build Coastguard Worker         default:
464*89a0ef05SAndroid Build Coastguard Worker           status.error_code = UHDR_CODEC_INVALID_PARAM;
465*89a0ef05SAndroid Build Coastguard Worker           status.has_detail = 1;
466*89a0ef05SAndroid Build Coastguard Worker           snprintf(status.detail, sizeof status.detail, "Unrecognized dest color gamut %d",
467*89a0ef05SAndroid Build Coastguard Worker                    dst_encoding);
468*89a0ef05SAndroid Build Coastguard Worker           return status;
469*89a0ef05SAndroid Build Coastguard Worker       }
470*89a0ef05SAndroid Build Coastguard Worker       break;
471*89a0ef05SAndroid Build Coastguard Worker     default:
472*89a0ef05SAndroid Build Coastguard Worker       status.error_code = UHDR_CODEC_INVALID_PARAM;
473*89a0ef05SAndroid Build Coastguard Worker       status.has_detail = 1;
474*89a0ef05SAndroid Build Coastguard Worker       snprintf(status.detail, sizeof status.detail, "Unrecognized src color gamut %d",
475*89a0ef05SAndroid Build Coastguard Worker                src_encoding);
476*89a0ef05SAndroid Build Coastguard Worker       return status;
477*89a0ef05SAndroid Build Coastguard Worker   }
478*89a0ef05SAndroid Build Coastguard Worker 
479*89a0ef05SAndroid Build Coastguard Worker   if (image->fmt == UHDR_IMG_FMT_12bppYCbCr420) {
480*89a0ef05SAndroid Build Coastguard Worker     transformYuv420(image, *coeffs_ptr);
481*89a0ef05SAndroid Build Coastguard Worker   } else if (image->fmt == UHDR_IMG_FMT_24bppYCbCr444) {
482*89a0ef05SAndroid Build Coastguard Worker     transformYuv444(image, *coeffs_ptr);
483*89a0ef05SAndroid Build Coastguard Worker   } else {
484*89a0ef05SAndroid Build Coastguard Worker     status.error_code = UHDR_CODEC_UNSUPPORTED_FEATURE;
485*89a0ef05SAndroid Build Coastguard Worker     status.has_detail = 1;
486*89a0ef05SAndroid Build Coastguard Worker     snprintf(status.detail, sizeof status.detail,
487*89a0ef05SAndroid Build Coastguard Worker              "No implementation available for performing gamut conversion for color format %d",
488*89a0ef05SAndroid Build Coastguard Worker              image->fmt);
489*89a0ef05SAndroid Build Coastguard Worker     return status;
490*89a0ef05SAndroid Build Coastguard Worker   }
491*89a0ef05SAndroid Build Coastguard Worker 
492*89a0ef05SAndroid Build Coastguard Worker   return status;
493*89a0ef05SAndroid Build Coastguard Worker }
494*89a0ef05SAndroid Build Coastguard Worker 
compressGainMap(uhdr_raw_image_t * gainmap_img,JpegEncoderHelper * jpeg_enc_obj)495*89a0ef05SAndroid Build Coastguard Worker uhdr_error_info_t JpegR::compressGainMap(uhdr_raw_image_t* gainmap_img,
496*89a0ef05SAndroid Build Coastguard Worker                                          JpegEncoderHelper* jpeg_enc_obj) {
497*89a0ef05SAndroid Build Coastguard Worker   return jpeg_enc_obj->compressImage(gainmap_img, mMapCompressQuality, nullptr, 0);
498*89a0ef05SAndroid Build Coastguard Worker }
499*89a0ef05SAndroid Build Coastguard Worker 
generateGainMap(uhdr_raw_image_t * sdr_intent,uhdr_raw_image_t * hdr_intent,uhdr_gainmap_metadata_ext_t * gainmap_metadata,std::unique_ptr<uhdr_raw_image_ext_t> & gainmap_img,bool sdr_is_601,bool use_luminance)500*89a0ef05SAndroid Build Coastguard Worker uhdr_error_info_t JpegR::generateGainMap(uhdr_raw_image_t* sdr_intent, uhdr_raw_image_t* hdr_intent,
501*89a0ef05SAndroid Build Coastguard Worker                                          uhdr_gainmap_metadata_ext_t* gainmap_metadata,
502*89a0ef05SAndroid Build Coastguard Worker                                          std::unique_ptr<uhdr_raw_image_ext_t>& gainmap_img,
503*89a0ef05SAndroid Build Coastguard Worker                                          bool sdr_is_601, bool use_luminance) {
504*89a0ef05SAndroid Build Coastguard Worker   uhdr_error_info_t status = g_no_error;
505*89a0ef05SAndroid Build Coastguard Worker 
506*89a0ef05SAndroid Build Coastguard Worker   if (sdr_intent->fmt != UHDR_IMG_FMT_24bppYCbCr444 &&
507*89a0ef05SAndroid Build Coastguard Worker       sdr_intent->fmt != UHDR_IMG_FMT_16bppYCbCr422 &&
508*89a0ef05SAndroid Build Coastguard Worker       sdr_intent->fmt != UHDR_IMG_FMT_12bppYCbCr420 &&
509*89a0ef05SAndroid Build Coastguard Worker       sdr_intent->fmt != UHDR_IMG_FMT_32bppRGBA8888) {
510*89a0ef05SAndroid Build Coastguard Worker     status.error_code = UHDR_CODEC_UNSUPPORTED_FEATURE;
511*89a0ef05SAndroid Build Coastguard Worker     status.has_detail = 1;
512*89a0ef05SAndroid Build Coastguard Worker     snprintf(status.detail, sizeof status.detail,
513*89a0ef05SAndroid Build Coastguard Worker              "generate gainmap method expects sdr intent color format to be one of "
514*89a0ef05SAndroid Build Coastguard Worker              "{UHDR_IMG_FMT_24bppYCbCr444, UHDR_IMG_FMT_16bppYCbCr422, "
515*89a0ef05SAndroid Build Coastguard Worker              "UHDR_IMG_FMT_12bppYCbCr420, UHDR_IMG_FMT_32bppRGBA8888}. Received %d",
516*89a0ef05SAndroid Build Coastguard Worker              sdr_intent->fmt);
517*89a0ef05SAndroid Build Coastguard Worker     return status;
518*89a0ef05SAndroid Build Coastguard Worker   }
519*89a0ef05SAndroid Build Coastguard Worker   if (hdr_intent->fmt != UHDR_IMG_FMT_24bppYCbCrP010 &&
520*89a0ef05SAndroid Build Coastguard Worker       hdr_intent->fmt != UHDR_IMG_FMT_30bppYCbCr444 &&
521*89a0ef05SAndroid Build Coastguard Worker       hdr_intent->fmt != UHDR_IMG_FMT_32bppRGBA1010102 &&
522*89a0ef05SAndroid Build Coastguard Worker       hdr_intent->fmt != UHDR_IMG_FMT_64bppRGBAHalfFloat) {
523*89a0ef05SAndroid Build Coastguard Worker     status.error_code = UHDR_CODEC_UNSUPPORTED_FEATURE;
524*89a0ef05SAndroid Build Coastguard Worker     status.has_detail = 1;
525*89a0ef05SAndroid Build Coastguard Worker     snprintf(status.detail, sizeof status.detail,
526*89a0ef05SAndroid Build Coastguard Worker              "generate gainmap method expects hdr intent color format to be one of "
527*89a0ef05SAndroid Build Coastguard Worker              "{UHDR_IMG_FMT_24bppYCbCrP010, UHDR_IMG_FMT_30bppYCbCr444, "
528*89a0ef05SAndroid Build Coastguard Worker              "UHDR_IMG_FMT_32bppRGBA1010102, UHDR_IMG_FMT_64bppRGBAHalfFloat}. Received %d",
529*89a0ef05SAndroid Build Coastguard Worker              hdr_intent->fmt);
530*89a0ef05SAndroid Build Coastguard Worker     return status;
531*89a0ef05SAndroid Build Coastguard Worker   }
532*89a0ef05SAndroid Build Coastguard Worker 
533*89a0ef05SAndroid Build Coastguard Worker   /*if (mUseMultiChannelGainMap) {
534*89a0ef05SAndroid Build Coastguard Worker     if (!kWriteIso21496_1Metadata || kWriteXmpMetadata) {
535*89a0ef05SAndroid Build Coastguard Worker       status.error_code = UHDR_CODEC_UNSUPPORTED_FEATURE;
536*89a0ef05SAndroid Build Coastguard Worker       status.has_detail = 1;
537*89a0ef05SAndroid Build Coastguard Worker       snprintf(status.detail, sizeof status.detail,
538*89a0ef05SAndroid Build Coastguard Worker                "Multi-channel gain map is only supported for ISO 21496-1 metadata");
539*89a0ef05SAndroid Build Coastguard Worker       return status;
540*89a0ef05SAndroid Build Coastguard Worker     }
541*89a0ef05SAndroid Build Coastguard Worker   }*/
542*89a0ef05SAndroid Build Coastguard Worker 
543*89a0ef05SAndroid Build Coastguard Worker   ColorTransformFn hdrInvOetf = getInverseOetfFn(hdr_intent->ct);
544*89a0ef05SAndroid Build Coastguard Worker   if (hdrInvOetf == nullptr) {
545*89a0ef05SAndroid Build Coastguard Worker     status.error_code = UHDR_CODEC_UNSUPPORTED_FEATURE;
546*89a0ef05SAndroid Build Coastguard Worker     status.has_detail = 1;
547*89a0ef05SAndroid Build Coastguard Worker     snprintf(status.detail, sizeof status.detail,
548*89a0ef05SAndroid Build Coastguard Worker              "No implementation available for converting transfer characteristics %d to linear",
549*89a0ef05SAndroid Build Coastguard Worker              hdr_intent->ct);
550*89a0ef05SAndroid Build Coastguard Worker     return status;
551*89a0ef05SAndroid Build Coastguard Worker   }
552*89a0ef05SAndroid Build Coastguard Worker 
553*89a0ef05SAndroid Build Coastguard Worker   LuminanceFn hdrLuminanceFn = getLuminanceFn(hdr_intent->cg);
554*89a0ef05SAndroid Build Coastguard Worker   if (hdrLuminanceFn == nullptr) {
555*89a0ef05SAndroid Build Coastguard Worker     status.error_code = UHDR_CODEC_UNSUPPORTED_FEATURE;
556*89a0ef05SAndroid Build Coastguard Worker     status.has_detail = 1;
557*89a0ef05SAndroid Build Coastguard Worker     snprintf(status.detail, sizeof status.detail,
558*89a0ef05SAndroid Build Coastguard Worker              "No implementation available for calculating luminance for color gamut %d",
559*89a0ef05SAndroid Build Coastguard Worker              hdr_intent->cg);
560*89a0ef05SAndroid Build Coastguard Worker     return status;
561*89a0ef05SAndroid Build Coastguard Worker   }
562*89a0ef05SAndroid Build Coastguard Worker 
563*89a0ef05SAndroid Build Coastguard Worker   SceneToDisplayLuminanceFn hdrOotfFn = getOotfFn(hdr_intent->ct);
564*89a0ef05SAndroid Build Coastguard Worker   if (hdrOotfFn == nullptr) {
565*89a0ef05SAndroid Build Coastguard Worker     status.error_code = UHDR_CODEC_UNSUPPORTED_FEATURE;
566*89a0ef05SAndroid Build Coastguard Worker     status.has_detail = 1;
567*89a0ef05SAndroid Build Coastguard Worker     snprintf(status.detail, sizeof status.detail,
568*89a0ef05SAndroid Build Coastguard Worker              "No implementation available for calculating Ootf for color transfer %d",
569*89a0ef05SAndroid Build Coastguard Worker              hdr_intent->ct);
570*89a0ef05SAndroid Build Coastguard Worker     return status;
571*89a0ef05SAndroid Build Coastguard Worker   }
572*89a0ef05SAndroid Build Coastguard Worker 
573*89a0ef05SAndroid Build Coastguard Worker   float hdr_white_nits = getReferenceDisplayPeakLuminanceInNits(hdr_intent->ct);
574*89a0ef05SAndroid Build Coastguard Worker   if (hdr_white_nits == -1.0f) {
575*89a0ef05SAndroid Build Coastguard Worker     status.error_code = UHDR_CODEC_UNSUPPORTED_FEATURE;
576*89a0ef05SAndroid Build Coastguard Worker     status.has_detail = 1;
577*89a0ef05SAndroid Build Coastguard Worker     snprintf(status.detail, sizeof status.detail,
578*89a0ef05SAndroid Build Coastguard Worker              "received invalid peak brightness %f nits for hdr reference display with color "
579*89a0ef05SAndroid Build Coastguard Worker              "transfer %d ",
580*89a0ef05SAndroid Build Coastguard Worker              hdr_white_nits, hdr_intent->ct);
581*89a0ef05SAndroid Build Coastguard Worker     return status;
582*89a0ef05SAndroid Build Coastguard Worker   }
583*89a0ef05SAndroid Build Coastguard Worker 
584*89a0ef05SAndroid Build Coastguard Worker   ColorTransformFn hdrGamutConversionFn = getGamutConversionFn(sdr_intent->cg, hdr_intent->cg);
585*89a0ef05SAndroid Build Coastguard Worker   if (hdrGamutConversionFn == nullptr) {
586*89a0ef05SAndroid Build Coastguard Worker     status.error_code = UHDR_CODEC_UNSUPPORTED_FEATURE;
587*89a0ef05SAndroid Build Coastguard Worker     status.has_detail = 1;
588*89a0ef05SAndroid Build Coastguard Worker     snprintf(status.detail, sizeof status.detail,
589*89a0ef05SAndroid Build Coastguard Worker              "No implementation available for gamut conversion from %d to %d", hdr_intent->cg,
590*89a0ef05SAndroid Build Coastguard Worker              sdr_intent->cg);
591*89a0ef05SAndroid Build Coastguard Worker     return status;
592*89a0ef05SAndroid Build Coastguard Worker   }
593*89a0ef05SAndroid Build Coastguard Worker 
594*89a0ef05SAndroid Build Coastguard Worker   ColorTransformFn sdrYuvToRgbFn = getYuvToRgbFn(sdr_intent->cg);
595*89a0ef05SAndroid Build Coastguard Worker   if (sdrYuvToRgbFn == nullptr) {
596*89a0ef05SAndroid Build Coastguard Worker     status.error_code = UHDR_CODEC_UNSUPPORTED_FEATURE;
597*89a0ef05SAndroid Build Coastguard Worker     status.has_detail = 1;
598*89a0ef05SAndroid Build Coastguard Worker     snprintf(status.detail, sizeof status.detail,
599*89a0ef05SAndroid Build Coastguard Worker              "No implementation available for converting yuv to rgb for color gamut %d",
600*89a0ef05SAndroid Build Coastguard Worker              sdr_intent->cg);
601*89a0ef05SAndroid Build Coastguard Worker     return status;
602*89a0ef05SAndroid Build Coastguard Worker   }
603*89a0ef05SAndroid Build Coastguard Worker 
604*89a0ef05SAndroid Build Coastguard Worker   ColorTransformFn hdrYuvToRgbFn = getYuvToRgbFn(hdr_intent->cg);
605*89a0ef05SAndroid Build Coastguard Worker   if (hdrYuvToRgbFn == nullptr) {
606*89a0ef05SAndroid Build Coastguard Worker     status.error_code = UHDR_CODEC_UNSUPPORTED_FEATURE;
607*89a0ef05SAndroid Build Coastguard Worker     status.has_detail = 1;
608*89a0ef05SAndroid Build Coastguard Worker     snprintf(status.detail, sizeof status.detail,
609*89a0ef05SAndroid Build Coastguard Worker              "No implementation available for converting yuv to rgb for color gamut %d",
610*89a0ef05SAndroid Build Coastguard Worker              hdr_intent->cg);
611*89a0ef05SAndroid Build Coastguard Worker     return status;
612*89a0ef05SAndroid Build Coastguard Worker   }
613*89a0ef05SAndroid Build Coastguard Worker 
614*89a0ef05SAndroid Build Coastguard Worker   LuminanceFn luminanceFn = getLuminanceFn(sdr_intent->cg);
615*89a0ef05SAndroid Build Coastguard Worker   if (luminanceFn == nullptr) {
616*89a0ef05SAndroid Build Coastguard Worker     status.error_code = UHDR_CODEC_UNSUPPORTED_FEATURE;
617*89a0ef05SAndroid Build Coastguard Worker     status.has_detail = 1;
618*89a0ef05SAndroid Build Coastguard Worker     snprintf(status.detail, sizeof status.detail,
619*89a0ef05SAndroid Build Coastguard Worker              "No implementation available for computing luminance for color gamut %d",
620*89a0ef05SAndroid Build Coastguard Worker              sdr_intent->cg);
621*89a0ef05SAndroid Build Coastguard Worker     return status;
622*89a0ef05SAndroid Build Coastguard Worker   }
623*89a0ef05SAndroid Build Coastguard Worker 
624*89a0ef05SAndroid Build Coastguard Worker   SamplePixelFn sdr_sample_pixel_fn = getSamplePixelFn(sdr_intent->fmt);
625*89a0ef05SAndroid Build Coastguard Worker   if (sdr_sample_pixel_fn == nullptr) {
626*89a0ef05SAndroid Build Coastguard Worker     status.error_code = UHDR_CODEC_UNSUPPORTED_FEATURE;
627*89a0ef05SAndroid Build Coastguard Worker     status.has_detail = 1;
628*89a0ef05SAndroid Build Coastguard Worker     snprintf(status.detail, sizeof status.detail,
629*89a0ef05SAndroid Build Coastguard Worker              "No implementation available for reading pixels for color format %d", sdr_intent->fmt);
630*89a0ef05SAndroid Build Coastguard Worker     return status;
631*89a0ef05SAndroid Build Coastguard Worker   }
632*89a0ef05SAndroid Build Coastguard Worker 
633*89a0ef05SAndroid Build Coastguard Worker   SamplePixelFn hdr_sample_pixel_fn = getSamplePixelFn(hdr_intent->fmt);
634*89a0ef05SAndroid Build Coastguard Worker   if (hdr_sample_pixel_fn == nullptr) {
635*89a0ef05SAndroid Build Coastguard Worker     status.error_code = UHDR_CODEC_UNSUPPORTED_FEATURE;
636*89a0ef05SAndroid Build Coastguard Worker     status.has_detail = 1;
637*89a0ef05SAndroid Build Coastguard Worker     snprintf(status.detail, sizeof status.detail,
638*89a0ef05SAndroid Build Coastguard Worker              "No implementation available for reading pixels for color format %d", hdr_intent->fmt);
639*89a0ef05SAndroid Build Coastguard Worker     return status;
640*89a0ef05SAndroid Build Coastguard Worker   }
641*89a0ef05SAndroid Build Coastguard Worker 
642*89a0ef05SAndroid Build Coastguard Worker   if (sdr_is_601) {
643*89a0ef05SAndroid Build Coastguard Worker     sdrYuvToRgbFn = p3YuvToRgb;
644*89a0ef05SAndroid Build Coastguard Worker   }
645*89a0ef05SAndroid Build Coastguard Worker 
646*89a0ef05SAndroid Build Coastguard Worker   unsigned int image_width = sdr_intent->w;
647*89a0ef05SAndroid Build Coastguard Worker   unsigned int image_height = sdr_intent->h;
648*89a0ef05SAndroid Build Coastguard Worker   unsigned int map_width = image_width / mMapDimensionScaleFactor;
649*89a0ef05SAndroid Build Coastguard Worker   unsigned int map_height = image_height / mMapDimensionScaleFactor;
650*89a0ef05SAndroid Build Coastguard Worker   if (map_width == 0 || map_height == 0) {
651*89a0ef05SAndroid Build Coastguard Worker     int scaleFactor = (std::min)(image_width, image_height);
652*89a0ef05SAndroid Build Coastguard Worker     scaleFactor = (scaleFactor >= DCTSIZE) ? (scaleFactor / DCTSIZE) : 1;
653*89a0ef05SAndroid Build Coastguard Worker     ALOGW(
654*89a0ef05SAndroid Build Coastguard Worker         "configured gainmap scale factor is resulting in gainmap width and/or height to be zero, "
655*89a0ef05SAndroid Build Coastguard Worker         "image width %u, image height %u, scale factor %d. Modifying gainmap scale factor to %d ",
656*89a0ef05SAndroid Build Coastguard Worker         image_width, image_height, mMapDimensionScaleFactor, scaleFactor);
657*89a0ef05SAndroid Build Coastguard Worker     setMapDimensionScaleFactor(scaleFactor);
658*89a0ef05SAndroid Build Coastguard Worker     map_width = image_width / mMapDimensionScaleFactor;
659*89a0ef05SAndroid Build Coastguard Worker     map_height = image_height / mMapDimensionScaleFactor;
660*89a0ef05SAndroid Build Coastguard Worker   }
661*89a0ef05SAndroid Build Coastguard Worker 
662*89a0ef05SAndroid Build Coastguard Worker   gainmap_img = std::make_unique<uhdr_raw_image_ext_t>(
663*89a0ef05SAndroid Build Coastguard Worker       mUseMultiChannelGainMap ? UHDR_IMG_FMT_24bppRGB888 : UHDR_IMG_FMT_8bppYCbCr400,
664*89a0ef05SAndroid Build Coastguard Worker       UHDR_CG_UNSPECIFIED, UHDR_CT_UNSPECIFIED, UHDR_CR_UNSPECIFIED, map_width, map_height, 64);
665*89a0ef05SAndroid Build Coastguard Worker   uhdr_raw_image_ext_t* dest = gainmap_img.get();
666*89a0ef05SAndroid Build Coastguard Worker 
667*89a0ef05SAndroid Build Coastguard Worker   auto generateGainMapOnePass = [this, sdr_intent, hdr_intent, gainmap_metadata, dest, map_height,
668*89a0ef05SAndroid Build Coastguard Worker                                  hdrInvOetf, hdrLuminanceFn, hdrOotfFn, hdrGamutConversionFn,
669*89a0ef05SAndroid Build Coastguard Worker                                  luminanceFn, sdrYuvToRgbFn, hdrYuvToRgbFn, sdr_sample_pixel_fn,
670*89a0ef05SAndroid Build Coastguard Worker                                  hdr_sample_pixel_fn, hdr_white_nits, use_luminance]() -> void {
671*89a0ef05SAndroid Build Coastguard Worker     gainmap_metadata->max_content_boost = hdr_white_nits / kSdrWhiteNits;
672*89a0ef05SAndroid Build Coastguard Worker     gainmap_metadata->min_content_boost = 1.0f;
673*89a0ef05SAndroid Build Coastguard Worker     gainmap_metadata->gamma = mGamma;
674*89a0ef05SAndroid Build Coastguard Worker     gainmap_metadata->offset_sdr = 0.0f;
675*89a0ef05SAndroid Build Coastguard Worker     gainmap_metadata->offset_hdr = 0.0f;
676*89a0ef05SAndroid Build Coastguard Worker     gainmap_metadata->hdr_capacity_min = 1.0f;
677*89a0ef05SAndroid Build Coastguard Worker     if (this->mTargetDispPeakBrightness != -1.0f) {
678*89a0ef05SAndroid Build Coastguard Worker       gainmap_metadata->hdr_capacity_max = this->mTargetDispPeakBrightness / kSdrWhiteNits;
679*89a0ef05SAndroid Build Coastguard Worker     } else {
680*89a0ef05SAndroid Build Coastguard Worker       gainmap_metadata->hdr_capacity_max = gainmap_metadata->max_content_boost;
681*89a0ef05SAndroid Build Coastguard Worker     }
682*89a0ef05SAndroid Build Coastguard Worker 
683*89a0ef05SAndroid Build Coastguard Worker     float log2MinBoost = log2(gainmap_metadata->min_content_boost);
684*89a0ef05SAndroid Build Coastguard Worker     float log2MaxBoost = log2(gainmap_metadata->max_content_boost);
685*89a0ef05SAndroid Build Coastguard Worker 
686*89a0ef05SAndroid Build Coastguard Worker     const int threads = (std::min)(GetCPUCoreCount(), 4u);
687*89a0ef05SAndroid Build Coastguard Worker     const int jobSizeInRows = 1;
688*89a0ef05SAndroid Build Coastguard Worker     unsigned int rowStep = threads == 1 ? map_height : jobSizeInRows;
689*89a0ef05SAndroid Build Coastguard Worker     JobQueue jobQueue;
690*89a0ef05SAndroid Build Coastguard Worker     std::function<void()> generateMap =
691*89a0ef05SAndroid Build Coastguard Worker         [this, sdr_intent, hdr_intent, gainmap_metadata, dest, hdrInvOetf, hdrLuminanceFn,
692*89a0ef05SAndroid Build Coastguard Worker          hdrOotfFn, hdrGamutConversionFn, luminanceFn, sdrYuvToRgbFn, hdrYuvToRgbFn,
693*89a0ef05SAndroid Build Coastguard Worker          sdr_sample_pixel_fn, hdr_sample_pixel_fn, hdr_white_nits, log2MinBoost, log2MaxBoost,
694*89a0ef05SAndroid Build Coastguard Worker          use_luminance, &jobQueue]() -> void {
695*89a0ef05SAndroid Build Coastguard Worker       unsigned int rowStart, rowEnd;
696*89a0ef05SAndroid Build Coastguard Worker       const bool isHdrIntentRgb = isPixelFormatRgb(hdr_intent->fmt);
697*89a0ef05SAndroid Build Coastguard Worker       const bool isSdrIntentRgb = isPixelFormatRgb(sdr_intent->fmt);
698*89a0ef05SAndroid Build Coastguard Worker       const float hdrSampleToNitsFactor =
699*89a0ef05SAndroid Build Coastguard Worker           hdr_intent->ct == UHDR_CT_LINEAR ? kSdrWhiteNits : hdr_white_nits;
700*89a0ef05SAndroid Build Coastguard Worker       ColorTransformFn clampPixel = hdr_intent->ct == UHDR_CT_LINEAR
701*89a0ef05SAndroid Build Coastguard Worker                                         ? static_cast<ColorTransformFn>(clampPixelFloatLinear)
702*89a0ef05SAndroid Build Coastguard Worker                                         : static_cast<ColorTransformFn>(clampPixelFloat);
703*89a0ef05SAndroid Build Coastguard Worker       while (jobQueue.dequeueJob(rowStart, rowEnd)) {
704*89a0ef05SAndroid Build Coastguard Worker         for (size_t y = rowStart; y < rowEnd; ++y) {
705*89a0ef05SAndroid Build Coastguard Worker           for (size_t x = 0; x < dest->w; ++x) {
706*89a0ef05SAndroid Build Coastguard Worker             Color sdr_rgb_gamma;
707*89a0ef05SAndroid Build Coastguard Worker 
708*89a0ef05SAndroid Build Coastguard Worker             if (isSdrIntentRgb) {
709*89a0ef05SAndroid Build Coastguard Worker               sdr_rgb_gamma = sdr_sample_pixel_fn(sdr_intent, mMapDimensionScaleFactor, x, y);
710*89a0ef05SAndroid Build Coastguard Worker             } else {
711*89a0ef05SAndroid Build Coastguard Worker               Color sdr_yuv_gamma = sdr_sample_pixel_fn(sdr_intent, mMapDimensionScaleFactor, x, y);
712*89a0ef05SAndroid Build Coastguard Worker               sdr_rgb_gamma = sdrYuvToRgbFn(sdr_yuv_gamma);
713*89a0ef05SAndroid Build Coastguard Worker             }
714*89a0ef05SAndroid Build Coastguard Worker 
715*89a0ef05SAndroid Build Coastguard Worker             // We are assuming the SDR input is always sRGB transfer.
716*89a0ef05SAndroid Build Coastguard Worker #if USE_SRGB_INVOETF_LUT
717*89a0ef05SAndroid Build Coastguard Worker             Color sdr_rgb = srgbInvOetfLUT(sdr_rgb_gamma);
718*89a0ef05SAndroid Build Coastguard Worker #else
719*89a0ef05SAndroid Build Coastguard Worker             Color sdr_rgb = srgbInvOetf(sdr_rgb_gamma);
720*89a0ef05SAndroid Build Coastguard Worker #endif
721*89a0ef05SAndroid Build Coastguard Worker 
722*89a0ef05SAndroid Build Coastguard Worker             Color hdr_rgb_gamma;
723*89a0ef05SAndroid Build Coastguard Worker 
724*89a0ef05SAndroid Build Coastguard Worker             if (isHdrIntentRgb) {
725*89a0ef05SAndroid Build Coastguard Worker               hdr_rgb_gamma = hdr_sample_pixel_fn(hdr_intent, mMapDimensionScaleFactor, x, y);
726*89a0ef05SAndroid Build Coastguard Worker             } else {
727*89a0ef05SAndroid Build Coastguard Worker               Color hdr_yuv_gamma = hdr_sample_pixel_fn(hdr_intent, mMapDimensionScaleFactor, x, y);
728*89a0ef05SAndroid Build Coastguard Worker               hdr_rgb_gamma = hdrYuvToRgbFn(hdr_yuv_gamma);
729*89a0ef05SAndroid Build Coastguard Worker             }
730*89a0ef05SAndroid Build Coastguard Worker             Color hdr_rgb = hdrInvOetf(hdr_rgb_gamma);
731*89a0ef05SAndroid Build Coastguard Worker             hdr_rgb = hdrOotfFn(hdr_rgb, hdrLuminanceFn);
732*89a0ef05SAndroid Build Coastguard Worker             hdr_rgb = hdrGamutConversionFn(hdr_rgb);
733*89a0ef05SAndroid Build Coastguard Worker             hdr_rgb = clampPixel(hdr_rgb);
734*89a0ef05SAndroid Build Coastguard Worker 
735*89a0ef05SAndroid Build Coastguard Worker             if (mUseMultiChannelGainMap) {
736*89a0ef05SAndroid Build Coastguard Worker               Color sdr_rgb_nits = sdr_rgb * kSdrWhiteNits;
737*89a0ef05SAndroid Build Coastguard Worker               Color hdr_rgb_nits = hdr_rgb * hdrSampleToNitsFactor;
738*89a0ef05SAndroid Build Coastguard Worker               size_t pixel_idx = (x + y * dest->stride[UHDR_PLANE_PACKED]) * 3;
739*89a0ef05SAndroid Build Coastguard Worker 
740*89a0ef05SAndroid Build Coastguard Worker               reinterpret_cast<uint8_t*>(dest->planes[UHDR_PLANE_PACKED])[pixel_idx] = encodeGain(
741*89a0ef05SAndroid Build Coastguard Worker                   sdr_rgb_nits.r, hdr_rgb_nits.r, gainmap_metadata, log2MinBoost, log2MaxBoost);
742*89a0ef05SAndroid Build Coastguard Worker               reinterpret_cast<uint8_t*>(dest->planes[UHDR_PLANE_PACKED])[pixel_idx + 1] =
743*89a0ef05SAndroid Build Coastguard Worker                   encodeGain(sdr_rgb_nits.g, hdr_rgb_nits.g, gainmap_metadata, log2MinBoost,
744*89a0ef05SAndroid Build Coastguard Worker                              log2MaxBoost);
745*89a0ef05SAndroid Build Coastguard Worker               reinterpret_cast<uint8_t*>(dest->planes[UHDR_PLANE_PACKED])[pixel_idx + 2] =
746*89a0ef05SAndroid Build Coastguard Worker                   encodeGain(sdr_rgb_nits.b, hdr_rgb_nits.b, gainmap_metadata, log2MinBoost,
747*89a0ef05SAndroid Build Coastguard Worker                              log2MaxBoost);
748*89a0ef05SAndroid Build Coastguard Worker             } else {
749*89a0ef05SAndroid Build Coastguard Worker               float sdr_y_nits;
750*89a0ef05SAndroid Build Coastguard Worker               float hdr_y_nits;
751*89a0ef05SAndroid Build Coastguard Worker               if (use_luminance) {
752*89a0ef05SAndroid Build Coastguard Worker                 sdr_y_nits = luminanceFn(sdr_rgb) * kSdrWhiteNits;
753*89a0ef05SAndroid Build Coastguard Worker                 hdr_y_nits = luminanceFn(hdr_rgb) * hdrSampleToNitsFactor;
754*89a0ef05SAndroid Build Coastguard Worker               } else {
755*89a0ef05SAndroid Build Coastguard Worker                 sdr_y_nits = fmax(sdr_rgb.r, fmax(sdr_rgb.g, sdr_rgb.b)) * kSdrWhiteNits;
756*89a0ef05SAndroid Build Coastguard Worker                 hdr_y_nits = fmax(hdr_rgb.r, fmax(hdr_rgb.g, hdr_rgb.b)) * hdrSampleToNitsFactor;
757*89a0ef05SAndroid Build Coastguard Worker               }
758*89a0ef05SAndroid Build Coastguard Worker 
759*89a0ef05SAndroid Build Coastguard Worker               size_t pixel_idx = x + y * dest->stride[UHDR_PLANE_Y];
760*89a0ef05SAndroid Build Coastguard Worker 
761*89a0ef05SAndroid Build Coastguard Worker               reinterpret_cast<uint8_t*>(dest->planes[UHDR_PLANE_Y])[pixel_idx] =
762*89a0ef05SAndroid Build Coastguard Worker                   encodeGain(sdr_y_nits, hdr_y_nits, gainmap_metadata, log2MinBoost, log2MaxBoost);
763*89a0ef05SAndroid Build Coastguard Worker             }
764*89a0ef05SAndroid Build Coastguard Worker           }
765*89a0ef05SAndroid Build Coastguard Worker         }
766*89a0ef05SAndroid Build Coastguard Worker       }
767*89a0ef05SAndroid Build Coastguard Worker     };
768*89a0ef05SAndroid Build Coastguard Worker 
769*89a0ef05SAndroid Build Coastguard Worker     // generate map
770*89a0ef05SAndroid Build Coastguard Worker     std::vector<std::thread> workers;
771*89a0ef05SAndroid Build Coastguard Worker     for (int th = 0; th < threads - 1; th++) {
772*89a0ef05SAndroid Build Coastguard Worker       workers.push_back(std::thread(generateMap));
773*89a0ef05SAndroid Build Coastguard Worker     }
774*89a0ef05SAndroid Build Coastguard Worker 
775*89a0ef05SAndroid Build Coastguard Worker     for (unsigned int rowStart = 0; rowStart < map_height;) {
776*89a0ef05SAndroid Build Coastguard Worker       unsigned int rowEnd = (std::min)(rowStart + rowStep, map_height);
777*89a0ef05SAndroid Build Coastguard Worker       jobQueue.enqueueJob(rowStart, rowEnd);
778*89a0ef05SAndroid Build Coastguard Worker       rowStart = rowEnd;
779*89a0ef05SAndroid Build Coastguard Worker     }
780*89a0ef05SAndroid Build Coastguard Worker     jobQueue.markQueueForEnd();
781*89a0ef05SAndroid Build Coastguard Worker     generateMap();
782*89a0ef05SAndroid Build Coastguard Worker     std::for_each(workers.begin(), workers.end(), [](std::thread& t) { t.join(); });
783*89a0ef05SAndroid Build Coastguard Worker   };
784*89a0ef05SAndroid Build Coastguard Worker 
785*89a0ef05SAndroid Build Coastguard Worker   auto generateGainMapTwoPass =
786*89a0ef05SAndroid Build Coastguard Worker       [this, sdr_intent, hdr_intent, gainmap_metadata, dest, map_width, map_height, hdrInvOetf,
787*89a0ef05SAndroid Build Coastguard Worker        hdrLuminanceFn, hdrOotfFn, hdrGamutConversionFn, luminanceFn, sdrYuvToRgbFn, hdrYuvToRgbFn,
788*89a0ef05SAndroid Build Coastguard Worker        sdr_sample_pixel_fn, hdr_sample_pixel_fn, hdr_white_nits, use_luminance]() -> void {
789*89a0ef05SAndroid Build Coastguard Worker     uhdr_memory_block_t gainmap_mem((size_t)map_width * map_height * sizeof(float) *
790*89a0ef05SAndroid Build Coastguard Worker                                     (mUseMultiChannelGainMap ? 3 : 1));
791*89a0ef05SAndroid Build Coastguard Worker     float* gainmap_data = reinterpret_cast<float*>(gainmap_mem.m_buffer.get());
792*89a0ef05SAndroid Build Coastguard Worker     float gainmap_min[3] = {127.0f, 127.0f, 127.0f};
793*89a0ef05SAndroid Build Coastguard Worker     float gainmap_max[3] = {-128.0f, -128.0f, -128.0f};
794*89a0ef05SAndroid Build Coastguard Worker     std::mutex gainmap_minmax;
795*89a0ef05SAndroid Build Coastguard Worker 
796*89a0ef05SAndroid Build Coastguard Worker     const int threads = (std::min)(GetCPUCoreCount(), 4u);
797*89a0ef05SAndroid Build Coastguard Worker     const int jobSizeInRows = 1;
798*89a0ef05SAndroid Build Coastguard Worker     unsigned int rowStep = threads == 1 ? map_height : jobSizeInRows;
799*89a0ef05SAndroid Build Coastguard Worker     JobQueue jobQueue;
800*89a0ef05SAndroid Build Coastguard Worker     std::function<void()> generateMap =
801*89a0ef05SAndroid Build Coastguard Worker         [this, sdr_intent, hdr_intent, gainmap_data, map_width, hdrInvOetf, hdrLuminanceFn,
802*89a0ef05SAndroid Build Coastguard Worker          hdrOotfFn, hdrGamutConversionFn, luminanceFn, sdrYuvToRgbFn, hdrYuvToRgbFn,
803*89a0ef05SAndroid Build Coastguard Worker          sdr_sample_pixel_fn, hdr_sample_pixel_fn, hdr_white_nits, use_luminance, &gainmap_min,
804*89a0ef05SAndroid Build Coastguard Worker          &gainmap_max, &gainmap_minmax, &jobQueue]() -> void {
805*89a0ef05SAndroid Build Coastguard Worker       unsigned int rowStart, rowEnd;
806*89a0ef05SAndroid Build Coastguard Worker       const bool isHdrIntentRgb = isPixelFormatRgb(hdr_intent->fmt);
807*89a0ef05SAndroid Build Coastguard Worker       const bool isSdrIntentRgb = isPixelFormatRgb(sdr_intent->fmt);
808*89a0ef05SAndroid Build Coastguard Worker       const float hdrSampleToNitsFactor =
809*89a0ef05SAndroid Build Coastguard Worker           hdr_intent->ct == UHDR_CT_LINEAR ? kSdrWhiteNits : hdr_white_nits;
810*89a0ef05SAndroid Build Coastguard Worker       ColorTransformFn clampPixel = hdr_intent->ct == UHDR_CT_LINEAR
811*89a0ef05SAndroid Build Coastguard Worker                                         ? static_cast<ColorTransformFn>(clampPixelFloatLinear)
812*89a0ef05SAndroid Build Coastguard Worker                                         : static_cast<ColorTransformFn>(clampPixelFloat);
813*89a0ef05SAndroid Build Coastguard Worker       float gainmap_min_th[3] = {127.0f, 127.0f, 127.0f};
814*89a0ef05SAndroid Build Coastguard Worker       float gainmap_max_th[3] = {-128.0f, -128.0f, -128.0f};
815*89a0ef05SAndroid Build Coastguard Worker 
816*89a0ef05SAndroid Build Coastguard Worker       while (jobQueue.dequeueJob(rowStart, rowEnd)) {
817*89a0ef05SAndroid Build Coastguard Worker         for (size_t y = rowStart; y < rowEnd; ++y) {
818*89a0ef05SAndroid Build Coastguard Worker           for (size_t x = 0; x < map_width; ++x) {
819*89a0ef05SAndroid Build Coastguard Worker             Color sdr_rgb_gamma;
820*89a0ef05SAndroid Build Coastguard Worker 
821*89a0ef05SAndroid Build Coastguard Worker             if (isSdrIntentRgb) {
822*89a0ef05SAndroid Build Coastguard Worker               sdr_rgb_gamma = sdr_sample_pixel_fn(sdr_intent, mMapDimensionScaleFactor, x, y);
823*89a0ef05SAndroid Build Coastguard Worker             } else {
824*89a0ef05SAndroid Build Coastguard Worker               Color sdr_yuv_gamma = sdr_sample_pixel_fn(sdr_intent, mMapDimensionScaleFactor, x, y);
825*89a0ef05SAndroid Build Coastguard Worker               sdr_rgb_gamma = sdrYuvToRgbFn(sdr_yuv_gamma);
826*89a0ef05SAndroid Build Coastguard Worker             }
827*89a0ef05SAndroid Build Coastguard Worker 
828*89a0ef05SAndroid Build Coastguard Worker             // We are assuming the SDR input is always sRGB transfer.
829*89a0ef05SAndroid Build Coastguard Worker #if USE_SRGB_INVOETF_LUT
830*89a0ef05SAndroid Build Coastguard Worker             Color sdr_rgb = srgbInvOetfLUT(sdr_rgb_gamma);
831*89a0ef05SAndroid Build Coastguard Worker #else
832*89a0ef05SAndroid Build Coastguard Worker             Color sdr_rgb = srgbInvOetf(sdr_rgb_gamma);
833*89a0ef05SAndroid Build Coastguard Worker #endif
834*89a0ef05SAndroid Build Coastguard Worker 
835*89a0ef05SAndroid Build Coastguard Worker             Color hdr_rgb_gamma;
836*89a0ef05SAndroid Build Coastguard Worker 
837*89a0ef05SAndroid Build Coastguard Worker             if (isHdrIntentRgb) {
838*89a0ef05SAndroid Build Coastguard Worker               hdr_rgb_gamma = hdr_sample_pixel_fn(hdr_intent, mMapDimensionScaleFactor, x, y);
839*89a0ef05SAndroid Build Coastguard Worker             } else {
840*89a0ef05SAndroid Build Coastguard Worker               Color hdr_yuv_gamma = hdr_sample_pixel_fn(hdr_intent, mMapDimensionScaleFactor, x, y);
841*89a0ef05SAndroid Build Coastguard Worker               hdr_rgb_gamma = hdrYuvToRgbFn(hdr_yuv_gamma);
842*89a0ef05SAndroid Build Coastguard Worker             }
843*89a0ef05SAndroid Build Coastguard Worker             Color hdr_rgb = hdrInvOetf(hdr_rgb_gamma);
844*89a0ef05SAndroid Build Coastguard Worker             hdr_rgb = hdrOotfFn(hdr_rgb, hdrLuminanceFn);
845*89a0ef05SAndroid Build Coastguard Worker             hdr_rgb = hdrGamutConversionFn(hdr_rgb);
846*89a0ef05SAndroid Build Coastguard Worker             hdr_rgb = clampPixel(hdr_rgb);
847*89a0ef05SAndroid Build Coastguard Worker 
848*89a0ef05SAndroid Build Coastguard Worker             if (mUseMultiChannelGainMap) {
849*89a0ef05SAndroid Build Coastguard Worker               Color sdr_rgb_nits = sdr_rgb * kSdrWhiteNits;
850*89a0ef05SAndroid Build Coastguard Worker               Color hdr_rgb_nits = hdr_rgb * hdrSampleToNitsFactor;
851*89a0ef05SAndroid Build Coastguard Worker               size_t pixel_idx = (x + y * map_width) * 3;
852*89a0ef05SAndroid Build Coastguard Worker 
853*89a0ef05SAndroid Build Coastguard Worker               gainmap_data[pixel_idx] = computeGain(sdr_rgb_nits.r, hdr_rgb_nits.r);
854*89a0ef05SAndroid Build Coastguard Worker               gainmap_data[pixel_idx + 1] = computeGain(sdr_rgb_nits.g, hdr_rgb_nits.g);
855*89a0ef05SAndroid Build Coastguard Worker               gainmap_data[pixel_idx + 2] = computeGain(sdr_rgb_nits.b, hdr_rgb_nits.b);
856*89a0ef05SAndroid Build Coastguard Worker               for (int i = 0; i < 3; i++) {
857*89a0ef05SAndroid Build Coastguard Worker                 gainmap_min_th[i] = (std::min)(gainmap_data[pixel_idx + i], gainmap_min_th[i]);
858*89a0ef05SAndroid Build Coastguard Worker                 gainmap_max_th[i] = (std::max)(gainmap_data[pixel_idx + i], gainmap_max_th[i]);
859*89a0ef05SAndroid Build Coastguard Worker               }
860*89a0ef05SAndroid Build Coastguard Worker             } else {
861*89a0ef05SAndroid Build Coastguard Worker               float sdr_y_nits;
862*89a0ef05SAndroid Build Coastguard Worker               float hdr_y_nits;
863*89a0ef05SAndroid Build Coastguard Worker 
864*89a0ef05SAndroid Build Coastguard Worker               if (use_luminance) {
865*89a0ef05SAndroid Build Coastguard Worker                 sdr_y_nits = luminanceFn(sdr_rgb) * kSdrWhiteNits;
866*89a0ef05SAndroid Build Coastguard Worker                 hdr_y_nits = luminanceFn(hdr_rgb) * hdrSampleToNitsFactor;
867*89a0ef05SAndroid Build Coastguard Worker               } else {
868*89a0ef05SAndroid Build Coastguard Worker                 sdr_y_nits = fmax(sdr_rgb.r, fmax(sdr_rgb.g, sdr_rgb.b)) * kSdrWhiteNits;
869*89a0ef05SAndroid Build Coastguard Worker                 hdr_y_nits = fmax(hdr_rgb.r, fmax(hdr_rgb.g, hdr_rgb.b)) * hdrSampleToNitsFactor;
870*89a0ef05SAndroid Build Coastguard Worker               }
871*89a0ef05SAndroid Build Coastguard Worker 
872*89a0ef05SAndroid Build Coastguard Worker               size_t pixel_idx = x + y * map_width;
873*89a0ef05SAndroid Build Coastguard Worker               gainmap_data[pixel_idx] = computeGain(sdr_y_nits, hdr_y_nits);
874*89a0ef05SAndroid Build Coastguard Worker               gainmap_min_th[0] = (std::min)(gainmap_data[pixel_idx], gainmap_min_th[0]);
875*89a0ef05SAndroid Build Coastguard Worker               gainmap_max_th[0] = (std::max)(gainmap_data[pixel_idx], gainmap_max_th[0]);
876*89a0ef05SAndroid Build Coastguard Worker             }
877*89a0ef05SAndroid Build Coastguard Worker           }
878*89a0ef05SAndroid Build Coastguard Worker         }
879*89a0ef05SAndroid Build Coastguard Worker       }
880*89a0ef05SAndroid Build Coastguard Worker       {
881*89a0ef05SAndroid Build Coastguard Worker         std::unique_lock<std::mutex> lock{gainmap_minmax};
882*89a0ef05SAndroid Build Coastguard Worker         for (int index = 0; index < (mUseMultiChannelGainMap ? 3 : 1); index++) {
883*89a0ef05SAndroid Build Coastguard Worker           gainmap_min[index] = (std::min)(gainmap_min[index], gainmap_min_th[index]);
884*89a0ef05SAndroid Build Coastguard Worker           gainmap_max[index] = (std::max)(gainmap_max[index], gainmap_max_th[index]);
885*89a0ef05SAndroid Build Coastguard Worker         }
886*89a0ef05SAndroid Build Coastguard Worker       }
887*89a0ef05SAndroid Build Coastguard Worker     };
888*89a0ef05SAndroid Build Coastguard Worker 
889*89a0ef05SAndroid Build Coastguard Worker     // generate map
890*89a0ef05SAndroid Build Coastguard Worker     std::vector<std::thread> workers;
891*89a0ef05SAndroid Build Coastguard Worker     for (int th = 0; th < threads - 1; th++) {
892*89a0ef05SAndroid Build Coastguard Worker       workers.push_back(std::thread(generateMap));
893*89a0ef05SAndroid Build Coastguard Worker     }
894*89a0ef05SAndroid Build Coastguard Worker 
895*89a0ef05SAndroid Build Coastguard Worker     for (unsigned int rowStart = 0; rowStart < map_height;) {
896*89a0ef05SAndroid Build Coastguard Worker       unsigned int rowEnd = (std::min)(rowStart + rowStep, map_height);
897*89a0ef05SAndroid Build Coastguard Worker       jobQueue.enqueueJob(rowStart, rowEnd);
898*89a0ef05SAndroid Build Coastguard Worker       rowStart = rowEnd;
899*89a0ef05SAndroid Build Coastguard Worker     }
900*89a0ef05SAndroid Build Coastguard Worker     jobQueue.markQueueForEnd();
901*89a0ef05SAndroid Build Coastguard Worker     generateMap();
902*89a0ef05SAndroid Build Coastguard Worker     std::for_each(workers.begin(), workers.end(), [](std::thread& t) { t.join(); });
903*89a0ef05SAndroid Build Coastguard Worker 
904*89a0ef05SAndroid Build Coastguard Worker     float min_content_boost_log2 = gainmap_min[0];
905*89a0ef05SAndroid Build Coastguard Worker     float max_content_boost_log2 = gainmap_max[0];
906*89a0ef05SAndroid Build Coastguard Worker     for (int index = 1; index < (mUseMultiChannelGainMap ? 3 : 1); index++) {
907*89a0ef05SAndroid Build Coastguard Worker       min_content_boost_log2 = (std::min)(gainmap_min[index], min_content_boost_log2);
908*89a0ef05SAndroid Build Coastguard Worker       max_content_boost_log2 = (std::max)(gainmap_max[index], max_content_boost_log2);
909*89a0ef05SAndroid Build Coastguard Worker     }
910*89a0ef05SAndroid Build Coastguard Worker     // -13.0 emphirically is a small enough gain factor that is capable of representing hdr
911*89a0ef05SAndroid Build Coastguard Worker     // black from any sdr luminance. Allowing further excursion might not offer any benefit and on
912*89a0ef05SAndroid Build Coastguard Worker     // the downside can cause bigger error during affine map and inverse map.
913*89a0ef05SAndroid Build Coastguard Worker     min_content_boost_log2 = (std::max)(-13.0f, min_content_boost_log2);
914*89a0ef05SAndroid Build Coastguard Worker     if (this->mMaxContentBoost != FLT_MAX) {
915*89a0ef05SAndroid Build Coastguard Worker       float suggestion = log2(this->mMaxContentBoost);
916*89a0ef05SAndroid Build Coastguard Worker       max_content_boost_log2 = (std::min)(max_content_boost_log2, suggestion);
917*89a0ef05SAndroid Build Coastguard Worker     }
918*89a0ef05SAndroid Build Coastguard Worker     if (this->mMinContentBoost != FLT_MIN) {
919*89a0ef05SAndroid Build Coastguard Worker       float suggestion = log2(this->mMinContentBoost);
920*89a0ef05SAndroid Build Coastguard Worker       min_content_boost_log2 = (std::max)(min_content_boost_log2, suggestion);
921*89a0ef05SAndroid Build Coastguard Worker     }
922*89a0ef05SAndroid Build Coastguard Worker     if (fabs(max_content_boost_log2 - min_content_boost_log2) < FLT_EPSILON) {
923*89a0ef05SAndroid Build Coastguard Worker       max_content_boost_log2 += 0.1;  // to avoid div by zero during affine transform
924*89a0ef05SAndroid Build Coastguard Worker     }
925*89a0ef05SAndroid Build Coastguard Worker 
926*89a0ef05SAndroid Build Coastguard Worker     std::function<void()> encodeMap = [this, gainmap_data, map_width, dest, min_content_boost_log2,
927*89a0ef05SAndroid Build Coastguard Worker                                        max_content_boost_log2, &jobQueue]() -> void {
928*89a0ef05SAndroid Build Coastguard Worker       unsigned int rowStart, rowEnd;
929*89a0ef05SAndroid Build Coastguard Worker 
930*89a0ef05SAndroid Build Coastguard Worker       while (jobQueue.dequeueJob(rowStart, rowEnd)) {
931*89a0ef05SAndroid Build Coastguard Worker         if (mUseMultiChannelGainMap) {
932*89a0ef05SAndroid Build Coastguard Worker           for (size_t j = rowStart; j < rowEnd; j++) {
933*89a0ef05SAndroid Build Coastguard Worker             size_t dst_pixel_idx = j * dest->stride[UHDR_PLANE_PACKED] * 3;
934*89a0ef05SAndroid Build Coastguard Worker             size_t src_pixel_idx = j * map_width * 3;
935*89a0ef05SAndroid Build Coastguard Worker             for (size_t i = 0; i < map_width * 3; i++) {
936*89a0ef05SAndroid Build Coastguard Worker               reinterpret_cast<uint8_t*>(dest->planes[UHDR_PLANE_PACKED])[dst_pixel_idx + i] =
937*89a0ef05SAndroid Build Coastguard Worker                   affineMapGain(gainmap_data[src_pixel_idx + i], min_content_boost_log2,
938*89a0ef05SAndroid Build Coastguard Worker                                 max_content_boost_log2, this->mGamma);
939*89a0ef05SAndroid Build Coastguard Worker             }
940*89a0ef05SAndroid Build Coastguard Worker           }
941*89a0ef05SAndroid Build Coastguard Worker         } else {
942*89a0ef05SAndroid Build Coastguard Worker           for (size_t j = rowStart; j < rowEnd; j++) {
943*89a0ef05SAndroid Build Coastguard Worker             size_t dst_pixel_idx = j * dest->stride[UHDR_PLANE_Y];
944*89a0ef05SAndroid Build Coastguard Worker             size_t src_pixel_idx = j * map_width;
945*89a0ef05SAndroid Build Coastguard Worker             for (size_t i = 0; i < map_width; i++) {
946*89a0ef05SAndroid Build Coastguard Worker               reinterpret_cast<uint8_t*>(dest->planes[UHDR_PLANE_Y])[dst_pixel_idx + i] =
947*89a0ef05SAndroid Build Coastguard Worker                   affineMapGain(gainmap_data[src_pixel_idx + i], min_content_boost_log2,
948*89a0ef05SAndroid Build Coastguard Worker                                 max_content_boost_log2, this->mGamma);
949*89a0ef05SAndroid Build Coastguard Worker             }
950*89a0ef05SAndroid Build Coastguard Worker           }
951*89a0ef05SAndroid Build Coastguard Worker         }
952*89a0ef05SAndroid Build Coastguard Worker       }
953*89a0ef05SAndroid Build Coastguard Worker     };
954*89a0ef05SAndroid Build Coastguard Worker     workers.clear();
955*89a0ef05SAndroid Build Coastguard Worker     jobQueue.reset();
956*89a0ef05SAndroid Build Coastguard Worker     rowStep = threads == 1 ? map_height : 1;
957*89a0ef05SAndroid Build Coastguard Worker     for (int th = 0; th < threads - 1; th++) {
958*89a0ef05SAndroid Build Coastguard Worker       workers.push_back(std::thread(encodeMap));
959*89a0ef05SAndroid Build Coastguard Worker     }
960*89a0ef05SAndroid Build Coastguard Worker     for (unsigned int rowStart = 0; rowStart < map_height;) {
961*89a0ef05SAndroid Build Coastguard Worker       unsigned int rowEnd = (std::min)(rowStart + rowStep, map_height);
962*89a0ef05SAndroid Build Coastguard Worker       jobQueue.enqueueJob(rowStart, rowEnd);
963*89a0ef05SAndroid Build Coastguard Worker       rowStart = rowEnd;
964*89a0ef05SAndroid Build Coastguard Worker     }
965*89a0ef05SAndroid Build Coastguard Worker     jobQueue.markQueueForEnd();
966*89a0ef05SAndroid Build Coastguard Worker     encodeMap();
967*89a0ef05SAndroid Build Coastguard Worker     std::for_each(workers.begin(), workers.end(), [](std::thread& t) { t.join(); });
968*89a0ef05SAndroid Build Coastguard Worker 
969*89a0ef05SAndroid Build Coastguard Worker     gainmap_metadata->max_content_boost = exp2(max_content_boost_log2);
970*89a0ef05SAndroid Build Coastguard Worker     gainmap_metadata->min_content_boost = exp2(min_content_boost_log2);
971*89a0ef05SAndroid Build Coastguard Worker     gainmap_metadata->gamma = this->mGamma;
972*89a0ef05SAndroid Build Coastguard Worker     gainmap_metadata->offset_sdr = 0.0f;
973*89a0ef05SAndroid Build Coastguard Worker     gainmap_metadata->offset_hdr = 0.0f;
974*89a0ef05SAndroid Build Coastguard Worker     gainmap_metadata->hdr_capacity_min = 1.0f;
975*89a0ef05SAndroid Build Coastguard Worker     if (this->mTargetDispPeakBrightness != -1.0f) {
976*89a0ef05SAndroid Build Coastguard Worker       gainmap_metadata->hdr_capacity_max = this->mTargetDispPeakBrightness / kSdrWhiteNits;
977*89a0ef05SAndroid Build Coastguard Worker     } else {
978*89a0ef05SAndroid Build Coastguard Worker       gainmap_metadata->hdr_capacity_max = hdr_white_nits / kSdrWhiteNits;
979*89a0ef05SAndroid Build Coastguard Worker     }
980*89a0ef05SAndroid Build Coastguard Worker   };
981*89a0ef05SAndroid Build Coastguard Worker 
982*89a0ef05SAndroid Build Coastguard Worker   if (mEncPreset == UHDR_USAGE_REALTIME) {
983*89a0ef05SAndroid Build Coastguard Worker     generateGainMapOnePass();
984*89a0ef05SAndroid Build Coastguard Worker   } else {
985*89a0ef05SAndroid Build Coastguard Worker     generateGainMapTwoPass();
986*89a0ef05SAndroid Build Coastguard Worker   }
987*89a0ef05SAndroid Build Coastguard Worker 
988*89a0ef05SAndroid Build Coastguard Worker   return status;
989*89a0ef05SAndroid Build Coastguard Worker }
990*89a0ef05SAndroid Build Coastguard Worker 
991*89a0ef05SAndroid Build Coastguard Worker // JPEG/R structure:
992*89a0ef05SAndroid Build Coastguard Worker // SOI (ff d8)
993*89a0ef05SAndroid Build Coastguard Worker //
994*89a0ef05SAndroid Build Coastguard Worker // (Optional, if EXIF package is from outside (Encode API-0 API-1), or if EXIF package presents
995*89a0ef05SAndroid Build Coastguard Worker // in the JPEG input (Encode API-2, API-3, API-4))
996*89a0ef05SAndroid Build Coastguard Worker // APP1 (ff e1)
997*89a0ef05SAndroid Build Coastguard Worker // 2 bytes of length (2 + length of exif package)
998*89a0ef05SAndroid Build Coastguard Worker // EXIF package (this includes the first two bytes representing the package length)
999*89a0ef05SAndroid Build Coastguard Worker //
1000*89a0ef05SAndroid Build Coastguard Worker // (Required, XMP package) APP1 (ff e1)
1001*89a0ef05SAndroid Build Coastguard Worker // 2 bytes of length (2 + 29 + length of xmp package)
1002*89a0ef05SAndroid Build Coastguard Worker // name space ("http://ns.adobe.com/xap/1.0/\0")
1003*89a0ef05SAndroid Build Coastguard Worker // XMP
1004*89a0ef05SAndroid Build Coastguard Worker //
1005*89a0ef05SAndroid Build Coastguard Worker // (Required, ISO 21496-1 metadata, version only) APP2 (ff e2)
1006*89a0ef05SAndroid Build Coastguard Worker // 2 bytes of length
1007*89a0ef05SAndroid Build Coastguard Worker // name space (""urn:iso:std:iso:ts:21496:-1\0")
1008*89a0ef05SAndroid Build Coastguard Worker // 2 bytes minimum_version: (00 00)
1009*89a0ef05SAndroid Build Coastguard Worker // 2 bytes writer_version: (00 00)
1010*89a0ef05SAndroid Build Coastguard Worker //
1011*89a0ef05SAndroid Build Coastguard Worker // (Required, MPF package) APP2 (ff e2)
1012*89a0ef05SAndroid Build Coastguard Worker // 2 bytes of length
1013*89a0ef05SAndroid Build Coastguard Worker // MPF
1014*89a0ef05SAndroid Build Coastguard Worker //
1015*89a0ef05SAndroid Build Coastguard Worker // (Required) primary image (without the first two bytes (SOI) and EXIF, may have other packages)
1016*89a0ef05SAndroid Build Coastguard Worker //
1017*89a0ef05SAndroid Build Coastguard Worker // SOI (ff d8)
1018*89a0ef05SAndroid Build Coastguard Worker //
1019*89a0ef05SAndroid Build Coastguard Worker // (Required, XMP package) APP1 (ff e1)
1020*89a0ef05SAndroid Build Coastguard Worker // 2 bytes of length (2 + 29 + length of xmp package)
1021*89a0ef05SAndroid Build Coastguard Worker // name space ("http://ns.adobe.com/xap/1.0/\0")
1022*89a0ef05SAndroid Build Coastguard Worker // XMP
1023*89a0ef05SAndroid Build Coastguard Worker //
1024*89a0ef05SAndroid Build Coastguard Worker // (Required, ISO 21496-1 metadata) APP2 (ff e2)
1025*89a0ef05SAndroid Build Coastguard Worker // 2 bytes of length
1026*89a0ef05SAndroid Build Coastguard Worker // name space (""urn:iso:std:iso:ts:21496:-1\0")
1027*89a0ef05SAndroid Build Coastguard Worker // metadata
1028*89a0ef05SAndroid Build Coastguard Worker //
1029*89a0ef05SAndroid Build Coastguard Worker // (Required) secondary image (the gain map, without the first two bytes (SOI))
1030*89a0ef05SAndroid Build Coastguard Worker //
1031*89a0ef05SAndroid Build Coastguard Worker // Metadata versions we are using:
1032*89a0ef05SAndroid Build Coastguard Worker // ECMA TR-98 for JFIF marker
1033*89a0ef05SAndroid Build Coastguard Worker // Exif 2.2 spec for EXIF marker
1034*89a0ef05SAndroid Build Coastguard Worker // Adobe XMP spec part 3 for XMP marker
1035*89a0ef05SAndroid Build Coastguard Worker // ICC v4.3 spec for ICC
appendGainMap(uhdr_compressed_image_t * sdr_intent_compressed,uhdr_compressed_image_t * gainmap_compressed,uhdr_mem_block_t * pExif,void * pIcc,size_t icc_size,uhdr_gainmap_metadata_ext_t * metadata,uhdr_compressed_image_t * dest)1036*89a0ef05SAndroid Build Coastguard Worker uhdr_error_info_t JpegR::appendGainMap(uhdr_compressed_image_t* sdr_intent_compressed,
1037*89a0ef05SAndroid Build Coastguard Worker                                        uhdr_compressed_image_t* gainmap_compressed,
1038*89a0ef05SAndroid Build Coastguard Worker                                        uhdr_mem_block_t* pExif, void* pIcc, size_t icc_size,
1039*89a0ef05SAndroid Build Coastguard Worker                                        uhdr_gainmap_metadata_ext_t* metadata,
1040*89a0ef05SAndroid Build Coastguard Worker                                        uhdr_compressed_image_t* dest) {
1041*89a0ef05SAndroid Build Coastguard Worker   const size_t xmpNameSpaceLength = kXmpNameSpace.size() + 1;  // need to count the null terminator
1042*89a0ef05SAndroid Build Coastguard Worker   const size_t isoNameSpaceLength = kIsoNameSpace.size() + 1;  // need to count the null terminator
1043*89a0ef05SAndroid Build Coastguard Worker 
1044*89a0ef05SAndroid Build Coastguard Worker   /////////////////////////////////////////////////////////////////////////////////////////////////
1045*89a0ef05SAndroid Build Coastguard Worker   // calculate secondary image length first, because the length will be written into the primary //
1046*89a0ef05SAndroid Build Coastguard Worker   // image xmp                                                                                   //
1047*89a0ef05SAndroid Build Coastguard Worker   /////////////////////////////////////////////////////////////////////////////////////////////////
1048*89a0ef05SAndroid Build Coastguard Worker 
1049*89a0ef05SAndroid Build Coastguard Worker   // XMP
1050*89a0ef05SAndroid Build Coastguard Worker   string xmp_secondary;
1051*89a0ef05SAndroid Build Coastguard Worker   size_t xmp_secondary_length;
1052*89a0ef05SAndroid Build Coastguard Worker   if (kWriteXmpMetadata) {
1053*89a0ef05SAndroid Build Coastguard Worker     xmp_secondary = generateXmpForSecondaryImage(*metadata);
1054*89a0ef05SAndroid Build Coastguard Worker     // xmp_secondary_length = 2 bytes representing the length of the package +
1055*89a0ef05SAndroid Build Coastguard Worker     //  + xmpNameSpaceLength = 29 bytes length
1056*89a0ef05SAndroid Build Coastguard Worker     //  + length of xmp packet = xmp_secondary.size()
1057*89a0ef05SAndroid Build Coastguard Worker     xmp_secondary_length = 2 + xmpNameSpaceLength + xmp_secondary.size();
1058*89a0ef05SAndroid Build Coastguard Worker   }
1059*89a0ef05SAndroid Build Coastguard Worker 
1060*89a0ef05SAndroid Build Coastguard Worker   // ISO
1061*89a0ef05SAndroid Build Coastguard Worker   uhdr_gainmap_metadata_frac iso_secondary_metadata;
1062*89a0ef05SAndroid Build Coastguard Worker   std::vector<uint8_t> iso_secondary_data;
1063*89a0ef05SAndroid Build Coastguard Worker   size_t iso_secondary_length;
1064*89a0ef05SAndroid Build Coastguard Worker   if (kWriteIso21496_1Metadata) {
1065*89a0ef05SAndroid Build Coastguard Worker     UHDR_ERR_CHECK(uhdr_gainmap_metadata_frac::gainmapMetadataFloatToFraction(
1066*89a0ef05SAndroid Build Coastguard Worker         metadata, &iso_secondary_metadata));
1067*89a0ef05SAndroid Build Coastguard Worker 
1068*89a0ef05SAndroid Build Coastguard Worker     UHDR_ERR_CHECK(uhdr_gainmap_metadata_frac::encodeGainmapMetadata(&iso_secondary_metadata,
1069*89a0ef05SAndroid Build Coastguard Worker                                                                      iso_secondary_data));
1070*89a0ef05SAndroid Build Coastguard Worker     // iso_secondary_length = 2 bytes representing the length of the package +
1071*89a0ef05SAndroid Build Coastguard Worker     //  + isoNameSpaceLength = 28 bytes length
1072*89a0ef05SAndroid Build Coastguard Worker     //  + length of iso metadata packet = iso_secondary_data.size()
1073*89a0ef05SAndroid Build Coastguard Worker     iso_secondary_length = 2 + isoNameSpaceLength + iso_secondary_data.size();
1074*89a0ef05SAndroid Build Coastguard Worker   }
1075*89a0ef05SAndroid Build Coastguard Worker 
1076*89a0ef05SAndroid Build Coastguard Worker   size_t secondary_image_size = 2 /* 2 bytes length of APP1 sign */ + gainmap_compressed->data_sz;
1077*89a0ef05SAndroid Build Coastguard Worker   if (kWriteXmpMetadata) {
1078*89a0ef05SAndroid Build Coastguard Worker     secondary_image_size += xmp_secondary_length;
1079*89a0ef05SAndroid Build Coastguard Worker   }
1080*89a0ef05SAndroid Build Coastguard Worker   if (kWriteIso21496_1Metadata) {
1081*89a0ef05SAndroid Build Coastguard Worker     secondary_image_size += iso_secondary_length;
1082*89a0ef05SAndroid Build Coastguard Worker   }
1083*89a0ef05SAndroid Build Coastguard Worker 
1084*89a0ef05SAndroid Build Coastguard Worker   // Check if EXIF package presents in the JPEG input.
1085*89a0ef05SAndroid Build Coastguard Worker   // If so, extract and remove the EXIF package.
1086*89a0ef05SAndroid Build Coastguard Worker   JpegDecoderHelper decoder;
1087*89a0ef05SAndroid Build Coastguard Worker   UHDR_ERR_CHECK(decoder.parseImage(sdr_intent_compressed->data, sdr_intent_compressed->data_sz));
1088*89a0ef05SAndroid Build Coastguard Worker 
1089*89a0ef05SAndroid Build Coastguard Worker   uhdr_mem_block_t exif_from_jpg;
1090*89a0ef05SAndroid Build Coastguard Worker   exif_from_jpg.data = nullptr;
1091*89a0ef05SAndroid Build Coastguard Worker   exif_from_jpg.data_sz = 0;
1092*89a0ef05SAndroid Build Coastguard Worker 
1093*89a0ef05SAndroid Build Coastguard Worker   uhdr_compressed_image_t new_jpg_image;
1094*89a0ef05SAndroid Build Coastguard Worker   new_jpg_image.data = nullptr;
1095*89a0ef05SAndroid Build Coastguard Worker   new_jpg_image.data_sz = 0;
1096*89a0ef05SAndroid Build Coastguard Worker   new_jpg_image.capacity = 0;
1097*89a0ef05SAndroid Build Coastguard Worker   new_jpg_image.cg = UHDR_CG_UNSPECIFIED;
1098*89a0ef05SAndroid Build Coastguard Worker   new_jpg_image.ct = UHDR_CT_UNSPECIFIED;
1099*89a0ef05SAndroid Build Coastguard Worker   new_jpg_image.range = UHDR_CR_UNSPECIFIED;
1100*89a0ef05SAndroid Build Coastguard Worker 
1101*89a0ef05SAndroid Build Coastguard Worker   std::unique_ptr<uint8_t[]> dest_data;
1102*89a0ef05SAndroid Build Coastguard Worker   if (decoder.getEXIFPos() >= 0) {
1103*89a0ef05SAndroid Build Coastguard Worker     if (pExif != nullptr) {
1104*89a0ef05SAndroid Build Coastguard Worker       uhdr_error_info_t status;
1105*89a0ef05SAndroid Build Coastguard Worker       status.error_code = UHDR_CODEC_INVALID_PARAM;
1106*89a0ef05SAndroid Build Coastguard Worker       status.has_detail = 1;
1107*89a0ef05SAndroid Build Coastguard Worker       snprintf(status.detail, sizeof status.detail,
1108*89a0ef05SAndroid Build Coastguard Worker                "received exif from uhdr_enc_set_exif_data() while the base image intent already "
1109*89a0ef05SAndroid Build Coastguard Worker                "contains exif, unsure which one to use");
1110*89a0ef05SAndroid Build Coastguard Worker       return status;
1111*89a0ef05SAndroid Build Coastguard Worker     }
1112*89a0ef05SAndroid Build Coastguard Worker     copyJpegWithoutExif(&new_jpg_image, sdr_intent_compressed, decoder.getEXIFPos(),
1113*89a0ef05SAndroid Build Coastguard Worker                         decoder.getEXIFSize());
1114*89a0ef05SAndroid Build Coastguard Worker     dest_data.reset(reinterpret_cast<uint8_t*>(new_jpg_image.data));
1115*89a0ef05SAndroid Build Coastguard Worker     exif_from_jpg.data = decoder.getEXIFPtr();
1116*89a0ef05SAndroid Build Coastguard Worker     exif_from_jpg.data_sz = decoder.getEXIFSize();
1117*89a0ef05SAndroid Build Coastguard Worker     pExif = &exif_from_jpg;
1118*89a0ef05SAndroid Build Coastguard Worker   }
1119*89a0ef05SAndroid Build Coastguard Worker 
1120*89a0ef05SAndroid Build Coastguard Worker   uhdr_compressed_image_t* final_primary_jpg_image_ptr =
1121*89a0ef05SAndroid Build Coastguard Worker       new_jpg_image.data_sz == 0 ? sdr_intent_compressed : &new_jpg_image;
1122*89a0ef05SAndroid Build Coastguard Worker 
1123*89a0ef05SAndroid Build Coastguard Worker   size_t pos = 0;
1124*89a0ef05SAndroid Build Coastguard Worker   // Begin primary image
1125*89a0ef05SAndroid Build Coastguard Worker   // Write SOI
1126*89a0ef05SAndroid Build Coastguard Worker   UHDR_ERR_CHECK(Write(dest, &photos_editing_formats::image_io::JpegMarker::kStart, 1, pos));
1127*89a0ef05SAndroid Build Coastguard Worker   UHDR_ERR_CHECK(Write(dest, &photos_editing_formats::image_io::JpegMarker::kSOI, 1, pos));
1128*89a0ef05SAndroid Build Coastguard Worker 
1129*89a0ef05SAndroid Build Coastguard Worker   // Write EXIF
1130*89a0ef05SAndroid Build Coastguard Worker   if (pExif != nullptr) {
1131*89a0ef05SAndroid Build Coastguard Worker     const size_t length = 2 + pExif->data_sz;
1132*89a0ef05SAndroid Build Coastguard Worker     const uint8_t lengthH = ((length >> 8) & 0xff);
1133*89a0ef05SAndroid Build Coastguard Worker     const uint8_t lengthL = (length & 0xff);
1134*89a0ef05SAndroid Build Coastguard Worker     UHDR_ERR_CHECK(Write(dest, &photos_editing_formats::image_io::JpegMarker::kStart, 1, pos));
1135*89a0ef05SAndroid Build Coastguard Worker     UHDR_ERR_CHECK(Write(dest, &photos_editing_formats::image_io::JpegMarker::kAPP1, 1, pos));
1136*89a0ef05SAndroid Build Coastguard Worker     UHDR_ERR_CHECK(Write(dest, &lengthH, 1, pos));
1137*89a0ef05SAndroid Build Coastguard Worker     UHDR_ERR_CHECK(Write(dest, &lengthL, 1, pos));
1138*89a0ef05SAndroid Build Coastguard Worker     UHDR_ERR_CHECK(Write(dest, pExif->data, pExif->data_sz, pos));
1139*89a0ef05SAndroid Build Coastguard Worker   }
1140*89a0ef05SAndroid Build Coastguard Worker 
1141*89a0ef05SAndroid Build Coastguard Worker   // Prepare and write XMP
1142*89a0ef05SAndroid Build Coastguard Worker   if (kWriteXmpMetadata) {
1143*89a0ef05SAndroid Build Coastguard Worker     const string xmp_primary = generateXmpForPrimaryImage(secondary_image_size, *metadata);
1144*89a0ef05SAndroid Build Coastguard Worker     const size_t length = 2 + xmpNameSpaceLength + xmp_primary.size();
1145*89a0ef05SAndroid Build Coastguard Worker     const uint8_t lengthH = ((length >> 8) & 0xff);
1146*89a0ef05SAndroid Build Coastguard Worker     const uint8_t lengthL = (length & 0xff);
1147*89a0ef05SAndroid Build Coastguard Worker     UHDR_ERR_CHECK(Write(dest, &photos_editing_formats::image_io::JpegMarker::kStart, 1, pos));
1148*89a0ef05SAndroid Build Coastguard Worker     UHDR_ERR_CHECK(Write(dest, &photos_editing_formats::image_io::JpegMarker::kAPP1, 1, pos));
1149*89a0ef05SAndroid Build Coastguard Worker     UHDR_ERR_CHECK(Write(dest, &lengthH, 1, pos));
1150*89a0ef05SAndroid Build Coastguard Worker     UHDR_ERR_CHECK(Write(dest, &lengthL, 1, pos));
1151*89a0ef05SAndroid Build Coastguard Worker     UHDR_ERR_CHECK(Write(dest, (void*)kXmpNameSpace.c_str(), xmpNameSpaceLength, pos));
1152*89a0ef05SAndroid Build Coastguard Worker     UHDR_ERR_CHECK(Write(dest, (void*)xmp_primary.c_str(), xmp_primary.size(), pos));
1153*89a0ef05SAndroid Build Coastguard Worker   }
1154*89a0ef05SAndroid Build Coastguard Worker 
1155*89a0ef05SAndroid Build Coastguard Worker   // Write ICC
1156*89a0ef05SAndroid Build Coastguard Worker   if (pIcc != nullptr && icc_size > 0) {
1157*89a0ef05SAndroid Build Coastguard Worker     const size_t length = icc_size + 2;
1158*89a0ef05SAndroid Build Coastguard Worker     const uint8_t lengthH = ((length >> 8) & 0xff);
1159*89a0ef05SAndroid Build Coastguard Worker     const uint8_t lengthL = (length & 0xff);
1160*89a0ef05SAndroid Build Coastguard Worker     UHDR_ERR_CHECK(Write(dest, &photos_editing_formats::image_io::JpegMarker::kStart, 1, pos));
1161*89a0ef05SAndroid Build Coastguard Worker     UHDR_ERR_CHECK(Write(dest, &photos_editing_formats::image_io::JpegMarker::kAPP2, 1, pos));
1162*89a0ef05SAndroid Build Coastguard Worker     UHDR_ERR_CHECK(Write(dest, &lengthH, 1, pos));
1163*89a0ef05SAndroid Build Coastguard Worker     UHDR_ERR_CHECK(Write(dest, &lengthL, 1, pos));
1164*89a0ef05SAndroid Build Coastguard Worker     UHDR_ERR_CHECK(Write(dest, pIcc, icc_size, pos));
1165*89a0ef05SAndroid Build Coastguard Worker   }
1166*89a0ef05SAndroid Build Coastguard Worker 
1167*89a0ef05SAndroid Build Coastguard Worker   // Prepare and write ISO 21496-1 metadata
1168*89a0ef05SAndroid Build Coastguard Worker   if (kWriteIso21496_1Metadata) {
1169*89a0ef05SAndroid Build Coastguard Worker     const size_t length = 2 + isoNameSpaceLength + 4;
1170*89a0ef05SAndroid Build Coastguard Worker     uint8_t zero = 0;
1171*89a0ef05SAndroid Build Coastguard Worker     const uint8_t lengthH = ((length >> 8) & 0xff);
1172*89a0ef05SAndroid Build Coastguard Worker     const uint8_t lengthL = (length & 0xff);
1173*89a0ef05SAndroid Build Coastguard Worker     UHDR_ERR_CHECK(Write(dest, &photos_editing_formats::image_io::JpegMarker::kStart, 1, pos));
1174*89a0ef05SAndroid Build Coastguard Worker     UHDR_ERR_CHECK(Write(dest, &photos_editing_formats::image_io::JpegMarker::kAPP2, 1, pos));
1175*89a0ef05SAndroid Build Coastguard Worker     UHDR_ERR_CHECK(Write(dest, &lengthH, 1, pos));
1176*89a0ef05SAndroid Build Coastguard Worker     UHDR_ERR_CHECK(Write(dest, &lengthL, 1, pos));
1177*89a0ef05SAndroid Build Coastguard Worker     UHDR_ERR_CHECK(Write(dest, (void*)kIsoNameSpace.c_str(), isoNameSpaceLength, pos));
1178*89a0ef05SAndroid Build Coastguard Worker     UHDR_ERR_CHECK(Write(dest, &zero, 1, pos));
1179*89a0ef05SAndroid Build Coastguard Worker     UHDR_ERR_CHECK(Write(dest, &zero, 1, pos));  // 2 bytes minimum_version: (00 00)
1180*89a0ef05SAndroid Build Coastguard Worker     UHDR_ERR_CHECK(Write(dest, &zero, 1, pos));
1181*89a0ef05SAndroid Build Coastguard Worker     UHDR_ERR_CHECK(Write(dest, &zero, 1, pos));  // 2 bytes writer_version: (00 00)
1182*89a0ef05SAndroid Build Coastguard Worker   }
1183*89a0ef05SAndroid Build Coastguard Worker 
1184*89a0ef05SAndroid Build Coastguard Worker   // Prepare and write MPF
1185*89a0ef05SAndroid Build Coastguard Worker   {
1186*89a0ef05SAndroid Build Coastguard Worker     const size_t length = 2 + calculateMpfSize();
1187*89a0ef05SAndroid Build Coastguard Worker     const uint8_t lengthH = ((length >> 8) & 0xff);
1188*89a0ef05SAndroid Build Coastguard Worker     const uint8_t lengthL = (length & 0xff);
1189*89a0ef05SAndroid Build Coastguard Worker     size_t primary_image_size = pos + length + final_primary_jpg_image_ptr->data_sz;
1190*89a0ef05SAndroid Build Coastguard Worker     // between APP2 + package size + signature
1191*89a0ef05SAndroid Build Coastguard Worker     // ff e2 00 58 4d 50 46 00
1192*89a0ef05SAndroid Build Coastguard Worker     // 2 + 2 + 4 = 8 (bytes)
1193*89a0ef05SAndroid Build Coastguard Worker     // and ff d8 sign of the secondary image
1194*89a0ef05SAndroid Build Coastguard Worker     size_t secondary_image_offset = primary_image_size - pos - 8;
1195*89a0ef05SAndroid Build Coastguard Worker     std::shared_ptr<DataStruct> mpf = generateMpf(primary_image_size, 0, /* primary_image_offset */
1196*89a0ef05SAndroid Build Coastguard Worker                                                   secondary_image_size, secondary_image_offset);
1197*89a0ef05SAndroid Build Coastguard Worker     UHDR_ERR_CHECK(Write(dest, &photos_editing_formats::image_io::JpegMarker::kStart, 1, pos));
1198*89a0ef05SAndroid Build Coastguard Worker     UHDR_ERR_CHECK(Write(dest, &photos_editing_formats::image_io::JpegMarker::kAPP2, 1, pos));
1199*89a0ef05SAndroid Build Coastguard Worker     UHDR_ERR_CHECK(Write(dest, &lengthH, 1, pos));
1200*89a0ef05SAndroid Build Coastguard Worker     UHDR_ERR_CHECK(Write(dest, &lengthL, 1, pos));
1201*89a0ef05SAndroid Build Coastguard Worker     UHDR_ERR_CHECK(Write(dest, (void*)mpf->getData(), mpf->getLength(), pos));
1202*89a0ef05SAndroid Build Coastguard Worker   }
1203*89a0ef05SAndroid Build Coastguard Worker 
1204*89a0ef05SAndroid Build Coastguard Worker   // Write primary image
1205*89a0ef05SAndroid Build Coastguard Worker   UHDR_ERR_CHECK(Write(dest, (uint8_t*)final_primary_jpg_image_ptr->data + 2,
1206*89a0ef05SAndroid Build Coastguard Worker                        final_primary_jpg_image_ptr->data_sz - 2, pos));
1207*89a0ef05SAndroid Build Coastguard Worker   // Finish primary image
1208*89a0ef05SAndroid Build Coastguard Worker 
1209*89a0ef05SAndroid Build Coastguard Worker   // Begin secondary image (gain map)
1210*89a0ef05SAndroid Build Coastguard Worker   // Write SOI
1211*89a0ef05SAndroid Build Coastguard Worker   UHDR_ERR_CHECK(Write(dest, &photos_editing_formats::image_io::JpegMarker::kStart, 1, pos));
1212*89a0ef05SAndroid Build Coastguard Worker   UHDR_ERR_CHECK(Write(dest, &photos_editing_formats::image_io::JpegMarker::kSOI, 1, pos));
1213*89a0ef05SAndroid Build Coastguard Worker 
1214*89a0ef05SAndroid Build Coastguard Worker   // Prepare and write XMP
1215*89a0ef05SAndroid Build Coastguard Worker   if (kWriteXmpMetadata) {
1216*89a0ef05SAndroid Build Coastguard Worker     const size_t length = xmp_secondary_length;
1217*89a0ef05SAndroid Build Coastguard Worker     const uint8_t lengthH = ((length >> 8) & 0xff);
1218*89a0ef05SAndroid Build Coastguard Worker     const uint8_t lengthL = (length & 0xff);
1219*89a0ef05SAndroid Build Coastguard Worker     UHDR_ERR_CHECK(Write(dest, &photos_editing_formats::image_io::JpegMarker::kStart, 1, pos));
1220*89a0ef05SAndroid Build Coastguard Worker     UHDR_ERR_CHECK(Write(dest, &photos_editing_formats::image_io::JpegMarker::kAPP1, 1, pos));
1221*89a0ef05SAndroid Build Coastguard Worker     UHDR_ERR_CHECK(Write(dest, &lengthH, 1, pos));
1222*89a0ef05SAndroid Build Coastguard Worker     UHDR_ERR_CHECK(Write(dest, &lengthL, 1, pos));
1223*89a0ef05SAndroid Build Coastguard Worker     UHDR_ERR_CHECK(Write(dest, (void*)kXmpNameSpace.c_str(), xmpNameSpaceLength, pos));
1224*89a0ef05SAndroid Build Coastguard Worker     UHDR_ERR_CHECK(Write(dest, (void*)xmp_secondary.c_str(), xmp_secondary.size(), pos));
1225*89a0ef05SAndroid Build Coastguard Worker   }
1226*89a0ef05SAndroid Build Coastguard Worker 
1227*89a0ef05SAndroid Build Coastguard Worker   // Prepare and write ISO 21496-1 metadata
1228*89a0ef05SAndroid Build Coastguard Worker   if (kWriteIso21496_1Metadata) {
1229*89a0ef05SAndroid Build Coastguard Worker     const size_t length = iso_secondary_length;
1230*89a0ef05SAndroid Build Coastguard Worker     const uint8_t lengthH = ((length >> 8) & 0xff);
1231*89a0ef05SAndroid Build Coastguard Worker     const uint8_t lengthL = (length & 0xff);
1232*89a0ef05SAndroid Build Coastguard Worker     UHDR_ERR_CHECK(Write(dest, &photos_editing_formats::image_io::JpegMarker::kStart, 1, pos));
1233*89a0ef05SAndroid Build Coastguard Worker     UHDR_ERR_CHECK(Write(dest, &photos_editing_formats::image_io::JpegMarker::kAPP2, 1, pos));
1234*89a0ef05SAndroid Build Coastguard Worker     UHDR_ERR_CHECK(Write(dest, &lengthH, 1, pos));
1235*89a0ef05SAndroid Build Coastguard Worker     UHDR_ERR_CHECK(Write(dest, &lengthL, 1, pos));
1236*89a0ef05SAndroid Build Coastguard Worker     UHDR_ERR_CHECK(Write(dest, (void*)kIsoNameSpace.c_str(), isoNameSpaceLength, pos));
1237*89a0ef05SAndroid Build Coastguard Worker     UHDR_ERR_CHECK(Write(dest, (void*)iso_secondary_data.data(), iso_secondary_data.size(), pos));
1238*89a0ef05SAndroid Build Coastguard Worker   }
1239*89a0ef05SAndroid Build Coastguard Worker 
1240*89a0ef05SAndroid Build Coastguard Worker   // Write secondary image
1241*89a0ef05SAndroid Build Coastguard Worker   UHDR_ERR_CHECK(
1242*89a0ef05SAndroid Build Coastguard Worker       Write(dest, (uint8_t*)gainmap_compressed->data + 2, gainmap_compressed->data_sz - 2, pos));
1243*89a0ef05SAndroid Build Coastguard Worker 
1244*89a0ef05SAndroid Build Coastguard Worker   // Set back length
1245*89a0ef05SAndroid Build Coastguard Worker   dest->data_sz = pos;
1246*89a0ef05SAndroid Build Coastguard Worker 
1247*89a0ef05SAndroid Build Coastguard Worker   // Done!
1248*89a0ef05SAndroid Build Coastguard Worker   return g_no_error;
1249*89a0ef05SAndroid Build Coastguard Worker }
1250*89a0ef05SAndroid Build Coastguard Worker 
getJPEGRInfo(uhdr_compressed_image_t * uhdr_compressed_img,jr_info_ptr uhdr_image_info)1251*89a0ef05SAndroid Build Coastguard Worker uhdr_error_info_t JpegR::getJPEGRInfo(uhdr_compressed_image_t* uhdr_compressed_img,
1252*89a0ef05SAndroid Build Coastguard Worker                                       jr_info_ptr uhdr_image_info) {
1253*89a0ef05SAndroid Build Coastguard Worker   uhdr_compressed_image_t primary_image, gainmap;
1254*89a0ef05SAndroid Build Coastguard Worker 
1255*89a0ef05SAndroid Build Coastguard Worker   UHDR_ERR_CHECK(extractPrimaryImageAndGainMap(uhdr_compressed_img, &primary_image, &gainmap))
1256*89a0ef05SAndroid Build Coastguard Worker 
1257*89a0ef05SAndroid Build Coastguard Worker   UHDR_ERR_CHECK(parseJpegInfo(&primary_image, uhdr_image_info->primaryImgInfo,
1258*89a0ef05SAndroid Build Coastguard Worker                                &uhdr_image_info->width, &uhdr_image_info->height))
1259*89a0ef05SAndroid Build Coastguard Worker   if (uhdr_image_info->gainmapImgInfo != nullptr) {
1260*89a0ef05SAndroid Build Coastguard Worker     UHDR_ERR_CHECK(parseJpegInfo(&gainmap, uhdr_image_info->gainmapImgInfo))
1261*89a0ef05SAndroid Build Coastguard Worker   }
1262*89a0ef05SAndroid Build Coastguard Worker 
1263*89a0ef05SAndroid Build Coastguard Worker   return g_no_error;
1264*89a0ef05SAndroid Build Coastguard Worker }
1265*89a0ef05SAndroid Build Coastguard Worker 
parseGainMapMetadata(uint8_t * iso_data,size_t iso_size,uint8_t * xmp_data,size_t xmp_size,uhdr_gainmap_metadata_ext_t * uhdr_metadata)1266*89a0ef05SAndroid Build Coastguard Worker uhdr_error_info_t JpegR::parseGainMapMetadata(uint8_t* iso_data, size_t iso_size, uint8_t* xmp_data,
1267*89a0ef05SAndroid Build Coastguard Worker                                               size_t xmp_size,
1268*89a0ef05SAndroid Build Coastguard Worker                                               uhdr_gainmap_metadata_ext_t* uhdr_metadata) {
1269*89a0ef05SAndroid Build Coastguard Worker   if (iso_size > 0) {
1270*89a0ef05SAndroid Build Coastguard Worker     if (iso_size < kIsoNameSpace.size() + 1) {
1271*89a0ef05SAndroid Build Coastguard Worker       uhdr_error_info_t status;
1272*89a0ef05SAndroid Build Coastguard Worker       status.error_code = UHDR_CODEC_ERROR;
1273*89a0ef05SAndroid Build Coastguard Worker       status.has_detail = 1;
1274*89a0ef05SAndroid Build Coastguard Worker       snprintf(status.detail, sizeof status.detail,
1275*89a0ef05SAndroid Build Coastguard Worker                "iso block size needs to be atleast %zd but got %zd", kIsoNameSpace.size() + 1,
1276*89a0ef05SAndroid Build Coastguard Worker                iso_size);
1277*89a0ef05SAndroid Build Coastguard Worker       return status;
1278*89a0ef05SAndroid Build Coastguard Worker     }
1279*89a0ef05SAndroid Build Coastguard Worker     uhdr_gainmap_metadata_frac decodedMetadata;
1280*89a0ef05SAndroid Build Coastguard Worker     std::vector<uint8_t> iso_vec;
1281*89a0ef05SAndroid Build Coastguard Worker     for (size_t i = kIsoNameSpace.size() + 1; i < iso_size; i++) {
1282*89a0ef05SAndroid Build Coastguard Worker       iso_vec.push_back(iso_data[i]);
1283*89a0ef05SAndroid Build Coastguard Worker     }
1284*89a0ef05SAndroid Build Coastguard Worker 
1285*89a0ef05SAndroid Build Coastguard Worker     UHDR_ERR_CHECK(uhdr_gainmap_metadata_frac::decodeGainmapMetadata(iso_vec, &decodedMetadata));
1286*89a0ef05SAndroid Build Coastguard Worker     UHDR_ERR_CHECK(uhdr_gainmap_metadata_frac::gainmapMetadataFractionToFloat(&decodedMetadata,
1287*89a0ef05SAndroid Build Coastguard Worker                                                                               uhdr_metadata));
1288*89a0ef05SAndroid Build Coastguard Worker   } else if (xmp_size > 0) {
1289*89a0ef05SAndroid Build Coastguard Worker     UHDR_ERR_CHECK(getMetadataFromXMP(xmp_data, xmp_size, uhdr_metadata));
1290*89a0ef05SAndroid Build Coastguard Worker   } else {
1291*89a0ef05SAndroid Build Coastguard Worker     uhdr_error_info_t status;
1292*89a0ef05SAndroid Build Coastguard Worker     status.error_code = UHDR_CODEC_INVALID_PARAM;
1293*89a0ef05SAndroid Build Coastguard Worker     status.has_detail = 1;
1294*89a0ef05SAndroid Build Coastguard Worker     snprintf(status.detail, sizeof status.detail,
1295*89a0ef05SAndroid Build Coastguard Worker              "received no valid buffer to parse gainmap metadata");
1296*89a0ef05SAndroid Build Coastguard Worker     return status;
1297*89a0ef05SAndroid Build Coastguard Worker   }
1298*89a0ef05SAndroid Build Coastguard Worker 
1299*89a0ef05SAndroid Build Coastguard Worker   return g_no_error;
1300*89a0ef05SAndroid Build Coastguard Worker }
1301*89a0ef05SAndroid Build Coastguard Worker 
1302*89a0ef05SAndroid Build Coastguard Worker /* Decode API */
decodeJPEGR(uhdr_compressed_image_t * uhdr_compressed_img,uhdr_raw_image_t * dest,float max_display_boost,uhdr_color_transfer_t output_ct,uhdr_img_fmt_t output_format,uhdr_raw_image_t * gainmap_img,uhdr_gainmap_metadata_t * gainmap_metadata)1303*89a0ef05SAndroid Build Coastguard Worker uhdr_error_info_t JpegR::decodeJPEGR(uhdr_compressed_image_t* uhdr_compressed_img,
1304*89a0ef05SAndroid Build Coastguard Worker                                      uhdr_raw_image_t* dest, float max_display_boost,
1305*89a0ef05SAndroid Build Coastguard Worker                                      uhdr_color_transfer_t output_ct, uhdr_img_fmt_t output_format,
1306*89a0ef05SAndroid Build Coastguard Worker                                      uhdr_raw_image_t* gainmap_img,
1307*89a0ef05SAndroid Build Coastguard Worker                                      uhdr_gainmap_metadata_t* gainmap_metadata) {
1308*89a0ef05SAndroid Build Coastguard Worker   uhdr_compressed_image_t primary_jpeg_image, gainmap_jpeg_image;
1309*89a0ef05SAndroid Build Coastguard Worker   UHDR_ERR_CHECK(
1310*89a0ef05SAndroid Build Coastguard Worker       extractPrimaryImageAndGainMap(uhdr_compressed_img, &primary_jpeg_image, &gainmap_jpeg_image))
1311*89a0ef05SAndroid Build Coastguard Worker 
1312*89a0ef05SAndroid Build Coastguard Worker   JpegDecoderHelper jpeg_dec_obj_sdr;
1313*89a0ef05SAndroid Build Coastguard Worker   UHDR_ERR_CHECK(jpeg_dec_obj_sdr.decompressImage(
1314*89a0ef05SAndroid Build Coastguard Worker       primary_jpeg_image.data, primary_jpeg_image.data_sz,
1315*89a0ef05SAndroid Build Coastguard Worker       (output_ct == UHDR_CT_SRGB) ? DECODE_TO_RGB_CS : DECODE_TO_YCBCR_CS));
1316*89a0ef05SAndroid Build Coastguard Worker 
1317*89a0ef05SAndroid Build Coastguard Worker   JpegDecoderHelper jpeg_dec_obj_gm;
1318*89a0ef05SAndroid Build Coastguard Worker   uhdr_raw_image_t gainmap;
1319*89a0ef05SAndroid Build Coastguard Worker   if (gainmap_img != nullptr || output_ct != UHDR_CT_SRGB) {
1320*89a0ef05SAndroid Build Coastguard Worker     UHDR_ERR_CHECK(jpeg_dec_obj_gm.decompressImage(gainmap_jpeg_image.data,
1321*89a0ef05SAndroid Build Coastguard Worker                                                    gainmap_jpeg_image.data_sz, DECODE_STREAM));
1322*89a0ef05SAndroid Build Coastguard Worker     gainmap = jpeg_dec_obj_gm.getDecompressedImage();
1323*89a0ef05SAndroid Build Coastguard Worker     if (gainmap_img != nullptr) {
1324*89a0ef05SAndroid Build Coastguard Worker       UHDR_ERR_CHECK(copy_raw_image(&gainmap, gainmap_img));
1325*89a0ef05SAndroid Build Coastguard Worker     }
1326*89a0ef05SAndroid Build Coastguard Worker   }
1327*89a0ef05SAndroid Build Coastguard Worker 
1328*89a0ef05SAndroid Build Coastguard Worker   uhdr_gainmap_metadata_ext_t uhdr_metadata;
1329*89a0ef05SAndroid Build Coastguard Worker   if (gainmap_metadata != nullptr || output_ct != UHDR_CT_SRGB) {
1330*89a0ef05SAndroid Build Coastguard Worker     UHDR_ERR_CHECK(parseGainMapMetadata(static_cast<uint8_t*>(jpeg_dec_obj_gm.getIsoMetadataPtr()),
1331*89a0ef05SAndroid Build Coastguard Worker                                         jpeg_dec_obj_gm.getIsoMetadataSize(),
1332*89a0ef05SAndroid Build Coastguard Worker                                         static_cast<uint8_t*>(jpeg_dec_obj_gm.getXMPPtr()),
1333*89a0ef05SAndroid Build Coastguard Worker                                         jpeg_dec_obj_gm.getXMPSize(), &uhdr_metadata))
1334*89a0ef05SAndroid Build Coastguard Worker     if (gainmap_metadata != nullptr) {
1335*89a0ef05SAndroid Build Coastguard Worker       gainmap_metadata->min_content_boost = uhdr_metadata.min_content_boost;
1336*89a0ef05SAndroid Build Coastguard Worker       gainmap_metadata->max_content_boost = uhdr_metadata.max_content_boost;
1337*89a0ef05SAndroid Build Coastguard Worker       gainmap_metadata->gamma = uhdr_metadata.gamma;
1338*89a0ef05SAndroid Build Coastguard Worker       gainmap_metadata->offset_sdr = uhdr_metadata.offset_sdr;
1339*89a0ef05SAndroid Build Coastguard Worker       gainmap_metadata->offset_hdr = uhdr_metadata.offset_hdr;
1340*89a0ef05SAndroid Build Coastguard Worker       gainmap_metadata->hdr_capacity_min = uhdr_metadata.hdr_capacity_min;
1341*89a0ef05SAndroid Build Coastguard Worker       gainmap_metadata->hdr_capacity_max = uhdr_metadata.hdr_capacity_max;
1342*89a0ef05SAndroid Build Coastguard Worker     }
1343*89a0ef05SAndroid Build Coastguard Worker   }
1344*89a0ef05SAndroid Build Coastguard Worker 
1345*89a0ef05SAndroid Build Coastguard Worker   uhdr_raw_image_t sdr_intent = jpeg_dec_obj_sdr.getDecompressedImage();
1346*89a0ef05SAndroid Build Coastguard Worker   sdr_intent.cg =
1347*89a0ef05SAndroid Build Coastguard Worker       IccHelper::readIccColorGamut(jpeg_dec_obj_sdr.getICCPtr(), jpeg_dec_obj_sdr.getICCSize());
1348*89a0ef05SAndroid Build Coastguard Worker   if (output_ct == UHDR_CT_SRGB) {
1349*89a0ef05SAndroid Build Coastguard Worker     UHDR_ERR_CHECK(copy_raw_image(&sdr_intent, dest));
1350*89a0ef05SAndroid Build Coastguard Worker     return g_no_error;
1351*89a0ef05SAndroid Build Coastguard Worker   }
1352*89a0ef05SAndroid Build Coastguard Worker 
1353*89a0ef05SAndroid Build Coastguard Worker   UHDR_ERR_CHECK(applyGainMap(&sdr_intent, &gainmap, &uhdr_metadata, output_ct, output_format,
1354*89a0ef05SAndroid Build Coastguard Worker                               max_display_boost, dest));
1355*89a0ef05SAndroid Build Coastguard Worker 
1356*89a0ef05SAndroid Build Coastguard Worker   return g_no_error;
1357*89a0ef05SAndroid Build Coastguard Worker }
1358*89a0ef05SAndroid Build Coastguard Worker 
applyGainMap(uhdr_raw_image_t * sdr_intent,uhdr_raw_image_t * gainmap_img,uhdr_gainmap_metadata_ext_t * gainmap_metadata,uhdr_color_transfer_t output_ct,uhdr_img_fmt_t output_format,float max_display_boost,uhdr_raw_image_t * dest)1359*89a0ef05SAndroid Build Coastguard Worker uhdr_error_info_t JpegR::applyGainMap(uhdr_raw_image_t* sdr_intent, uhdr_raw_image_t* gainmap_img,
1360*89a0ef05SAndroid Build Coastguard Worker                                       uhdr_gainmap_metadata_ext_t* gainmap_metadata,
1361*89a0ef05SAndroid Build Coastguard Worker                                       uhdr_color_transfer_t output_ct,
1362*89a0ef05SAndroid Build Coastguard Worker                                       [[maybe_unused]] uhdr_img_fmt_t output_format,
1363*89a0ef05SAndroid Build Coastguard Worker                                       float max_display_boost, uhdr_raw_image_t* dest) {
1364*89a0ef05SAndroid Build Coastguard Worker   if (gainmap_metadata->version.compare(kJpegrVersion)) {
1365*89a0ef05SAndroid Build Coastguard Worker     uhdr_error_info_t status;
1366*89a0ef05SAndroid Build Coastguard Worker     status.error_code = UHDR_CODEC_UNSUPPORTED_FEATURE;
1367*89a0ef05SAndroid Build Coastguard Worker     status.has_detail = 1;
1368*89a0ef05SAndroid Build Coastguard Worker     snprintf(status.detail, sizeof status.detail,
1369*89a0ef05SAndroid Build Coastguard Worker              "Unsupported gainmap metadata, version. Expected %s, Got %s", kJpegrVersion,
1370*89a0ef05SAndroid Build Coastguard Worker              gainmap_metadata->version.c_str());
1371*89a0ef05SAndroid Build Coastguard Worker     return status;
1372*89a0ef05SAndroid Build Coastguard Worker   }
1373*89a0ef05SAndroid Build Coastguard Worker   UHDR_ERR_CHECK(uhdr_validate_gainmap_metadata_descriptor(gainmap_metadata));
1374*89a0ef05SAndroid Build Coastguard Worker   if (sdr_intent->fmt != UHDR_IMG_FMT_24bppYCbCr444 &&
1375*89a0ef05SAndroid Build Coastguard Worker       sdr_intent->fmt != UHDR_IMG_FMT_16bppYCbCr422 &&
1376*89a0ef05SAndroid Build Coastguard Worker       sdr_intent->fmt != UHDR_IMG_FMT_12bppYCbCr420) {
1377*89a0ef05SAndroid Build Coastguard Worker     uhdr_error_info_t status;
1378*89a0ef05SAndroid Build Coastguard Worker     status.error_code = UHDR_CODEC_UNSUPPORTED_FEATURE;
1379*89a0ef05SAndroid Build Coastguard Worker     status.has_detail = 1;
1380*89a0ef05SAndroid Build Coastguard Worker     snprintf(status.detail, sizeof status.detail,
1381*89a0ef05SAndroid Build Coastguard Worker              "apply gainmap method expects base image color format to be one of "
1382*89a0ef05SAndroid Build Coastguard Worker              "{UHDR_IMG_FMT_24bppYCbCr444, UHDR_IMG_FMT_16bppYCbCr422, "
1383*89a0ef05SAndroid Build Coastguard Worker              "UHDR_IMG_FMT_12bppYCbCr420}. Received %d",
1384*89a0ef05SAndroid Build Coastguard Worker              sdr_intent->fmt);
1385*89a0ef05SAndroid Build Coastguard Worker     return status;
1386*89a0ef05SAndroid Build Coastguard Worker   }
1387*89a0ef05SAndroid Build Coastguard Worker   if (gainmap_img->fmt != UHDR_IMG_FMT_8bppYCbCr400 &&
1388*89a0ef05SAndroid Build Coastguard Worker       gainmap_img->fmt != UHDR_IMG_FMT_24bppRGB888 &&
1389*89a0ef05SAndroid Build Coastguard Worker       gainmap_img->fmt != UHDR_IMG_FMT_32bppRGBA8888) {
1390*89a0ef05SAndroid Build Coastguard Worker     uhdr_error_info_t status;
1391*89a0ef05SAndroid Build Coastguard Worker     status.error_code = UHDR_CODEC_UNSUPPORTED_FEATURE;
1392*89a0ef05SAndroid Build Coastguard Worker     status.has_detail = 1;
1393*89a0ef05SAndroid Build Coastguard Worker     snprintf(status.detail, sizeof status.detail,
1394*89a0ef05SAndroid Build Coastguard Worker              "apply gainmap method expects gainmap image color format to be one of "
1395*89a0ef05SAndroid Build Coastguard Worker              "{UHDR_IMG_FMT_8bppYCbCr400, UHDR_IMG_FMT_24bppRGB888, UHDR_IMG_FMT_32bppRGBA8888}. "
1396*89a0ef05SAndroid Build Coastguard Worker              "Received %d",
1397*89a0ef05SAndroid Build Coastguard Worker              gainmap_img->fmt);
1398*89a0ef05SAndroid Build Coastguard Worker     return status;
1399*89a0ef05SAndroid Build Coastguard Worker   }
1400*89a0ef05SAndroid Build Coastguard Worker 
1401*89a0ef05SAndroid Build Coastguard Worker #ifdef UHDR_ENABLE_GLES
1402*89a0ef05SAndroid Build Coastguard Worker   if (mUhdrGLESCtxt != nullptr) {
1403*89a0ef05SAndroid Build Coastguard Worker     if (((sdr_intent->fmt == UHDR_IMG_FMT_12bppYCbCr420 && sdr_intent->w % 2 == 0 &&
1404*89a0ef05SAndroid Build Coastguard Worker           sdr_intent->h % 2 == 0) ||
1405*89a0ef05SAndroid Build Coastguard Worker          (sdr_intent->fmt == UHDR_IMG_FMT_16bppYCbCr422 && sdr_intent->w % 2 == 0) ||
1406*89a0ef05SAndroid Build Coastguard Worker          (sdr_intent->fmt == UHDR_IMG_FMT_24bppYCbCr444)) &&
1407*89a0ef05SAndroid Build Coastguard Worker         isBufferDataContiguous(sdr_intent) && isBufferDataContiguous(gainmap_img) &&
1408*89a0ef05SAndroid Build Coastguard Worker         isBufferDataContiguous(dest)) {
1409*89a0ef05SAndroid Build Coastguard Worker       // TODO: both inputs and outputs of GLES implementation assumes that raw image is contiguous
1410*89a0ef05SAndroid Build Coastguard Worker       // and without strides. If not, handle the same by using temp copy
1411*89a0ef05SAndroid Build Coastguard Worker       float display_boost = (std::min)(max_display_boost, gainmap_metadata->hdr_capacity_max);
1412*89a0ef05SAndroid Build Coastguard Worker 
1413*89a0ef05SAndroid Build Coastguard Worker       return applyGainMapGLES(sdr_intent, gainmap_img, gainmap_metadata, output_ct, display_boost,
1414*89a0ef05SAndroid Build Coastguard Worker                               dest, static_cast<uhdr_opengl_ctxt_t*>(mUhdrGLESCtxt));
1415*89a0ef05SAndroid Build Coastguard Worker     }
1416*89a0ef05SAndroid Build Coastguard Worker   }
1417*89a0ef05SAndroid Build Coastguard Worker #endif
1418*89a0ef05SAndroid Build Coastguard Worker 
1419*89a0ef05SAndroid Build Coastguard Worker   std::unique_ptr<uhdr_raw_image_ext_t> resized_gainmap = nullptr;
1420*89a0ef05SAndroid Build Coastguard Worker   {
1421*89a0ef05SAndroid Build Coastguard Worker     float primary_aspect_ratio = (float)sdr_intent->w / sdr_intent->h;
1422*89a0ef05SAndroid Build Coastguard Worker     float gainmap_aspect_ratio = (float)gainmap_img->w / gainmap_img->h;
1423*89a0ef05SAndroid Build Coastguard Worker     float delta_aspect_ratio = fabs(primary_aspect_ratio - gainmap_aspect_ratio);
1424*89a0ef05SAndroid Build Coastguard Worker     // Allow 1% delta
1425*89a0ef05SAndroid Build Coastguard Worker     const float delta_tolerance = 0.01;
1426*89a0ef05SAndroid Build Coastguard Worker     if (delta_aspect_ratio / primary_aspect_ratio > delta_tolerance) {
1427*89a0ef05SAndroid Build Coastguard Worker       resized_gainmap = resize_image(gainmap_img, sdr_intent->w, sdr_intent->h);
1428*89a0ef05SAndroid Build Coastguard Worker       if (resized_gainmap == nullptr) {
1429*89a0ef05SAndroid Build Coastguard Worker         uhdr_error_info_t status;
1430*89a0ef05SAndroid Build Coastguard Worker         status.error_code = UHDR_CODEC_UNSUPPORTED_FEATURE;
1431*89a0ef05SAndroid Build Coastguard Worker         status.has_detail = 1;
1432*89a0ef05SAndroid Build Coastguard Worker         snprintf(status.detail, sizeof status.detail,
1433*89a0ef05SAndroid Build Coastguard Worker                  "encountered error while resizing the gainmap image from %ux%u to %ux%u",
1434*89a0ef05SAndroid Build Coastguard Worker                  gainmap_img->w, gainmap_img->h, sdr_intent->w, sdr_intent->h);
1435*89a0ef05SAndroid Build Coastguard Worker         return status;
1436*89a0ef05SAndroid Build Coastguard Worker       }
1437*89a0ef05SAndroid Build Coastguard Worker       gainmap_img = resized_gainmap.get();
1438*89a0ef05SAndroid Build Coastguard Worker     }
1439*89a0ef05SAndroid Build Coastguard Worker   }
1440*89a0ef05SAndroid Build Coastguard Worker 
1441*89a0ef05SAndroid Build Coastguard Worker   float map_scale_factor = (float)sdr_intent->w / gainmap_img->w;
1442*89a0ef05SAndroid Build Coastguard Worker   int map_scale_factor_rnd = (std::max)(1, (int)std::roundf(map_scale_factor));
1443*89a0ef05SAndroid Build Coastguard Worker 
1444*89a0ef05SAndroid Build Coastguard Worker   dest->cg = sdr_intent->cg;
1445*89a0ef05SAndroid Build Coastguard Worker   // Table will only be used when map scale factor is integer.
1446*89a0ef05SAndroid Build Coastguard Worker   ShepardsIDW idwTable(map_scale_factor_rnd);
1447*89a0ef05SAndroid Build Coastguard Worker   float display_boost = (std::min)(max_display_boost, gainmap_metadata->hdr_capacity_max);
1448*89a0ef05SAndroid Build Coastguard Worker 
1449*89a0ef05SAndroid Build Coastguard Worker   float gainmap_weight;
1450*89a0ef05SAndroid Build Coastguard Worker   if (display_boost != gainmap_metadata->hdr_capacity_max) {
1451*89a0ef05SAndroid Build Coastguard Worker     gainmap_weight =
1452*89a0ef05SAndroid Build Coastguard Worker         (log2(display_boost) - log2(gainmap_metadata->hdr_capacity_min)) /
1453*89a0ef05SAndroid Build Coastguard Worker         (log2(gainmap_metadata->hdr_capacity_max) - log2(gainmap_metadata->hdr_capacity_min));
1454*89a0ef05SAndroid Build Coastguard Worker     // avoid extrapolating the gain map to fill the displayable range
1455*89a0ef05SAndroid Build Coastguard Worker     gainmap_weight = CLIP3(0.0f, gainmap_weight, 1.0f);
1456*89a0ef05SAndroid Build Coastguard Worker   } else {
1457*89a0ef05SAndroid Build Coastguard Worker     gainmap_weight = 1.0f;
1458*89a0ef05SAndroid Build Coastguard Worker   }
1459*89a0ef05SAndroid Build Coastguard Worker   GainLUT gainLUT(gainmap_metadata, gainmap_weight);
1460*89a0ef05SAndroid Build Coastguard Worker 
1461*89a0ef05SAndroid Build Coastguard Worker   GetPixelFn get_pixel_fn = getPixelFn(sdr_intent->fmt);
1462*89a0ef05SAndroid Build Coastguard Worker   if (get_pixel_fn == nullptr) {
1463*89a0ef05SAndroid Build Coastguard Worker     uhdr_error_info_t status;
1464*89a0ef05SAndroid Build Coastguard Worker     status.error_code = UHDR_CODEC_UNSUPPORTED_FEATURE;
1465*89a0ef05SAndroid Build Coastguard Worker     status.has_detail = 1;
1466*89a0ef05SAndroid Build Coastguard Worker     snprintf(status.detail, sizeof status.detail,
1467*89a0ef05SAndroid Build Coastguard Worker              "No implementation available for reading pixels for color format %d", sdr_intent->fmt);
1468*89a0ef05SAndroid Build Coastguard Worker     return status;
1469*89a0ef05SAndroid Build Coastguard Worker   }
1470*89a0ef05SAndroid Build Coastguard Worker 
1471*89a0ef05SAndroid Build Coastguard Worker   JobQueue jobQueue;
1472*89a0ef05SAndroid Build Coastguard Worker   std::function<void()> applyRecMap = [sdr_intent, gainmap_img, dest, &jobQueue, &idwTable,
1473*89a0ef05SAndroid Build Coastguard Worker                                        output_ct, &gainLUT, gainmap_metadata,
1474*89a0ef05SAndroid Build Coastguard Worker #if !USE_APPLY_GAIN_LUT
1475*89a0ef05SAndroid Build Coastguard Worker                                        gainmap_weight,
1476*89a0ef05SAndroid Build Coastguard Worker #endif
1477*89a0ef05SAndroid Build Coastguard Worker                                        map_scale_factor, get_pixel_fn]() -> void {
1478*89a0ef05SAndroid Build Coastguard Worker     unsigned int width = sdr_intent->w;
1479*89a0ef05SAndroid Build Coastguard Worker     unsigned int rowStart, rowEnd;
1480*89a0ef05SAndroid Build Coastguard Worker 
1481*89a0ef05SAndroid Build Coastguard Worker     while (jobQueue.dequeueJob(rowStart, rowEnd)) {
1482*89a0ef05SAndroid Build Coastguard Worker       for (size_t y = rowStart; y < rowEnd; ++y) {
1483*89a0ef05SAndroid Build Coastguard Worker         for (size_t x = 0; x < width; ++x) {
1484*89a0ef05SAndroid Build Coastguard Worker           Color yuv_gamma_sdr = get_pixel_fn(sdr_intent, x, y);
1485*89a0ef05SAndroid Build Coastguard Worker           // Assuming the sdr image is a decoded JPEG, we should always use Rec.601 YUV coefficients
1486*89a0ef05SAndroid Build Coastguard Worker           Color rgb_gamma_sdr = p3YuvToRgb(yuv_gamma_sdr);
1487*89a0ef05SAndroid Build Coastguard Worker           // We are assuming the SDR base image is always sRGB transfer.
1488*89a0ef05SAndroid Build Coastguard Worker #if USE_SRGB_INVOETF_LUT
1489*89a0ef05SAndroid Build Coastguard Worker           Color rgb_sdr = srgbInvOetfLUT(rgb_gamma_sdr);
1490*89a0ef05SAndroid Build Coastguard Worker #else
1491*89a0ef05SAndroid Build Coastguard Worker           Color rgb_sdr = srgbInvOetf(rgb_gamma_sdr);
1492*89a0ef05SAndroid Build Coastguard Worker #endif
1493*89a0ef05SAndroid Build Coastguard Worker           Color rgb_hdr;
1494*89a0ef05SAndroid Build Coastguard Worker           if (gainmap_img->fmt == UHDR_IMG_FMT_8bppYCbCr400) {
1495*89a0ef05SAndroid Build Coastguard Worker             float gain;
1496*89a0ef05SAndroid Build Coastguard Worker 
1497*89a0ef05SAndroid Build Coastguard Worker             if (map_scale_factor != floorf(map_scale_factor)) {
1498*89a0ef05SAndroid Build Coastguard Worker               gain = sampleMap(gainmap_img, map_scale_factor, x, y);
1499*89a0ef05SAndroid Build Coastguard Worker             } else {
1500*89a0ef05SAndroid Build Coastguard Worker               gain = sampleMap(gainmap_img, map_scale_factor, x, y, idwTable);
1501*89a0ef05SAndroid Build Coastguard Worker             }
1502*89a0ef05SAndroid Build Coastguard Worker 
1503*89a0ef05SAndroid Build Coastguard Worker #if USE_APPLY_GAIN_LUT
1504*89a0ef05SAndroid Build Coastguard Worker             rgb_hdr = applyGainLUT(rgb_sdr, gain, gainLUT, gainmap_metadata);
1505*89a0ef05SAndroid Build Coastguard Worker #else
1506*89a0ef05SAndroid Build Coastguard Worker             rgb_hdr = applyGain(rgb_sdr, gain, gainmap_metadata, gainmap_weight);
1507*89a0ef05SAndroid Build Coastguard Worker #endif
1508*89a0ef05SAndroid Build Coastguard Worker           } else {
1509*89a0ef05SAndroid Build Coastguard Worker             Color gain;
1510*89a0ef05SAndroid Build Coastguard Worker 
1511*89a0ef05SAndroid Build Coastguard Worker             if (map_scale_factor != floorf(map_scale_factor)) {
1512*89a0ef05SAndroid Build Coastguard Worker               gain = sampleMap3Channel(gainmap_img, map_scale_factor, x, y,
1513*89a0ef05SAndroid Build Coastguard Worker                                        gainmap_img->fmt == UHDR_IMG_FMT_32bppRGBA8888);
1514*89a0ef05SAndroid Build Coastguard Worker             } else {
1515*89a0ef05SAndroid Build Coastguard Worker               gain = sampleMap3Channel(gainmap_img, map_scale_factor, x, y, idwTable,
1516*89a0ef05SAndroid Build Coastguard Worker                                        gainmap_img->fmt == UHDR_IMG_FMT_32bppRGBA8888);
1517*89a0ef05SAndroid Build Coastguard Worker             }
1518*89a0ef05SAndroid Build Coastguard Worker 
1519*89a0ef05SAndroid Build Coastguard Worker #if USE_APPLY_GAIN_LUT
1520*89a0ef05SAndroid Build Coastguard Worker             rgb_hdr = applyGainLUT(rgb_sdr, gain, gainLUT, gainmap_metadata);
1521*89a0ef05SAndroid Build Coastguard Worker #else
1522*89a0ef05SAndroid Build Coastguard Worker             rgb_hdr = applyGain(rgb_sdr, gain, gainmap_metadata, gainmap_weight);
1523*89a0ef05SAndroid Build Coastguard Worker #endif
1524*89a0ef05SAndroid Build Coastguard Worker           }
1525*89a0ef05SAndroid Build Coastguard Worker 
1526*89a0ef05SAndroid Build Coastguard Worker           size_t pixel_idx = x + y * dest->stride[UHDR_PLANE_PACKED];
1527*89a0ef05SAndroid Build Coastguard Worker 
1528*89a0ef05SAndroid Build Coastguard Worker           switch (output_ct) {
1529*89a0ef05SAndroid Build Coastguard Worker             case UHDR_CT_LINEAR: {
1530*89a0ef05SAndroid Build Coastguard Worker               uint64_t rgba_f16 = colorToRgbaF16(rgb_hdr);
1531*89a0ef05SAndroid Build Coastguard Worker               reinterpret_cast<uint64_t*>(dest->planes[UHDR_PLANE_PACKED])[pixel_idx] = rgba_f16;
1532*89a0ef05SAndroid Build Coastguard Worker               break;
1533*89a0ef05SAndroid Build Coastguard Worker             }
1534*89a0ef05SAndroid Build Coastguard Worker             case UHDR_CT_HLG: {
1535*89a0ef05SAndroid Build Coastguard Worker #if USE_HLG_OETF_LUT
1536*89a0ef05SAndroid Build Coastguard Worker               ColorTransformFn hdrOetf = hlgOetfLUT;
1537*89a0ef05SAndroid Build Coastguard Worker #else
1538*89a0ef05SAndroid Build Coastguard Worker               ColorTransformFn hdrOetf = hlgOetf;
1539*89a0ef05SAndroid Build Coastguard Worker #endif
1540*89a0ef05SAndroid Build Coastguard Worker               rgb_hdr = rgb_hdr * kSdrWhiteNits / kHlgMaxNits;
1541*89a0ef05SAndroid Build Coastguard Worker               rgb_hdr = hlgInverseOotfApprox(rgb_hdr);
1542*89a0ef05SAndroid Build Coastguard Worker               Color rgb_gamma_hdr = hdrOetf(rgb_hdr);
1543*89a0ef05SAndroid Build Coastguard Worker               uint32_t rgba_1010102 = colorToRgba1010102(rgb_gamma_hdr);
1544*89a0ef05SAndroid Build Coastguard Worker               reinterpret_cast<uint32_t*>(dest->planes[UHDR_PLANE_PACKED])[pixel_idx] =
1545*89a0ef05SAndroid Build Coastguard Worker                   rgba_1010102;
1546*89a0ef05SAndroid Build Coastguard Worker               break;
1547*89a0ef05SAndroid Build Coastguard Worker             }
1548*89a0ef05SAndroid Build Coastguard Worker             case UHDR_CT_PQ: {
1549*89a0ef05SAndroid Build Coastguard Worker #if USE_PQ_OETF_LUT
1550*89a0ef05SAndroid Build Coastguard Worker               ColorTransformFn hdrOetf = pqOetfLUT;
1551*89a0ef05SAndroid Build Coastguard Worker #else
1552*89a0ef05SAndroid Build Coastguard Worker               ColorTransformFn hdrOetf = pqOetf;
1553*89a0ef05SAndroid Build Coastguard Worker #endif
1554*89a0ef05SAndroid Build Coastguard Worker               rgb_hdr = rgb_hdr * kSdrWhiteNits / kPqMaxNits;
1555*89a0ef05SAndroid Build Coastguard Worker               Color rgb_gamma_hdr = hdrOetf(rgb_hdr);
1556*89a0ef05SAndroid Build Coastguard Worker               uint32_t rgba_1010102 = colorToRgba1010102(rgb_gamma_hdr);
1557*89a0ef05SAndroid Build Coastguard Worker               reinterpret_cast<uint32_t*>(dest->planes[UHDR_PLANE_PACKED])[pixel_idx] =
1558*89a0ef05SAndroid Build Coastguard Worker                   rgba_1010102;
1559*89a0ef05SAndroid Build Coastguard Worker               break;
1560*89a0ef05SAndroid Build Coastguard Worker             }
1561*89a0ef05SAndroid Build Coastguard Worker             default: {
1562*89a0ef05SAndroid Build Coastguard Worker             }
1563*89a0ef05SAndroid Build Coastguard Worker               // Should be impossible to hit after input validation.
1564*89a0ef05SAndroid Build Coastguard Worker           }
1565*89a0ef05SAndroid Build Coastguard Worker         }
1566*89a0ef05SAndroid Build Coastguard Worker       }
1567*89a0ef05SAndroid Build Coastguard Worker     }
1568*89a0ef05SAndroid Build Coastguard Worker   };
1569*89a0ef05SAndroid Build Coastguard Worker 
1570*89a0ef05SAndroid Build Coastguard Worker   const int threads = (std::min)(GetCPUCoreCount(), 4u);
1571*89a0ef05SAndroid Build Coastguard Worker   std::vector<std::thread> workers;
1572*89a0ef05SAndroid Build Coastguard Worker   for (int th = 0; th < threads - 1; th++) {
1573*89a0ef05SAndroid Build Coastguard Worker     workers.push_back(std::thread(applyRecMap));
1574*89a0ef05SAndroid Build Coastguard Worker   }
1575*89a0ef05SAndroid Build Coastguard Worker   const unsigned int rowStep = threads == 1 ? sdr_intent->h : map_scale_factor_rnd;
1576*89a0ef05SAndroid Build Coastguard Worker   for (unsigned int rowStart = 0; rowStart < sdr_intent->h;) {
1577*89a0ef05SAndroid Build Coastguard Worker     unsigned int rowEnd = (std::min)(rowStart + rowStep, sdr_intent->h);
1578*89a0ef05SAndroid Build Coastguard Worker     jobQueue.enqueueJob(rowStart, rowEnd);
1579*89a0ef05SAndroid Build Coastguard Worker     rowStart = rowEnd;
1580*89a0ef05SAndroid Build Coastguard Worker   }
1581*89a0ef05SAndroid Build Coastguard Worker   jobQueue.markQueueForEnd();
1582*89a0ef05SAndroid Build Coastguard Worker   applyRecMap();
1583*89a0ef05SAndroid Build Coastguard Worker   std::for_each(workers.begin(), workers.end(), [](std::thread& t) { t.join(); });
1584*89a0ef05SAndroid Build Coastguard Worker 
1585*89a0ef05SAndroid Build Coastguard Worker   return g_no_error;
1586*89a0ef05SAndroid Build Coastguard Worker }
1587*89a0ef05SAndroid Build Coastguard Worker 
extractPrimaryImageAndGainMap(uhdr_compressed_image_t * jpegr_image,uhdr_compressed_image_t * primary_image,uhdr_compressed_image_t * gainmap_image)1588*89a0ef05SAndroid Build Coastguard Worker uhdr_error_info_t JpegR::extractPrimaryImageAndGainMap(uhdr_compressed_image_t* jpegr_image,
1589*89a0ef05SAndroid Build Coastguard Worker                                                        uhdr_compressed_image_t* primary_image,
1590*89a0ef05SAndroid Build Coastguard Worker                                                        uhdr_compressed_image_t* gainmap_image) {
1591*89a0ef05SAndroid Build Coastguard Worker   MessageHandler msg_handler;
1592*89a0ef05SAndroid Build Coastguard Worker   msg_handler.SetMessageWriter(make_unique<AlogMessageWriter>(AlogMessageWriter()));
1593*89a0ef05SAndroid Build Coastguard Worker 
1594*89a0ef05SAndroid Build Coastguard Worker   std::shared_ptr<DataSegment> seg = DataSegment::Create(
1595*89a0ef05SAndroid Build Coastguard Worker       DataRange(0, jpegr_image->data_sz), static_cast<const uint8_t*>(jpegr_image->data),
1596*89a0ef05SAndroid Build Coastguard Worker       DataSegment::BufferDispositionPolicy::kDontDelete);
1597*89a0ef05SAndroid Build Coastguard Worker   DataSegmentDataSource data_source(seg);
1598*89a0ef05SAndroid Build Coastguard Worker 
1599*89a0ef05SAndroid Build Coastguard Worker   JpegInfoBuilder jpeg_info_builder;
1600*89a0ef05SAndroid Build Coastguard Worker   jpeg_info_builder.SetImageLimit(2);
1601*89a0ef05SAndroid Build Coastguard Worker 
1602*89a0ef05SAndroid Build Coastguard Worker   JpegScanner jpeg_scanner(&msg_handler);
1603*89a0ef05SAndroid Build Coastguard Worker   jpeg_scanner.Run(&data_source, &jpeg_info_builder);
1604*89a0ef05SAndroid Build Coastguard Worker   data_source.Reset();
1605*89a0ef05SAndroid Build Coastguard Worker 
1606*89a0ef05SAndroid Build Coastguard Worker   if (jpeg_scanner.HasError()) {
1607*89a0ef05SAndroid Build Coastguard Worker     uhdr_error_info_t status;
1608*89a0ef05SAndroid Build Coastguard Worker     status.error_code = UHDR_CODEC_ERROR;
1609*89a0ef05SAndroid Build Coastguard Worker     status.has_detail = 1;
1610*89a0ef05SAndroid Build Coastguard Worker     auto messages = msg_handler.GetMessages();
1611*89a0ef05SAndroid Build Coastguard Worker     std::string append{};
1612*89a0ef05SAndroid Build Coastguard Worker     for (auto message : messages) append += message.GetText();
1613*89a0ef05SAndroid Build Coastguard Worker     snprintf(status.detail, sizeof status.detail, "%s", append.c_str());
1614*89a0ef05SAndroid Build Coastguard Worker     return status;
1615*89a0ef05SAndroid Build Coastguard Worker   }
1616*89a0ef05SAndroid Build Coastguard Worker 
1617*89a0ef05SAndroid Build Coastguard Worker   const auto& jpeg_info = jpeg_info_builder.GetInfo();
1618*89a0ef05SAndroid Build Coastguard Worker   const auto& image_ranges = jpeg_info.GetImageRanges();
1619*89a0ef05SAndroid Build Coastguard Worker 
1620*89a0ef05SAndroid Build Coastguard Worker   if (image_ranges.empty()) {
1621*89a0ef05SAndroid Build Coastguard Worker     uhdr_error_info_t status;
1622*89a0ef05SAndroid Build Coastguard Worker     status.error_code = UHDR_CODEC_INVALID_PARAM;
1623*89a0ef05SAndroid Build Coastguard Worker     status.has_detail = 1;
1624*89a0ef05SAndroid Build Coastguard Worker     snprintf(status.detail, sizeof status.detail, "input uhdr image does not any valid images");
1625*89a0ef05SAndroid Build Coastguard Worker     return status;
1626*89a0ef05SAndroid Build Coastguard Worker   }
1627*89a0ef05SAndroid Build Coastguard Worker 
1628*89a0ef05SAndroid Build Coastguard Worker   if (primary_image != nullptr) {
1629*89a0ef05SAndroid Build Coastguard Worker     primary_image->data = static_cast<uint8_t*>(jpegr_image->data) + image_ranges[0].GetBegin();
1630*89a0ef05SAndroid Build Coastguard Worker     primary_image->data_sz = image_ranges[0].GetLength();
1631*89a0ef05SAndroid Build Coastguard Worker   }
1632*89a0ef05SAndroid Build Coastguard Worker 
1633*89a0ef05SAndroid Build Coastguard Worker   if (image_ranges.size() == 1) {
1634*89a0ef05SAndroid Build Coastguard Worker     uhdr_error_info_t status;
1635*89a0ef05SAndroid Build Coastguard Worker     status.error_code = UHDR_CODEC_INVALID_PARAM;
1636*89a0ef05SAndroid Build Coastguard Worker     status.has_detail = 1;
1637*89a0ef05SAndroid Build Coastguard Worker     snprintf(status.detail, sizeof status.detail,
1638*89a0ef05SAndroid Build Coastguard Worker              "input uhdr image does not contain gainmap image");
1639*89a0ef05SAndroid Build Coastguard Worker     return status;
1640*89a0ef05SAndroid Build Coastguard Worker   }
1641*89a0ef05SAndroid Build Coastguard Worker 
1642*89a0ef05SAndroid Build Coastguard Worker   if (gainmap_image != nullptr) {
1643*89a0ef05SAndroid Build Coastguard Worker     gainmap_image->data = static_cast<uint8_t*>(jpegr_image->data) + image_ranges[1].GetBegin();
1644*89a0ef05SAndroid Build Coastguard Worker     gainmap_image->data_sz = image_ranges[1].GetLength();
1645*89a0ef05SAndroid Build Coastguard Worker   }
1646*89a0ef05SAndroid Build Coastguard Worker 
1647*89a0ef05SAndroid Build Coastguard Worker   // TODO: choose primary image and gain map image carefully
1648*89a0ef05SAndroid Build Coastguard Worker   if (image_ranges.size() > 2) {
1649*89a0ef05SAndroid Build Coastguard Worker     ALOGW("Number of jpeg images present %d, primary, gain map images may not be correctly chosen",
1650*89a0ef05SAndroid Build Coastguard Worker           (int)image_ranges.size());
1651*89a0ef05SAndroid Build Coastguard Worker   }
1652*89a0ef05SAndroid Build Coastguard Worker 
1653*89a0ef05SAndroid Build Coastguard Worker   return g_no_error;
1654*89a0ef05SAndroid Build Coastguard Worker }
1655*89a0ef05SAndroid Build Coastguard Worker 
parseJpegInfo(uhdr_compressed_image_t * jpeg_image,j_info_ptr image_info,unsigned int * img_width,unsigned int * img_height)1656*89a0ef05SAndroid Build Coastguard Worker uhdr_error_info_t JpegR::parseJpegInfo(uhdr_compressed_image_t* jpeg_image, j_info_ptr image_info,
1657*89a0ef05SAndroid Build Coastguard Worker                                        unsigned int* img_width, unsigned int* img_height) {
1658*89a0ef05SAndroid Build Coastguard Worker   JpegDecoderHelper jpeg_dec_obj;
1659*89a0ef05SAndroid Build Coastguard Worker   UHDR_ERR_CHECK(jpeg_dec_obj.parseImage(jpeg_image->data, jpeg_image->data_sz))
1660*89a0ef05SAndroid Build Coastguard Worker   unsigned int imgWidth, imgHeight, numComponents;
1661*89a0ef05SAndroid Build Coastguard Worker   imgWidth = jpeg_dec_obj.getDecompressedImageWidth();
1662*89a0ef05SAndroid Build Coastguard Worker   imgHeight = jpeg_dec_obj.getDecompressedImageHeight();
1663*89a0ef05SAndroid Build Coastguard Worker   numComponents = jpeg_dec_obj.getNumComponentsInImage();
1664*89a0ef05SAndroid Build Coastguard Worker 
1665*89a0ef05SAndroid Build Coastguard Worker   if (image_info != nullptr) {
1666*89a0ef05SAndroid Build Coastguard Worker     image_info->width = imgWidth;
1667*89a0ef05SAndroid Build Coastguard Worker     image_info->height = imgHeight;
1668*89a0ef05SAndroid Build Coastguard Worker     image_info->numComponents = numComponents;
1669*89a0ef05SAndroid Build Coastguard Worker     image_info->imgData.resize(jpeg_image->data_sz, 0);
1670*89a0ef05SAndroid Build Coastguard Worker     memcpy(static_cast<void*>(image_info->imgData.data()), jpeg_image->data, jpeg_image->data_sz);
1671*89a0ef05SAndroid Build Coastguard Worker     if (jpeg_dec_obj.getICCSize() != 0) {
1672*89a0ef05SAndroid Build Coastguard Worker       image_info->iccData.resize(jpeg_dec_obj.getICCSize(), 0);
1673*89a0ef05SAndroid Build Coastguard Worker       memcpy(static_cast<void*>(image_info->iccData.data()), jpeg_dec_obj.getICCPtr(),
1674*89a0ef05SAndroid Build Coastguard Worker              jpeg_dec_obj.getICCSize());
1675*89a0ef05SAndroid Build Coastguard Worker     }
1676*89a0ef05SAndroid Build Coastguard Worker     if (jpeg_dec_obj.getEXIFSize() != 0) {
1677*89a0ef05SAndroid Build Coastguard Worker       image_info->exifData.resize(jpeg_dec_obj.getEXIFSize(), 0);
1678*89a0ef05SAndroid Build Coastguard Worker       memcpy(static_cast<void*>(image_info->exifData.data()), jpeg_dec_obj.getEXIFPtr(),
1679*89a0ef05SAndroid Build Coastguard Worker              jpeg_dec_obj.getEXIFSize());
1680*89a0ef05SAndroid Build Coastguard Worker     }
1681*89a0ef05SAndroid Build Coastguard Worker     if (jpeg_dec_obj.getXMPSize() != 0) {
1682*89a0ef05SAndroid Build Coastguard Worker       image_info->xmpData.resize(jpeg_dec_obj.getXMPSize(), 0);
1683*89a0ef05SAndroid Build Coastguard Worker       memcpy(static_cast<void*>(image_info->xmpData.data()), jpeg_dec_obj.getXMPPtr(),
1684*89a0ef05SAndroid Build Coastguard Worker              jpeg_dec_obj.getXMPSize());
1685*89a0ef05SAndroid Build Coastguard Worker     }
1686*89a0ef05SAndroid Build Coastguard Worker     if (jpeg_dec_obj.getIsoMetadataSize() != 0) {
1687*89a0ef05SAndroid Build Coastguard Worker       image_info->isoData.resize(jpeg_dec_obj.getIsoMetadataSize(), 0);
1688*89a0ef05SAndroid Build Coastguard Worker       memcpy(static_cast<void*>(image_info->isoData.data()), jpeg_dec_obj.getIsoMetadataPtr(),
1689*89a0ef05SAndroid Build Coastguard Worker              jpeg_dec_obj.getIsoMetadataSize());
1690*89a0ef05SAndroid Build Coastguard Worker     }
1691*89a0ef05SAndroid Build Coastguard Worker   }
1692*89a0ef05SAndroid Build Coastguard Worker   if (img_width != nullptr && img_height != nullptr) {
1693*89a0ef05SAndroid Build Coastguard Worker     *img_width = imgWidth;
1694*89a0ef05SAndroid Build Coastguard Worker     *img_height = imgHeight;
1695*89a0ef05SAndroid Build Coastguard Worker   }
1696*89a0ef05SAndroid Build Coastguard Worker   return g_no_error;
1697*89a0ef05SAndroid Build Coastguard Worker }
1698*89a0ef05SAndroid Build Coastguard Worker 
ReinhardMap(float y_hdr,float headroom)1699*89a0ef05SAndroid Build Coastguard Worker static float ReinhardMap(float y_hdr, float headroom) {
1700*89a0ef05SAndroid Build Coastguard Worker   float out = 1.0 + y_hdr / (headroom * headroom);
1701*89a0ef05SAndroid Build Coastguard Worker   out /= 1.0 + y_hdr;
1702*89a0ef05SAndroid Build Coastguard Worker   return out * y_hdr;
1703*89a0ef05SAndroid Build Coastguard Worker }
1704*89a0ef05SAndroid Build Coastguard Worker 
globalTonemap(const std::array<float,3> & rgb_in,float headroom,bool is_normalized)1705*89a0ef05SAndroid Build Coastguard Worker GlobalTonemapOutputs globalTonemap(const std::array<float, 3>& rgb_in, float headroom,
1706*89a0ef05SAndroid Build Coastguard Worker                                    bool is_normalized) {
1707*89a0ef05SAndroid Build Coastguard Worker   // Scale to Headroom to get HDR values that are referenced to SDR white. The range [0.0, 1.0] is
1708*89a0ef05SAndroid Build Coastguard Worker   // linearly stretched to [0.0, headroom].
1709*89a0ef05SAndroid Build Coastguard Worker   std::array<float, 3> rgb_hdr;
1710*89a0ef05SAndroid Build Coastguard Worker   std::transform(rgb_in.begin(), rgb_in.end(), rgb_hdr.begin(),
1711*89a0ef05SAndroid Build Coastguard Worker                  [&](float x) { return is_normalized ? x * headroom : x; });
1712*89a0ef05SAndroid Build Coastguard Worker 
1713*89a0ef05SAndroid Build Coastguard Worker   // Apply a tone mapping to compress the range [0, headroom] to [0, 1] by
1714*89a0ef05SAndroid Build Coastguard Worker   // keeping the shadows the same and crushing the highlights.
1715*89a0ef05SAndroid Build Coastguard Worker   float max_hdr = *std::max_element(rgb_hdr.begin(), rgb_hdr.end());
1716*89a0ef05SAndroid Build Coastguard Worker   float max_sdr = ReinhardMap(max_hdr, headroom);
1717*89a0ef05SAndroid Build Coastguard Worker   std::array<float, 3> rgb_sdr;
1718*89a0ef05SAndroid Build Coastguard Worker   std::transform(rgb_hdr.begin(), rgb_hdr.end(), rgb_sdr.begin(), [&](float x) {
1719*89a0ef05SAndroid Build Coastguard Worker     if (x > 0.0f) {
1720*89a0ef05SAndroid Build Coastguard Worker       return x * max_sdr / max_hdr;
1721*89a0ef05SAndroid Build Coastguard Worker     }
1722*89a0ef05SAndroid Build Coastguard Worker     return 0.0f;
1723*89a0ef05SAndroid Build Coastguard Worker   });
1724*89a0ef05SAndroid Build Coastguard Worker 
1725*89a0ef05SAndroid Build Coastguard Worker   GlobalTonemapOutputs tonemap_outputs;
1726*89a0ef05SAndroid Build Coastguard Worker   tonemap_outputs.rgb_out = rgb_sdr;
1727*89a0ef05SAndroid Build Coastguard Worker   tonemap_outputs.y_hdr = max_hdr;
1728*89a0ef05SAndroid Build Coastguard Worker   tonemap_outputs.y_sdr = max_sdr;
1729*89a0ef05SAndroid Build Coastguard Worker 
1730*89a0ef05SAndroid Build Coastguard Worker   return tonemap_outputs;
1731*89a0ef05SAndroid Build Coastguard Worker }
1732*89a0ef05SAndroid Build Coastguard Worker 
ScaleTo8Bit(float value)1733*89a0ef05SAndroid Build Coastguard Worker uint8_t ScaleTo8Bit(float value) {
1734*89a0ef05SAndroid Build Coastguard Worker   constexpr float kMaxValFloat = 255.0f;
1735*89a0ef05SAndroid Build Coastguard Worker   constexpr int kMaxValInt = 255;
1736*89a0ef05SAndroid Build Coastguard Worker   return std::clamp(static_cast<int>(std::round(value * kMaxValFloat)), 0, kMaxValInt);
1737*89a0ef05SAndroid Build Coastguard Worker }
1738*89a0ef05SAndroid Build Coastguard Worker 
toneMap(uhdr_raw_image_t * hdr_intent,uhdr_raw_image_t * sdr_intent)1739*89a0ef05SAndroid Build Coastguard Worker uhdr_error_info_t JpegR::toneMap(uhdr_raw_image_t* hdr_intent, uhdr_raw_image_t* sdr_intent) {
1740*89a0ef05SAndroid Build Coastguard Worker   if (hdr_intent->fmt != UHDR_IMG_FMT_24bppYCbCrP010 &&
1741*89a0ef05SAndroid Build Coastguard Worker       hdr_intent->fmt != UHDR_IMG_FMT_30bppYCbCr444 &&
1742*89a0ef05SAndroid Build Coastguard Worker       hdr_intent->fmt != UHDR_IMG_FMT_32bppRGBA1010102 &&
1743*89a0ef05SAndroid Build Coastguard Worker       hdr_intent->fmt != UHDR_IMG_FMT_64bppRGBAHalfFloat) {
1744*89a0ef05SAndroid Build Coastguard Worker     uhdr_error_info_t status;
1745*89a0ef05SAndroid Build Coastguard Worker     status.error_code = UHDR_CODEC_UNSUPPORTED_FEATURE;
1746*89a0ef05SAndroid Build Coastguard Worker     status.has_detail = 1;
1747*89a0ef05SAndroid Build Coastguard Worker     snprintf(status.detail, sizeof status.detail,
1748*89a0ef05SAndroid Build Coastguard Worker              "tonemap method expects hdr intent color format to be one of "
1749*89a0ef05SAndroid Build Coastguard Worker              "{UHDR_IMG_FMT_24bppYCbCrP010, UHDR_IMG_FMT_30bppYCbCr444, "
1750*89a0ef05SAndroid Build Coastguard Worker              "UHDR_IMG_FMT_32bppRGBA1010102, UHDR_IMG_FMT_64bppRGBAHalfFloat}. Received %d",
1751*89a0ef05SAndroid Build Coastguard Worker              hdr_intent->fmt);
1752*89a0ef05SAndroid Build Coastguard Worker     return status;
1753*89a0ef05SAndroid Build Coastguard Worker   }
1754*89a0ef05SAndroid Build Coastguard Worker 
1755*89a0ef05SAndroid Build Coastguard Worker   if (hdr_intent->fmt == UHDR_IMG_FMT_24bppYCbCrP010 &&
1756*89a0ef05SAndroid Build Coastguard Worker       sdr_intent->fmt != UHDR_IMG_FMT_12bppYCbCr420) {
1757*89a0ef05SAndroid Build Coastguard Worker     uhdr_error_info_t status;
1758*89a0ef05SAndroid Build Coastguard Worker     status.error_code = UHDR_CODEC_UNSUPPORTED_FEATURE;
1759*89a0ef05SAndroid Build Coastguard Worker     status.has_detail = 1;
1760*89a0ef05SAndroid Build Coastguard Worker     snprintf(status.detail, sizeof status.detail,
1761*89a0ef05SAndroid Build Coastguard Worker              "tonemap method expects sdr intent color format to be UHDR_IMG_FMT_12bppYCbCr420, if "
1762*89a0ef05SAndroid Build Coastguard Worker              "hdr intent color format is UHDR_IMG_FMT_24bppYCbCrP010. Received %d",
1763*89a0ef05SAndroid Build Coastguard Worker              sdr_intent->fmt);
1764*89a0ef05SAndroid Build Coastguard Worker     return status;
1765*89a0ef05SAndroid Build Coastguard Worker   }
1766*89a0ef05SAndroid Build Coastguard Worker 
1767*89a0ef05SAndroid Build Coastguard Worker   if (hdr_intent->fmt == UHDR_IMG_FMT_30bppYCbCr444 &&
1768*89a0ef05SAndroid Build Coastguard Worker       sdr_intent->fmt != UHDR_IMG_FMT_24bppYCbCr444) {
1769*89a0ef05SAndroid Build Coastguard Worker     uhdr_error_info_t status;
1770*89a0ef05SAndroid Build Coastguard Worker     status.error_code = UHDR_CODEC_UNSUPPORTED_FEATURE;
1771*89a0ef05SAndroid Build Coastguard Worker     status.has_detail = 1;
1772*89a0ef05SAndroid Build Coastguard Worker     snprintf(status.detail, sizeof status.detail,
1773*89a0ef05SAndroid Build Coastguard Worker              "tonemap method expects sdr intent color format to be UHDR_IMG_FMT_24bppYCbCr444, if "
1774*89a0ef05SAndroid Build Coastguard Worker              "hdr intent color format is UHDR_IMG_FMT_30bppYCbCr444. Received %d",
1775*89a0ef05SAndroid Build Coastguard Worker              sdr_intent->fmt);
1776*89a0ef05SAndroid Build Coastguard Worker     return status;
1777*89a0ef05SAndroid Build Coastguard Worker   }
1778*89a0ef05SAndroid Build Coastguard Worker 
1779*89a0ef05SAndroid Build Coastguard Worker   if ((hdr_intent->fmt == UHDR_IMG_FMT_32bppRGBA1010102 ||
1780*89a0ef05SAndroid Build Coastguard Worker        hdr_intent->fmt == UHDR_IMG_FMT_64bppRGBAHalfFloat) &&
1781*89a0ef05SAndroid Build Coastguard Worker       sdr_intent->fmt != UHDR_IMG_FMT_32bppRGBA8888) {
1782*89a0ef05SAndroid Build Coastguard Worker     uhdr_error_info_t status;
1783*89a0ef05SAndroid Build Coastguard Worker     status.error_code = UHDR_CODEC_UNSUPPORTED_FEATURE;
1784*89a0ef05SAndroid Build Coastguard Worker     status.has_detail = 1;
1785*89a0ef05SAndroid Build Coastguard Worker     snprintf(status.detail, sizeof status.detail,
1786*89a0ef05SAndroid Build Coastguard Worker              "tonemap method expects sdr intent color format to be UHDR_IMG_FMT_32bppRGBA8888, if "
1787*89a0ef05SAndroid Build Coastguard Worker              "hdr intent color format is UHDR_IMG_FMT_32bppRGBA1010102 or "
1788*89a0ef05SAndroid Build Coastguard Worker              "UHDR_IMG_FMT_64bppRGBAHalfFloat. Received %d",
1789*89a0ef05SAndroid Build Coastguard Worker              sdr_intent->fmt);
1790*89a0ef05SAndroid Build Coastguard Worker     return status;
1791*89a0ef05SAndroid Build Coastguard Worker   }
1792*89a0ef05SAndroid Build Coastguard Worker 
1793*89a0ef05SAndroid Build Coastguard Worker   ColorTransformFn hdrYuvToRgbFn = getYuvToRgbFn(hdr_intent->cg);
1794*89a0ef05SAndroid Build Coastguard Worker   if (hdrYuvToRgbFn == nullptr) {
1795*89a0ef05SAndroid Build Coastguard Worker     uhdr_error_info_t status;
1796*89a0ef05SAndroid Build Coastguard Worker     status.error_code = UHDR_CODEC_UNSUPPORTED_FEATURE;
1797*89a0ef05SAndroid Build Coastguard Worker     status.has_detail = 1;
1798*89a0ef05SAndroid Build Coastguard Worker     snprintf(status.detail, sizeof status.detail,
1799*89a0ef05SAndroid Build Coastguard Worker              "No implementation available for converting yuv to rgb for color gamut %d",
1800*89a0ef05SAndroid Build Coastguard Worker              hdr_intent->cg);
1801*89a0ef05SAndroid Build Coastguard Worker     return status;
1802*89a0ef05SAndroid Build Coastguard Worker   }
1803*89a0ef05SAndroid Build Coastguard Worker 
1804*89a0ef05SAndroid Build Coastguard Worker   LuminanceFn hdrLuminanceFn = getLuminanceFn(hdr_intent->cg);
1805*89a0ef05SAndroid Build Coastguard Worker   if (hdrLuminanceFn == nullptr) {
1806*89a0ef05SAndroid Build Coastguard Worker     uhdr_error_info_t status;
1807*89a0ef05SAndroid Build Coastguard Worker     status.error_code = UHDR_CODEC_UNSUPPORTED_FEATURE;
1808*89a0ef05SAndroid Build Coastguard Worker     status.has_detail = 1;
1809*89a0ef05SAndroid Build Coastguard Worker     snprintf(status.detail, sizeof status.detail,
1810*89a0ef05SAndroid Build Coastguard Worker              "No implementation available for calculating luminance for color gamut %d",
1811*89a0ef05SAndroid Build Coastguard Worker              hdr_intent->cg);
1812*89a0ef05SAndroid Build Coastguard Worker     return status;
1813*89a0ef05SAndroid Build Coastguard Worker   }
1814*89a0ef05SAndroid Build Coastguard Worker 
1815*89a0ef05SAndroid Build Coastguard Worker   SceneToDisplayLuminanceFn hdrOotfFn = getOotfFn(hdr_intent->ct);
1816*89a0ef05SAndroid Build Coastguard Worker   if (hdrOotfFn == nullptr) {
1817*89a0ef05SAndroid Build Coastguard Worker     uhdr_error_info_t status;
1818*89a0ef05SAndroid Build Coastguard Worker     status.error_code = UHDR_CODEC_UNSUPPORTED_FEATURE;
1819*89a0ef05SAndroid Build Coastguard Worker     status.has_detail = 1;
1820*89a0ef05SAndroid Build Coastguard Worker     snprintf(status.detail, sizeof status.detail,
1821*89a0ef05SAndroid Build Coastguard Worker              "No implementation available for calculating Ootf for color transfer %d",
1822*89a0ef05SAndroid Build Coastguard Worker              hdr_intent->ct);
1823*89a0ef05SAndroid Build Coastguard Worker     return status;
1824*89a0ef05SAndroid Build Coastguard Worker   }
1825*89a0ef05SAndroid Build Coastguard Worker 
1826*89a0ef05SAndroid Build Coastguard Worker   ColorTransformFn hdrInvOetf = getInverseOetfFn(hdr_intent->ct);
1827*89a0ef05SAndroid Build Coastguard Worker   if (hdrInvOetf == nullptr) {
1828*89a0ef05SAndroid Build Coastguard Worker     uhdr_error_info_t status;
1829*89a0ef05SAndroid Build Coastguard Worker     status.error_code = UHDR_CODEC_UNSUPPORTED_FEATURE;
1830*89a0ef05SAndroid Build Coastguard Worker     status.has_detail = 1;
1831*89a0ef05SAndroid Build Coastguard Worker     snprintf(status.detail, sizeof status.detail,
1832*89a0ef05SAndroid Build Coastguard Worker              "No implementation available for converting transfer characteristics %d to linear",
1833*89a0ef05SAndroid Build Coastguard Worker              hdr_intent->ct);
1834*89a0ef05SAndroid Build Coastguard Worker     return status;
1835*89a0ef05SAndroid Build Coastguard Worker   }
1836*89a0ef05SAndroid Build Coastguard Worker 
1837*89a0ef05SAndroid Build Coastguard Worker   float hdr_white_nits = getReferenceDisplayPeakLuminanceInNits(hdr_intent->ct);
1838*89a0ef05SAndroid Build Coastguard Worker   if (hdr_white_nits == -1.0f) {
1839*89a0ef05SAndroid Build Coastguard Worker     uhdr_error_info_t status;
1840*89a0ef05SAndroid Build Coastguard Worker     status.error_code = UHDR_CODEC_UNSUPPORTED_FEATURE;
1841*89a0ef05SAndroid Build Coastguard Worker     status.has_detail = 1;
1842*89a0ef05SAndroid Build Coastguard Worker     snprintf(status.detail, sizeof status.detail,
1843*89a0ef05SAndroid Build Coastguard Worker              "received invalid peak brightness %f nits for hdr reference display with color "
1844*89a0ef05SAndroid Build Coastguard Worker              "transfer %d ",
1845*89a0ef05SAndroid Build Coastguard Worker              hdr_white_nits, hdr_intent->ct);
1846*89a0ef05SAndroid Build Coastguard Worker     return status;
1847*89a0ef05SAndroid Build Coastguard Worker   }
1848*89a0ef05SAndroid Build Coastguard Worker 
1849*89a0ef05SAndroid Build Coastguard Worker   GetPixelFn get_pixel_fn = getPixelFn(hdr_intent->fmt);
1850*89a0ef05SAndroid Build Coastguard Worker   if (get_pixel_fn == nullptr) {
1851*89a0ef05SAndroid Build Coastguard Worker     uhdr_error_info_t status;
1852*89a0ef05SAndroid Build Coastguard Worker     status.error_code = UHDR_CODEC_UNSUPPORTED_FEATURE;
1853*89a0ef05SAndroid Build Coastguard Worker     status.has_detail = 1;
1854*89a0ef05SAndroid Build Coastguard Worker     snprintf(status.detail, sizeof status.detail,
1855*89a0ef05SAndroid Build Coastguard Worker              "No implementation available for reading pixels for color format %d", hdr_intent->fmt);
1856*89a0ef05SAndroid Build Coastguard Worker     return status;
1857*89a0ef05SAndroid Build Coastguard Worker   }
1858*89a0ef05SAndroid Build Coastguard Worker 
1859*89a0ef05SAndroid Build Coastguard Worker   PutPixelFn put_pixel_fn = putPixelFn(sdr_intent->fmt);
1860*89a0ef05SAndroid Build Coastguard Worker   // for subsampled formats, we are writing to raw image buffers directly instead of using
1861*89a0ef05SAndroid Build Coastguard Worker   // put_pixel_fn
1862*89a0ef05SAndroid Build Coastguard Worker   if (put_pixel_fn == nullptr && sdr_intent->fmt != UHDR_IMG_FMT_12bppYCbCr420) {
1863*89a0ef05SAndroid Build Coastguard Worker     uhdr_error_info_t status;
1864*89a0ef05SAndroid Build Coastguard Worker     status.error_code = UHDR_CODEC_UNSUPPORTED_FEATURE;
1865*89a0ef05SAndroid Build Coastguard Worker     status.has_detail = 1;
1866*89a0ef05SAndroid Build Coastguard Worker     snprintf(status.detail, sizeof status.detail,
1867*89a0ef05SAndroid Build Coastguard Worker              "No implementation available for writing pixels for color format %d", sdr_intent->fmt);
1868*89a0ef05SAndroid Build Coastguard Worker     return status;
1869*89a0ef05SAndroid Build Coastguard Worker   }
1870*89a0ef05SAndroid Build Coastguard Worker 
1871*89a0ef05SAndroid Build Coastguard Worker   sdr_intent->cg = UHDR_CG_DISPLAY_P3;
1872*89a0ef05SAndroid Build Coastguard Worker   sdr_intent->ct = UHDR_CT_SRGB;
1873*89a0ef05SAndroid Build Coastguard Worker   sdr_intent->range = UHDR_CR_FULL_RANGE;
1874*89a0ef05SAndroid Build Coastguard Worker 
1875*89a0ef05SAndroid Build Coastguard Worker   ColorTransformFn hdrGamutConversionFn = getGamutConversionFn(sdr_intent->cg, hdr_intent->cg);
1876*89a0ef05SAndroid Build Coastguard Worker 
1877*89a0ef05SAndroid Build Coastguard Worker   unsigned int height = hdr_intent->h;
1878*89a0ef05SAndroid Build Coastguard Worker   const int threads = (std::min)(GetCPUCoreCount(), 4u);
1879*89a0ef05SAndroid Build Coastguard Worker   // for 420 subsampling, process 2 rows at once
1880*89a0ef05SAndroid Build Coastguard Worker   const int jobSizeInRows = hdr_intent->fmt == UHDR_IMG_FMT_24bppYCbCrP010 ? 2 : 1;
1881*89a0ef05SAndroid Build Coastguard Worker   unsigned int rowStep = threads == 1 ? height : jobSizeInRows;
1882*89a0ef05SAndroid Build Coastguard Worker   JobQueue jobQueue;
1883*89a0ef05SAndroid Build Coastguard Worker   std::function<void()> toneMapInternal;
1884*89a0ef05SAndroid Build Coastguard Worker 
1885*89a0ef05SAndroid Build Coastguard Worker   toneMapInternal = [hdr_intent, sdr_intent, hdrInvOetf, hdrGamutConversionFn, hdrYuvToRgbFn,
1886*89a0ef05SAndroid Build Coastguard Worker                      hdr_white_nits, get_pixel_fn, put_pixel_fn, hdrLuminanceFn, hdrOotfFn,
1887*89a0ef05SAndroid Build Coastguard Worker                      &jobQueue]() -> void {
1888*89a0ef05SAndroid Build Coastguard Worker     unsigned int rowStart, rowEnd;
1889*89a0ef05SAndroid Build Coastguard Worker     const int hfactor = hdr_intent->fmt == UHDR_IMG_FMT_24bppYCbCrP010 ? 2 : 1;
1890*89a0ef05SAndroid Build Coastguard Worker     const int vfactor = hdr_intent->fmt == UHDR_IMG_FMT_24bppYCbCrP010 ? 2 : 1;
1891*89a0ef05SAndroid Build Coastguard Worker     const bool isHdrIntentRgb = isPixelFormatRgb(hdr_intent->fmt);
1892*89a0ef05SAndroid Build Coastguard Worker     const bool isSdrIntentRgb = isPixelFormatRgb(sdr_intent->fmt);
1893*89a0ef05SAndroid Build Coastguard Worker     const bool is_normalized = hdr_intent->ct != UHDR_CT_LINEAR;
1894*89a0ef05SAndroid Build Coastguard Worker     uint8_t* luma_data = reinterpret_cast<uint8_t*>(sdr_intent->planes[UHDR_PLANE_Y]);
1895*89a0ef05SAndroid Build Coastguard Worker     uint8_t* cb_data = reinterpret_cast<uint8_t*>(sdr_intent->planes[UHDR_PLANE_U]);
1896*89a0ef05SAndroid Build Coastguard Worker     uint8_t* cr_data = reinterpret_cast<uint8_t*>(sdr_intent->planes[UHDR_PLANE_V]);
1897*89a0ef05SAndroid Build Coastguard Worker     size_t luma_stride = sdr_intent->stride[UHDR_PLANE_Y];
1898*89a0ef05SAndroid Build Coastguard Worker     size_t cb_stride = sdr_intent->stride[UHDR_PLANE_U];
1899*89a0ef05SAndroid Build Coastguard Worker     size_t cr_stride = sdr_intent->stride[UHDR_PLANE_V];
1900*89a0ef05SAndroid Build Coastguard Worker 
1901*89a0ef05SAndroid Build Coastguard Worker     while (jobQueue.dequeueJob(rowStart, rowEnd)) {
1902*89a0ef05SAndroid Build Coastguard Worker       for (size_t y = rowStart; y < rowEnd; y += vfactor) {
1903*89a0ef05SAndroid Build Coastguard Worker         for (size_t x = 0; x < hdr_intent->w; x += hfactor) {
1904*89a0ef05SAndroid Build Coastguard Worker           // meant for p010 input
1905*89a0ef05SAndroid Build Coastguard Worker           float sdr_u_gamma = 0.0f;
1906*89a0ef05SAndroid Build Coastguard Worker           float sdr_v_gamma = 0.0f;
1907*89a0ef05SAndroid Build Coastguard Worker 
1908*89a0ef05SAndroid Build Coastguard Worker           for (int i = 0; i < vfactor; i++) {
1909*89a0ef05SAndroid Build Coastguard Worker             for (int j = 0; j < hfactor; j++) {
1910*89a0ef05SAndroid Build Coastguard Worker               Color hdr_rgb_gamma;
1911*89a0ef05SAndroid Build Coastguard Worker 
1912*89a0ef05SAndroid Build Coastguard Worker               if (isHdrIntentRgb) {
1913*89a0ef05SAndroid Build Coastguard Worker                 hdr_rgb_gamma = get_pixel_fn(hdr_intent, x + j, y + i);
1914*89a0ef05SAndroid Build Coastguard Worker               } else {
1915*89a0ef05SAndroid Build Coastguard Worker                 Color hdr_yuv_gamma = get_pixel_fn(hdr_intent, x + j, y + i);
1916*89a0ef05SAndroid Build Coastguard Worker                 hdr_rgb_gamma = hdrYuvToRgbFn(hdr_yuv_gamma);
1917*89a0ef05SAndroid Build Coastguard Worker               }
1918*89a0ef05SAndroid Build Coastguard Worker               Color hdr_rgb = hdrInvOetf(hdr_rgb_gamma);
1919*89a0ef05SAndroid Build Coastguard Worker               hdr_rgb = hdrOotfFn(hdr_rgb, hdrLuminanceFn);
1920*89a0ef05SAndroid Build Coastguard Worker 
1921*89a0ef05SAndroid Build Coastguard Worker               GlobalTonemapOutputs tonemap_outputs = globalTonemap(
1922*89a0ef05SAndroid Build Coastguard Worker                   {hdr_rgb.r, hdr_rgb.g, hdr_rgb.b}, hdr_white_nits / kSdrWhiteNits, is_normalized);
1923*89a0ef05SAndroid Build Coastguard Worker               Color sdr_rgb_linear_bt2100 = {
1924*89a0ef05SAndroid Build Coastguard Worker                   {{tonemap_outputs.rgb_out[0], tonemap_outputs.rgb_out[1],
1925*89a0ef05SAndroid Build Coastguard Worker                     tonemap_outputs.rgb_out[2]}}};
1926*89a0ef05SAndroid Build Coastguard Worker               Color sdr_rgb = hdrGamutConversionFn(sdr_rgb_linear_bt2100);
1927*89a0ef05SAndroid Build Coastguard Worker 
1928*89a0ef05SAndroid Build Coastguard Worker               // Hard clip out-of-gamut values;
1929*89a0ef05SAndroid Build Coastguard Worker               sdr_rgb = clampPixelFloat(sdr_rgb);
1930*89a0ef05SAndroid Build Coastguard Worker 
1931*89a0ef05SAndroid Build Coastguard Worker               Color sdr_rgb_gamma = srgbOetf(sdr_rgb);
1932*89a0ef05SAndroid Build Coastguard Worker               if (isSdrIntentRgb) {
1933*89a0ef05SAndroid Build Coastguard Worker                 put_pixel_fn(sdr_intent, (x + j), (y + i), sdr_rgb_gamma);
1934*89a0ef05SAndroid Build Coastguard Worker               } else {
1935*89a0ef05SAndroid Build Coastguard Worker                 Color sdr_yuv_gamma = p3RgbToYuv(sdr_rgb_gamma);
1936*89a0ef05SAndroid Build Coastguard Worker                 sdr_yuv_gamma += {{{0.0f, 0.5f, 0.5f}}};
1937*89a0ef05SAndroid Build Coastguard Worker                 if (sdr_intent->fmt != UHDR_IMG_FMT_12bppYCbCr420) {
1938*89a0ef05SAndroid Build Coastguard Worker                   put_pixel_fn(sdr_intent, (x + j), (y + i), sdr_yuv_gamma);
1939*89a0ef05SAndroid Build Coastguard Worker                 } else {
1940*89a0ef05SAndroid Build Coastguard Worker                   size_t out_y_idx = (y + i) * luma_stride + x + j;
1941*89a0ef05SAndroid Build Coastguard Worker                   luma_data[out_y_idx] = ScaleTo8Bit(sdr_yuv_gamma.y);
1942*89a0ef05SAndroid Build Coastguard Worker 
1943*89a0ef05SAndroid Build Coastguard Worker                   sdr_u_gamma += sdr_yuv_gamma.u;
1944*89a0ef05SAndroid Build Coastguard Worker                   sdr_v_gamma += sdr_yuv_gamma.v;
1945*89a0ef05SAndroid Build Coastguard Worker                 }
1946*89a0ef05SAndroid Build Coastguard Worker               }
1947*89a0ef05SAndroid Build Coastguard Worker             }
1948*89a0ef05SAndroid Build Coastguard Worker           }
1949*89a0ef05SAndroid Build Coastguard Worker           if (sdr_intent->fmt == UHDR_IMG_FMT_12bppYCbCr420) {
1950*89a0ef05SAndroid Build Coastguard Worker             sdr_u_gamma /= (hfactor * vfactor);
1951*89a0ef05SAndroid Build Coastguard Worker             sdr_v_gamma /= (hfactor * vfactor);
1952*89a0ef05SAndroid Build Coastguard Worker             cb_data[x / hfactor + (y / vfactor) * cb_stride] = ScaleTo8Bit(sdr_u_gamma);
1953*89a0ef05SAndroid Build Coastguard Worker             cr_data[x / hfactor + (y / vfactor) * cr_stride] = ScaleTo8Bit(sdr_v_gamma);
1954*89a0ef05SAndroid Build Coastguard Worker           }
1955*89a0ef05SAndroid Build Coastguard Worker         }
1956*89a0ef05SAndroid Build Coastguard Worker       }
1957*89a0ef05SAndroid Build Coastguard Worker     }
1958*89a0ef05SAndroid Build Coastguard Worker   };
1959*89a0ef05SAndroid Build Coastguard Worker 
1960*89a0ef05SAndroid Build Coastguard Worker   // tone map
1961*89a0ef05SAndroid Build Coastguard Worker   std::vector<std::thread> workers;
1962*89a0ef05SAndroid Build Coastguard Worker   for (int th = 0; th < threads - 1; th++) {
1963*89a0ef05SAndroid Build Coastguard Worker     workers.push_back(std::thread(toneMapInternal));
1964*89a0ef05SAndroid Build Coastguard Worker   }
1965*89a0ef05SAndroid Build Coastguard Worker 
1966*89a0ef05SAndroid Build Coastguard Worker   for (unsigned int rowStart = 0; rowStart < height;) {
1967*89a0ef05SAndroid Build Coastguard Worker     unsigned int rowEnd = (std::min)(rowStart + rowStep, height);
1968*89a0ef05SAndroid Build Coastguard Worker     jobQueue.enqueueJob(rowStart, rowEnd);
1969*89a0ef05SAndroid Build Coastguard Worker     rowStart = rowEnd;
1970*89a0ef05SAndroid Build Coastguard Worker   }
1971*89a0ef05SAndroid Build Coastguard Worker   jobQueue.markQueueForEnd();
1972*89a0ef05SAndroid Build Coastguard Worker   toneMapInternal();
1973*89a0ef05SAndroid Build Coastguard Worker   std::for_each(workers.begin(), workers.end(), [](std::thread& t) { t.join(); });
1974*89a0ef05SAndroid Build Coastguard Worker 
1975*89a0ef05SAndroid Build Coastguard Worker   return g_no_error;
1976*89a0ef05SAndroid Build Coastguard Worker }
1977*89a0ef05SAndroid Build Coastguard Worker 
areInputArgumentsValid(jr_uncompressed_ptr p010_image_ptr,jr_uncompressed_ptr yuv420_image_ptr,ultrahdr_transfer_function hdr_tf,jr_compressed_ptr dest_ptr)1978*89a0ef05SAndroid Build Coastguard Worker status_t JpegR::areInputArgumentsValid(jr_uncompressed_ptr p010_image_ptr,
1979*89a0ef05SAndroid Build Coastguard Worker                                        jr_uncompressed_ptr yuv420_image_ptr,
1980*89a0ef05SAndroid Build Coastguard Worker                                        ultrahdr_transfer_function hdr_tf,
1981*89a0ef05SAndroid Build Coastguard Worker                                        jr_compressed_ptr dest_ptr) {
1982*89a0ef05SAndroid Build Coastguard Worker   if (p010_image_ptr == nullptr || p010_image_ptr->data == nullptr) {
1983*89a0ef05SAndroid Build Coastguard Worker     ALOGE("Received nullptr for input p010 image");
1984*89a0ef05SAndroid Build Coastguard Worker     return ERROR_JPEGR_BAD_PTR;
1985*89a0ef05SAndroid Build Coastguard Worker   }
1986*89a0ef05SAndroid Build Coastguard Worker   if (p010_image_ptr->width % 2 != 0 || p010_image_ptr->height % 2 != 0) {
1987*89a0ef05SAndroid Build Coastguard Worker     ALOGE("Image dimensions cannot be odd, image dimensions %ux%u", p010_image_ptr->width,
1988*89a0ef05SAndroid Build Coastguard Worker           p010_image_ptr->height);
1989*89a0ef05SAndroid Build Coastguard Worker     return ERROR_JPEGR_UNSUPPORTED_WIDTH_HEIGHT;
1990*89a0ef05SAndroid Build Coastguard Worker   }
1991*89a0ef05SAndroid Build Coastguard Worker   if ((int)p010_image_ptr->width < kMinWidth || (int)p010_image_ptr->height < kMinHeight) {
1992*89a0ef05SAndroid Build Coastguard Worker     ALOGE("Image dimensions cannot be less than %dx%d, image dimensions %ux%u", kMinWidth,
1993*89a0ef05SAndroid Build Coastguard Worker           kMinHeight, p010_image_ptr->width, p010_image_ptr->height);
1994*89a0ef05SAndroid Build Coastguard Worker     return ERROR_JPEGR_UNSUPPORTED_WIDTH_HEIGHT;
1995*89a0ef05SAndroid Build Coastguard Worker   }
1996*89a0ef05SAndroid Build Coastguard Worker   if ((int)p010_image_ptr->width > kMaxWidth || (int)p010_image_ptr->height > kMaxHeight) {
1997*89a0ef05SAndroid Build Coastguard Worker     ALOGE("Image dimensions cannot be larger than %dx%d, image dimensions %ux%u", kMaxWidth,
1998*89a0ef05SAndroid Build Coastguard Worker           kMaxHeight, p010_image_ptr->width, p010_image_ptr->height);
1999*89a0ef05SAndroid Build Coastguard Worker     return ERROR_JPEGR_UNSUPPORTED_WIDTH_HEIGHT;
2000*89a0ef05SAndroid Build Coastguard Worker   }
2001*89a0ef05SAndroid Build Coastguard Worker   if (p010_image_ptr->colorGamut <= ULTRAHDR_COLORGAMUT_UNSPECIFIED ||
2002*89a0ef05SAndroid Build Coastguard Worker       p010_image_ptr->colorGamut > ULTRAHDR_COLORGAMUT_MAX) {
2003*89a0ef05SAndroid Build Coastguard Worker     ALOGE("Unrecognized p010 color gamut %d", p010_image_ptr->colorGamut);
2004*89a0ef05SAndroid Build Coastguard Worker     return ERROR_JPEGR_INVALID_COLORGAMUT;
2005*89a0ef05SAndroid Build Coastguard Worker   }
2006*89a0ef05SAndroid Build Coastguard Worker   if (p010_image_ptr->luma_stride != 0 && p010_image_ptr->luma_stride < p010_image_ptr->width) {
2007*89a0ef05SAndroid Build Coastguard Worker     ALOGE("Luma stride must not be smaller than width, stride=%u, width=%u",
2008*89a0ef05SAndroid Build Coastguard Worker           p010_image_ptr->luma_stride, p010_image_ptr->width);
2009*89a0ef05SAndroid Build Coastguard Worker     return ERROR_JPEGR_INVALID_STRIDE;
2010*89a0ef05SAndroid Build Coastguard Worker   }
2011*89a0ef05SAndroid Build Coastguard Worker   if (p010_image_ptr->chroma_data != nullptr &&
2012*89a0ef05SAndroid Build Coastguard Worker       p010_image_ptr->chroma_stride < p010_image_ptr->width) {
2013*89a0ef05SAndroid Build Coastguard Worker     ALOGE("Chroma stride must not be smaller than width, stride=%u, width=%u",
2014*89a0ef05SAndroid Build Coastguard Worker           p010_image_ptr->chroma_stride, p010_image_ptr->width);
2015*89a0ef05SAndroid Build Coastguard Worker     return ERROR_JPEGR_INVALID_STRIDE;
2016*89a0ef05SAndroid Build Coastguard Worker   }
2017*89a0ef05SAndroid Build Coastguard Worker   if (dest_ptr == nullptr || dest_ptr->data == nullptr) {
2018*89a0ef05SAndroid Build Coastguard Worker     ALOGE("Received nullptr for destination");
2019*89a0ef05SAndroid Build Coastguard Worker     return ERROR_JPEGR_BAD_PTR;
2020*89a0ef05SAndroid Build Coastguard Worker   }
2021*89a0ef05SAndroid Build Coastguard Worker   if (hdr_tf <= ULTRAHDR_TF_UNSPECIFIED || hdr_tf > ULTRAHDR_TF_MAX || hdr_tf == ULTRAHDR_TF_SRGB) {
2022*89a0ef05SAndroid Build Coastguard Worker     ALOGE("Invalid hdr transfer function %d", hdr_tf);
2023*89a0ef05SAndroid Build Coastguard Worker     return ERROR_JPEGR_INVALID_TRANS_FUNC;
2024*89a0ef05SAndroid Build Coastguard Worker   }
2025*89a0ef05SAndroid Build Coastguard Worker   if (mMapDimensionScaleFactor <= 0 || mMapDimensionScaleFactor > 128) {
2026*89a0ef05SAndroid Build Coastguard Worker     ALOGE("gainmap scale factor is ecpected to be in range (0, 128], received %d",
2027*89a0ef05SAndroid Build Coastguard Worker           mMapDimensionScaleFactor);
2028*89a0ef05SAndroid Build Coastguard Worker     return ERROR_JPEGR_UNSUPPORTED_MAP_SCALE_FACTOR;
2029*89a0ef05SAndroid Build Coastguard Worker   }
2030*89a0ef05SAndroid Build Coastguard Worker   if (mMapCompressQuality < 0 || mMapCompressQuality > 100) {
2031*89a0ef05SAndroid Build Coastguard Worker     ALOGE("invalid quality factor %d, expects in range [0-100]", mMapCompressQuality);
2032*89a0ef05SAndroid Build Coastguard Worker     return ERROR_JPEGR_INVALID_QUALITY_FACTOR;
2033*89a0ef05SAndroid Build Coastguard Worker   }
2034*89a0ef05SAndroid Build Coastguard Worker   if (!std::isfinite(mGamma) || mGamma <= 0.0f) {
2035*89a0ef05SAndroid Build Coastguard Worker     ALOGE("unsupported gainmap gamma %f, expects to be > 0", mGamma);
2036*89a0ef05SAndroid Build Coastguard Worker     return ERROR_JPEGR_INVALID_GAMMA;
2037*89a0ef05SAndroid Build Coastguard Worker   }
2038*89a0ef05SAndroid Build Coastguard Worker   if (mEncPreset != UHDR_USAGE_REALTIME && mEncPreset != UHDR_USAGE_BEST_QUALITY) {
2039*89a0ef05SAndroid Build Coastguard Worker     ALOGE("invalid preset %d, expects one of {UHDR_USAGE_REALTIME, UHDR_USAGE_BEST_QUALITY}",
2040*89a0ef05SAndroid Build Coastguard Worker           mEncPreset);
2041*89a0ef05SAndroid Build Coastguard Worker     return ERROR_JPEGR_INVALID_ENC_PRESET;
2042*89a0ef05SAndroid Build Coastguard Worker   }
2043*89a0ef05SAndroid Build Coastguard Worker   if (!std::isfinite(mMinContentBoost) || !std::isfinite(mMaxContentBoost) ||
2044*89a0ef05SAndroid Build Coastguard Worker       mMaxContentBoost < mMinContentBoost || mMinContentBoost <= 0.0f) {
2045*89a0ef05SAndroid Build Coastguard Worker     ALOGE("Invalid min boost / max boost configuration. Configured max boost %f, min boost %f",
2046*89a0ef05SAndroid Build Coastguard Worker           mMaxContentBoost, mMinContentBoost);
2047*89a0ef05SAndroid Build Coastguard Worker     return ERROR_JPEGR_INVALID_DISPLAY_BOOST;
2048*89a0ef05SAndroid Build Coastguard Worker   }
2049*89a0ef05SAndroid Build Coastguard Worker   if ((!std::isfinite(mTargetDispPeakBrightness) ||
2050*89a0ef05SAndroid Build Coastguard Worker        mTargetDispPeakBrightness < ultrahdr::kSdrWhiteNits ||
2051*89a0ef05SAndroid Build Coastguard Worker        mTargetDispPeakBrightness > ultrahdr::kPqMaxNits) &&
2052*89a0ef05SAndroid Build Coastguard Worker       mTargetDispPeakBrightness != -1.0f) {
2053*89a0ef05SAndroid Build Coastguard Worker     ALOGE("unexpected target display peak brightness nits %f, expects to be with in range [%f %f]",
2054*89a0ef05SAndroid Build Coastguard Worker           mTargetDispPeakBrightness, ultrahdr::kSdrWhiteNits, ultrahdr::kPqMaxNits);
2055*89a0ef05SAndroid Build Coastguard Worker     return ERROR_JPEGR_INVALID_TARGET_DISP_PEAK_BRIGHTNESS;
2056*89a0ef05SAndroid Build Coastguard Worker   }
2057*89a0ef05SAndroid Build Coastguard Worker   if (yuv420_image_ptr == nullptr) {
2058*89a0ef05SAndroid Build Coastguard Worker     return JPEGR_NO_ERROR;
2059*89a0ef05SAndroid Build Coastguard Worker   }
2060*89a0ef05SAndroid Build Coastguard Worker   if (yuv420_image_ptr->data == nullptr) {
2061*89a0ef05SAndroid Build Coastguard Worker     ALOGE("Received nullptr for uncompressed 420 image");
2062*89a0ef05SAndroid Build Coastguard Worker     return ERROR_JPEGR_BAD_PTR;
2063*89a0ef05SAndroid Build Coastguard Worker   }
2064*89a0ef05SAndroid Build Coastguard Worker   if (yuv420_image_ptr->luma_stride != 0 &&
2065*89a0ef05SAndroid Build Coastguard Worker       yuv420_image_ptr->luma_stride < yuv420_image_ptr->width) {
2066*89a0ef05SAndroid Build Coastguard Worker     ALOGE("Luma stride must not be smaller than width, stride=%u, width=%u",
2067*89a0ef05SAndroid Build Coastguard Worker           yuv420_image_ptr->luma_stride, yuv420_image_ptr->width);
2068*89a0ef05SAndroid Build Coastguard Worker     return ERROR_JPEGR_INVALID_STRIDE;
2069*89a0ef05SAndroid Build Coastguard Worker   }
2070*89a0ef05SAndroid Build Coastguard Worker   if (yuv420_image_ptr->chroma_data != nullptr &&
2071*89a0ef05SAndroid Build Coastguard Worker       yuv420_image_ptr->chroma_stride < yuv420_image_ptr->width / 2) {
2072*89a0ef05SAndroid Build Coastguard Worker     ALOGE("Chroma stride must not be smaller than (width / 2), stride=%u, width=%u",
2073*89a0ef05SAndroid Build Coastguard Worker           yuv420_image_ptr->chroma_stride, yuv420_image_ptr->width);
2074*89a0ef05SAndroid Build Coastguard Worker     return ERROR_JPEGR_INVALID_STRIDE;
2075*89a0ef05SAndroid Build Coastguard Worker   }
2076*89a0ef05SAndroid Build Coastguard Worker   if (p010_image_ptr->width != yuv420_image_ptr->width ||
2077*89a0ef05SAndroid Build Coastguard Worker       p010_image_ptr->height != yuv420_image_ptr->height) {
2078*89a0ef05SAndroid Build Coastguard Worker     ALOGE("Image resolutions mismatch: P010: %ux%u, YUV420: %ux%u", p010_image_ptr->width,
2079*89a0ef05SAndroid Build Coastguard Worker           p010_image_ptr->height, yuv420_image_ptr->width, yuv420_image_ptr->height);
2080*89a0ef05SAndroid Build Coastguard Worker     return ERROR_JPEGR_RESOLUTION_MISMATCH;
2081*89a0ef05SAndroid Build Coastguard Worker   }
2082*89a0ef05SAndroid Build Coastguard Worker   if (yuv420_image_ptr->colorGamut <= ULTRAHDR_COLORGAMUT_UNSPECIFIED ||
2083*89a0ef05SAndroid Build Coastguard Worker       yuv420_image_ptr->colorGamut > ULTRAHDR_COLORGAMUT_MAX) {
2084*89a0ef05SAndroid Build Coastguard Worker     ALOGE("Unrecognized 420 color gamut %d", yuv420_image_ptr->colorGamut);
2085*89a0ef05SAndroid Build Coastguard Worker     return ERROR_JPEGR_INVALID_COLORGAMUT;
2086*89a0ef05SAndroid Build Coastguard Worker   }
2087*89a0ef05SAndroid Build Coastguard Worker   return JPEGR_NO_ERROR;
2088*89a0ef05SAndroid Build Coastguard Worker }
2089*89a0ef05SAndroid Build Coastguard Worker 
areInputArgumentsValid(jr_uncompressed_ptr p010_image_ptr,jr_uncompressed_ptr yuv420_image_ptr,ultrahdr_transfer_function hdr_tf,jr_compressed_ptr dest_ptr,int quality)2090*89a0ef05SAndroid Build Coastguard Worker status_t JpegR::areInputArgumentsValid(jr_uncompressed_ptr p010_image_ptr,
2091*89a0ef05SAndroid Build Coastguard Worker                                        jr_uncompressed_ptr yuv420_image_ptr,
2092*89a0ef05SAndroid Build Coastguard Worker                                        ultrahdr_transfer_function hdr_tf,
2093*89a0ef05SAndroid Build Coastguard Worker                                        jr_compressed_ptr dest_ptr, int quality) {
2094*89a0ef05SAndroid Build Coastguard Worker   if (quality < 0 || quality > 100) {
2095*89a0ef05SAndroid Build Coastguard Worker     ALOGE("quality factor is out side range [0-100], quality factor : %d", quality);
2096*89a0ef05SAndroid Build Coastguard Worker     return ERROR_JPEGR_INVALID_QUALITY_FACTOR;
2097*89a0ef05SAndroid Build Coastguard Worker   }
2098*89a0ef05SAndroid Build Coastguard Worker   return areInputArgumentsValid(p010_image_ptr, yuv420_image_ptr, hdr_tf, dest_ptr);
2099*89a0ef05SAndroid Build Coastguard Worker }
2100*89a0ef05SAndroid Build Coastguard Worker 
map_legacy_ct_to_ct(ultrahdr::ultrahdr_transfer_function ct)2101*89a0ef05SAndroid Build Coastguard Worker uhdr_color_transfer_t map_legacy_ct_to_ct(ultrahdr::ultrahdr_transfer_function ct) {
2102*89a0ef05SAndroid Build Coastguard Worker   switch (ct) {
2103*89a0ef05SAndroid Build Coastguard Worker     case ultrahdr::ULTRAHDR_TF_HLG:
2104*89a0ef05SAndroid Build Coastguard Worker       return UHDR_CT_HLG;
2105*89a0ef05SAndroid Build Coastguard Worker     case ultrahdr::ULTRAHDR_TF_PQ:
2106*89a0ef05SAndroid Build Coastguard Worker       return UHDR_CT_PQ;
2107*89a0ef05SAndroid Build Coastguard Worker     case ultrahdr::ULTRAHDR_TF_LINEAR:
2108*89a0ef05SAndroid Build Coastguard Worker       return UHDR_CT_LINEAR;
2109*89a0ef05SAndroid Build Coastguard Worker     case ultrahdr::ULTRAHDR_TF_SRGB:
2110*89a0ef05SAndroid Build Coastguard Worker       return UHDR_CT_SRGB;
2111*89a0ef05SAndroid Build Coastguard Worker     default:
2112*89a0ef05SAndroid Build Coastguard Worker       return UHDR_CT_UNSPECIFIED;
2113*89a0ef05SAndroid Build Coastguard Worker   }
2114*89a0ef05SAndroid Build Coastguard Worker }
2115*89a0ef05SAndroid Build Coastguard Worker 
map_legacy_cg_to_cg(ultrahdr::ultrahdr_color_gamut cg)2116*89a0ef05SAndroid Build Coastguard Worker uhdr_color_gamut_t map_legacy_cg_to_cg(ultrahdr::ultrahdr_color_gamut cg) {
2117*89a0ef05SAndroid Build Coastguard Worker   switch (cg) {
2118*89a0ef05SAndroid Build Coastguard Worker     case ultrahdr::ULTRAHDR_COLORGAMUT_BT2100:
2119*89a0ef05SAndroid Build Coastguard Worker       return UHDR_CG_BT_2100;
2120*89a0ef05SAndroid Build Coastguard Worker     case ultrahdr::ULTRAHDR_COLORGAMUT_BT709:
2121*89a0ef05SAndroid Build Coastguard Worker       return UHDR_CG_BT_709;
2122*89a0ef05SAndroid Build Coastguard Worker     case ultrahdr::ULTRAHDR_COLORGAMUT_P3:
2123*89a0ef05SAndroid Build Coastguard Worker       return UHDR_CG_DISPLAY_P3;
2124*89a0ef05SAndroid Build Coastguard Worker     default:
2125*89a0ef05SAndroid Build Coastguard Worker       return UHDR_CG_UNSPECIFIED;
2126*89a0ef05SAndroid Build Coastguard Worker   }
2127*89a0ef05SAndroid Build Coastguard Worker }
2128*89a0ef05SAndroid Build Coastguard Worker 
map_cg_to_legacy_cg(uhdr_color_gamut_t cg)2129*89a0ef05SAndroid Build Coastguard Worker ultrahdr::ultrahdr_color_gamut map_cg_to_legacy_cg(uhdr_color_gamut_t cg) {
2130*89a0ef05SAndroid Build Coastguard Worker   switch (cg) {
2131*89a0ef05SAndroid Build Coastguard Worker     case UHDR_CG_BT_2100:
2132*89a0ef05SAndroid Build Coastguard Worker       return ultrahdr::ULTRAHDR_COLORGAMUT_BT2100;
2133*89a0ef05SAndroid Build Coastguard Worker     case UHDR_CG_BT_709:
2134*89a0ef05SAndroid Build Coastguard Worker       return ultrahdr::ULTRAHDR_COLORGAMUT_BT709;
2135*89a0ef05SAndroid Build Coastguard Worker     case UHDR_CG_DISPLAY_P3:
2136*89a0ef05SAndroid Build Coastguard Worker       return ultrahdr::ULTRAHDR_COLORGAMUT_P3;
2137*89a0ef05SAndroid Build Coastguard Worker     default:
2138*89a0ef05SAndroid Build Coastguard Worker       return ultrahdr::ULTRAHDR_COLORGAMUT_UNSPECIFIED;
2139*89a0ef05SAndroid Build Coastguard Worker   }
2140*89a0ef05SAndroid Build Coastguard Worker }
2141*89a0ef05SAndroid Build Coastguard Worker 
2142*89a0ef05SAndroid Build Coastguard Worker /* Encode API-0 */
encodeJPEGR(jr_uncompressed_ptr p010_image_ptr,ultrahdr_transfer_function hdr_tf,jr_compressed_ptr dest,int quality,jr_exif_ptr exif)2143*89a0ef05SAndroid Build Coastguard Worker status_t JpegR::encodeJPEGR(jr_uncompressed_ptr p010_image_ptr, ultrahdr_transfer_function hdr_tf,
2144*89a0ef05SAndroid Build Coastguard Worker                             jr_compressed_ptr dest, int quality, jr_exif_ptr exif) {
2145*89a0ef05SAndroid Build Coastguard Worker   // validate input arguments
2146*89a0ef05SAndroid Build Coastguard Worker   JPEGR_CHECK(areInputArgumentsValid(p010_image_ptr, nullptr, hdr_tf, dest, quality));
2147*89a0ef05SAndroid Build Coastguard Worker   if (exif != nullptr && exif->data == nullptr) {
2148*89a0ef05SAndroid Build Coastguard Worker     ALOGE("received nullptr for exif metadata");
2149*89a0ef05SAndroid Build Coastguard Worker     return ERROR_JPEGR_BAD_PTR;
2150*89a0ef05SAndroid Build Coastguard Worker   }
2151*89a0ef05SAndroid Build Coastguard Worker 
2152*89a0ef05SAndroid Build Coastguard Worker   // clean up input structure for later usage
2153*89a0ef05SAndroid Build Coastguard Worker   jpegr_uncompressed_struct p010_image = *p010_image_ptr;
2154*89a0ef05SAndroid Build Coastguard Worker   if (p010_image.luma_stride == 0) p010_image.luma_stride = p010_image.width;
2155*89a0ef05SAndroid Build Coastguard Worker   if (!p010_image.chroma_data) {
2156*89a0ef05SAndroid Build Coastguard Worker     uint16_t* data = reinterpret_cast<uint16_t*>(p010_image.data);
2157*89a0ef05SAndroid Build Coastguard Worker     p010_image.chroma_data = data + (size_t)p010_image.luma_stride * p010_image.height;
2158*89a0ef05SAndroid Build Coastguard Worker     p010_image.chroma_stride = p010_image.luma_stride;
2159*89a0ef05SAndroid Build Coastguard Worker   }
2160*89a0ef05SAndroid Build Coastguard Worker 
2161*89a0ef05SAndroid Build Coastguard Worker   uhdr_raw_image_t hdr_intent;
2162*89a0ef05SAndroid Build Coastguard Worker   hdr_intent.fmt = UHDR_IMG_FMT_24bppYCbCrP010;
2163*89a0ef05SAndroid Build Coastguard Worker   hdr_intent.cg = map_legacy_cg_to_cg(p010_image.colorGamut);
2164*89a0ef05SAndroid Build Coastguard Worker   hdr_intent.ct = map_legacy_ct_to_ct(hdr_tf);
2165*89a0ef05SAndroid Build Coastguard Worker   hdr_intent.range = p010_image.colorRange;
2166*89a0ef05SAndroid Build Coastguard Worker   hdr_intent.w = p010_image.width;
2167*89a0ef05SAndroid Build Coastguard Worker   hdr_intent.h = p010_image.height;
2168*89a0ef05SAndroid Build Coastguard Worker   hdr_intent.planes[UHDR_PLANE_Y] = p010_image.data;
2169*89a0ef05SAndroid Build Coastguard Worker   hdr_intent.stride[UHDR_PLANE_Y] = p010_image.luma_stride;
2170*89a0ef05SAndroid Build Coastguard Worker   hdr_intent.planes[UHDR_PLANE_UV] = p010_image.chroma_data;
2171*89a0ef05SAndroid Build Coastguard Worker   hdr_intent.stride[UHDR_PLANE_UV] = p010_image.chroma_stride;
2172*89a0ef05SAndroid Build Coastguard Worker   hdr_intent.planes[UHDR_PLANE_V] = nullptr;
2173*89a0ef05SAndroid Build Coastguard Worker   hdr_intent.stride[UHDR_PLANE_V] = 0;
2174*89a0ef05SAndroid Build Coastguard Worker 
2175*89a0ef05SAndroid Build Coastguard Worker   uhdr_compressed_image_t output;
2176*89a0ef05SAndroid Build Coastguard Worker   output.data = dest->data;
2177*89a0ef05SAndroid Build Coastguard Worker   output.data_sz = 0;
2178*89a0ef05SAndroid Build Coastguard Worker   output.capacity = dest->maxLength;
2179*89a0ef05SAndroid Build Coastguard Worker   output.cg = UHDR_CG_UNSPECIFIED;
2180*89a0ef05SAndroid Build Coastguard Worker   output.ct = UHDR_CT_UNSPECIFIED;
2181*89a0ef05SAndroid Build Coastguard Worker   output.range = UHDR_CR_UNSPECIFIED;
2182*89a0ef05SAndroid Build Coastguard Worker 
2183*89a0ef05SAndroid Build Coastguard Worker   uhdr_mem_block_t exifBlock;
2184*89a0ef05SAndroid Build Coastguard Worker   if (exif) {
2185*89a0ef05SAndroid Build Coastguard Worker     exifBlock.data = exif->data;
2186*89a0ef05SAndroid Build Coastguard Worker     exifBlock.data_sz = exifBlock.capacity = exif->length;
2187*89a0ef05SAndroid Build Coastguard Worker   }
2188*89a0ef05SAndroid Build Coastguard Worker 
2189*89a0ef05SAndroid Build Coastguard Worker   auto result = encodeJPEGR(&hdr_intent, &output, quality, exif ? &exifBlock : nullptr);
2190*89a0ef05SAndroid Build Coastguard Worker   if (result.error_code == UHDR_CODEC_OK) {
2191*89a0ef05SAndroid Build Coastguard Worker     dest->colorGamut = map_cg_to_legacy_cg(output.cg);
2192*89a0ef05SAndroid Build Coastguard Worker     dest->length = output.data_sz;
2193*89a0ef05SAndroid Build Coastguard Worker   }
2194*89a0ef05SAndroid Build Coastguard Worker 
2195*89a0ef05SAndroid Build Coastguard Worker   return result.error_code == UHDR_CODEC_OK ? JPEGR_NO_ERROR : JPEGR_UNKNOWN_ERROR;
2196*89a0ef05SAndroid Build Coastguard Worker }
2197*89a0ef05SAndroid Build Coastguard Worker 
2198*89a0ef05SAndroid Build Coastguard Worker /* Encode API-1 */
encodeJPEGR(jr_uncompressed_ptr p010_image_ptr,jr_uncompressed_ptr yuv420_image_ptr,ultrahdr_transfer_function hdr_tf,jr_compressed_ptr dest,int quality,jr_exif_ptr exif)2199*89a0ef05SAndroid Build Coastguard Worker status_t JpegR::encodeJPEGR(jr_uncompressed_ptr p010_image_ptr,
2200*89a0ef05SAndroid Build Coastguard Worker                             jr_uncompressed_ptr yuv420_image_ptr, ultrahdr_transfer_function hdr_tf,
2201*89a0ef05SAndroid Build Coastguard Worker                             jr_compressed_ptr dest, int quality, jr_exif_ptr exif) {
2202*89a0ef05SAndroid Build Coastguard Worker   // validate input arguments
2203*89a0ef05SAndroid Build Coastguard Worker   if (yuv420_image_ptr == nullptr) {
2204*89a0ef05SAndroid Build Coastguard Worker     ALOGE("received nullptr for uncompressed 420 image");
2205*89a0ef05SAndroid Build Coastguard Worker     return ERROR_JPEGR_BAD_PTR;
2206*89a0ef05SAndroid Build Coastguard Worker   }
2207*89a0ef05SAndroid Build Coastguard Worker   if (exif != nullptr && exif->data == nullptr) {
2208*89a0ef05SAndroid Build Coastguard Worker     ALOGE("received nullptr for exif metadata");
2209*89a0ef05SAndroid Build Coastguard Worker     return ERROR_JPEGR_BAD_PTR;
2210*89a0ef05SAndroid Build Coastguard Worker   }
2211*89a0ef05SAndroid Build Coastguard Worker   JPEGR_CHECK(areInputArgumentsValid(p010_image_ptr, yuv420_image_ptr, hdr_tf, dest, quality))
2212*89a0ef05SAndroid Build Coastguard Worker 
2213*89a0ef05SAndroid Build Coastguard Worker   // clean up input structure for later usage
2214*89a0ef05SAndroid Build Coastguard Worker   jpegr_uncompressed_struct p010_image = *p010_image_ptr;
2215*89a0ef05SAndroid Build Coastguard Worker   if (p010_image.luma_stride == 0) p010_image.luma_stride = p010_image.width;
2216*89a0ef05SAndroid Build Coastguard Worker   if (!p010_image.chroma_data) {
2217*89a0ef05SAndroid Build Coastguard Worker     uint16_t* data = reinterpret_cast<uint16_t*>(p010_image.data);
2218*89a0ef05SAndroid Build Coastguard Worker     p010_image.chroma_data = data + (size_t)p010_image.luma_stride * p010_image.height;
2219*89a0ef05SAndroid Build Coastguard Worker     p010_image.chroma_stride = p010_image.luma_stride;
2220*89a0ef05SAndroid Build Coastguard Worker   }
2221*89a0ef05SAndroid Build Coastguard Worker   uhdr_raw_image_t hdr_intent;
2222*89a0ef05SAndroid Build Coastguard Worker   hdr_intent.fmt = UHDR_IMG_FMT_24bppYCbCrP010;
2223*89a0ef05SAndroid Build Coastguard Worker   hdr_intent.cg = map_legacy_cg_to_cg(p010_image.colorGamut);
2224*89a0ef05SAndroid Build Coastguard Worker   hdr_intent.ct = map_legacy_ct_to_ct(hdr_tf);
2225*89a0ef05SAndroid Build Coastguard Worker   hdr_intent.range = p010_image.colorRange;
2226*89a0ef05SAndroid Build Coastguard Worker   hdr_intent.w = p010_image.width;
2227*89a0ef05SAndroid Build Coastguard Worker   hdr_intent.h = p010_image.height;
2228*89a0ef05SAndroid Build Coastguard Worker   hdr_intent.planes[UHDR_PLANE_Y] = p010_image.data;
2229*89a0ef05SAndroid Build Coastguard Worker   hdr_intent.stride[UHDR_PLANE_Y] = p010_image.luma_stride;
2230*89a0ef05SAndroid Build Coastguard Worker   hdr_intent.planes[UHDR_PLANE_UV] = p010_image.chroma_data;
2231*89a0ef05SAndroid Build Coastguard Worker   hdr_intent.stride[UHDR_PLANE_UV] = p010_image.chroma_stride;
2232*89a0ef05SAndroid Build Coastguard Worker   hdr_intent.planes[UHDR_PLANE_V] = nullptr;
2233*89a0ef05SAndroid Build Coastguard Worker   hdr_intent.stride[UHDR_PLANE_V] = 0;
2234*89a0ef05SAndroid Build Coastguard Worker 
2235*89a0ef05SAndroid Build Coastguard Worker   jpegr_uncompressed_struct yuv420_image = *yuv420_image_ptr;
2236*89a0ef05SAndroid Build Coastguard Worker   if (yuv420_image.luma_stride == 0) yuv420_image.luma_stride = yuv420_image.width;
2237*89a0ef05SAndroid Build Coastguard Worker   if (!yuv420_image.chroma_data) {
2238*89a0ef05SAndroid Build Coastguard Worker     uint8_t* data = reinterpret_cast<uint8_t*>(yuv420_image.data);
2239*89a0ef05SAndroid Build Coastguard Worker     yuv420_image.chroma_data = data + (size_t)yuv420_image.luma_stride * yuv420_image.height;
2240*89a0ef05SAndroid Build Coastguard Worker     yuv420_image.chroma_stride = yuv420_image.luma_stride >> 1;
2241*89a0ef05SAndroid Build Coastguard Worker   }
2242*89a0ef05SAndroid Build Coastguard Worker   uhdr_raw_image_t sdrRawImg;
2243*89a0ef05SAndroid Build Coastguard Worker   sdrRawImg.fmt = UHDR_IMG_FMT_12bppYCbCr420;
2244*89a0ef05SAndroid Build Coastguard Worker   sdrRawImg.cg = map_legacy_cg_to_cg(yuv420_image.colorGamut);
2245*89a0ef05SAndroid Build Coastguard Worker   sdrRawImg.ct = UHDR_CT_SRGB;
2246*89a0ef05SAndroid Build Coastguard Worker   sdrRawImg.range = yuv420_image.colorRange;
2247*89a0ef05SAndroid Build Coastguard Worker   sdrRawImg.w = yuv420_image.width;
2248*89a0ef05SAndroid Build Coastguard Worker   sdrRawImg.h = yuv420_image.height;
2249*89a0ef05SAndroid Build Coastguard Worker   sdrRawImg.planes[UHDR_PLANE_Y] = yuv420_image.data;
2250*89a0ef05SAndroid Build Coastguard Worker   sdrRawImg.stride[UHDR_PLANE_Y] = yuv420_image.luma_stride;
2251*89a0ef05SAndroid Build Coastguard Worker   sdrRawImg.planes[UHDR_PLANE_U] = yuv420_image.chroma_data;
2252*89a0ef05SAndroid Build Coastguard Worker   sdrRawImg.stride[UHDR_PLANE_U] = yuv420_image.chroma_stride;
2253*89a0ef05SAndroid Build Coastguard Worker   uint8_t* data = reinterpret_cast<uint8_t*>(yuv420_image.chroma_data);
2254*89a0ef05SAndroid Build Coastguard Worker   data += (yuv420_image.height * yuv420_image.chroma_stride) / 2;
2255*89a0ef05SAndroid Build Coastguard Worker   sdrRawImg.planes[UHDR_PLANE_V] = data;
2256*89a0ef05SAndroid Build Coastguard Worker   sdrRawImg.stride[UHDR_PLANE_V] = yuv420_image.chroma_stride;
2257*89a0ef05SAndroid Build Coastguard Worker   auto sdr_intent = convert_raw_input_to_ycbcr(&sdrRawImg);
2258*89a0ef05SAndroid Build Coastguard Worker 
2259*89a0ef05SAndroid Build Coastguard Worker   uhdr_compressed_image_t output;
2260*89a0ef05SAndroid Build Coastguard Worker   output.data = dest->data;
2261*89a0ef05SAndroid Build Coastguard Worker   output.data_sz = 0;
2262*89a0ef05SAndroid Build Coastguard Worker   output.capacity = dest->maxLength;
2263*89a0ef05SAndroid Build Coastguard Worker   output.cg = UHDR_CG_UNSPECIFIED;
2264*89a0ef05SAndroid Build Coastguard Worker   output.ct = UHDR_CT_UNSPECIFIED;
2265*89a0ef05SAndroid Build Coastguard Worker   output.range = UHDR_CR_UNSPECIFIED;
2266*89a0ef05SAndroid Build Coastguard Worker 
2267*89a0ef05SAndroid Build Coastguard Worker   uhdr_mem_block_t exifBlock;
2268*89a0ef05SAndroid Build Coastguard Worker   if (exif) {
2269*89a0ef05SAndroid Build Coastguard Worker     exifBlock.data = exif->data;
2270*89a0ef05SAndroid Build Coastguard Worker     exifBlock.data_sz = exifBlock.capacity = exif->length;
2271*89a0ef05SAndroid Build Coastguard Worker   }
2272*89a0ef05SAndroid Build Coastguard Worker 
2273*89a0ef05SAndroid Build Coastguard Worker   auto result =
2274*89a0ef05SAndroid Build Coastguard Worker       encodeJPEGR(&hdr_intent, sdr_intent.get(), &output, quality, exif ? &exifBlock : nullptr);
2275*89a0ef05SAndroid Build Coastguard Worker   if (result.error_code == UHDR_CODEC_OK) {
2276*89a0ef05SAndroid Build Coastguard Worker     dest->colorGamut = map_cg_to_legacy_cg(output.cg);
2277*89a0ef05SAndroid Build Coastguard Worker     dest->length = output.data_sz;
2278*89a0ef05SAndroid Build Coastguard Worker   }
2279*89a0ef05SAndroid Build Coastguard Worker 
2280*89a0ef05SAndroid Build Coastguard Worker   return result.error_code == UHDR_CODEC_OK ? JPEGR_NO_ERROR : JPEGR_UNKNOWN_ERROR;
2281*89a0ef05SAndroid Build Coastguard Worker }
2282*89a0ef05SAndroid Build Coastguard Worker 
2283*89a0ef05SAndroid Build Coastguard Worker /* Encode API-2 */
encodeJPEGR(jr_uncompressed_ptr p010_image_ptr,jr_uncompressed_ptr yuv420_image_ptr,jr_compressed_ptr yuv420jpg_image_ptr,ultrahdr_transfer_function hdr_tf,jr_compressed_ptr dest)2284*89a0ef05SAndroid Build Coastguard Worker status_t JpegR::encodeJPEGR(jr_uncompressed_ptr p010_image_ptr,
2285*89a0ef05SAndroid Build Coastguard Worker                             jr_uncompressed_ptr yuv420_image_ptr,
2286*89a0ef05SAndroid Build Coastguard Worker                             jr_compressed_ptr yuv420jpg_image_ptr,
2287*89a0ef05SAndroid Build Coastguard Worker                             ultrahdr_transfer_function hdr_tf, jr_compressed_ptr dest) {
2288*89a0ef05SAndroid Build Coastguard Worker   // validate input arguments
2289*89a0ef05SAndroid Build Coastguard Worker   if (yuv420_image_ptr == nullptr) {
2290*89a0ef05SAndroid Build Coastguard Worker     ALOGE("received nullptr for uncompressed 420 image");
2291*89a0ef05SAndroid Build Coastguard Worker     return ERROR_JPEGR_BAD_PTR;
2292*89a0ef05SAndroid Build Coastguard Worker   }
2293*89a0ef05SAndroid Build Coastguard Worker   if (yuv420jpg_image_ptr == nullptr || yuv420jpg_image_ptr->data == nullptr) {
2294*89a0ef05SAndroid Build Coastguard Worker     ALOGE("received nullptr for compressed jpeg image");
2295*89a0ef05SAndroid Build Coastguard Worker     return ERROR_JPEGR_BAD_PTR;
2296*89a0ef05SAndroid Build Coastguard Worker   }
2297*89a0ef05SAndroid Build Coastguard Worker   JPEGR_CHECK(areInputArgumentsValid(p010_image_ptr, yuv420_image_ptr, hdr_tf, dest))
2298*89a0ef05SAndroid Build Coastguard Worker 
2299*89a0ef05SAndroid Build Coastguard Worker   // clean up input structure for later usage
2300*89a0ef05SAndroid Build Coastguard Worker   jpegr_uncompressed_struct p010_image = *p010_image_ptr;
2301*89a0ef05SAndroid Build Coastguard Worker   if (p010_image.luma_stride == 0) p010_image.luma_stride = p010_image.width;
2302*89a0ef05SAndroid Build Coastguard Worker   if (!p010_image.chroma_data) {
2303*89a0ef05SAndroid Build Coastguard Worker     uint16_t* data = reinterpret_cast<uint16_t*>(p010_image.data);
2304*89a0ef05SAndroid Build Coastguard Worker     p010_image.chroma_data = data + (size_t)p010_image.luma_stride * p010_image.height;
2305*89a0ef05SAndroid Build Coastguard Worker     p010_image.chroma_stride = p010_image.luma_stride;
2306*89a0ef05SAndroid Build Coastguard Worker   }
2307*89a0ef05SAndroid Build Coastguard Worker   uhdr_raw_image_t hdr_intent;
2308*89a0ef05SAndroid Build Coastguard Worker   hdr_intent.fmt = UHDR_IMG_FMT_24bppYCbCrP010;
2309*89a0ef05SAndroid Build Coastguard Worker   hdr_intent.cg = map_legacy_cg_to_cg(p010_image.colorGamut);
2310*89a0ef05SAndroid Build Coastguard Worker   hdr_intent.ct = map_legacy_ct_to_ct(hdr_tf);
2311*89a0ef05SAndroid Build Coastguard Worker   hdr_intent.range = p010_image.colorRange;
2312*89a0ef05SAndroid Build Coastguard Worker   hdr_intent.w = p010_image.width;
2313*89a0ef05SAndroid Build Coastguard Worker   hdr_intent.h = p010_image.height;
2314*89a0ef05SAndroid Build Coastguard Worker   hdr_intent.planes[UHDR_PLANE_Y] = p010_image.data;
2315*89a0ef05SAndroid Build Coastguard Worker   hdr_intent.stride[UHDR_PLANE_Y] = p010_image.luma_stride;
2316*89a0ef05SAndroid Build Coastguard Worker   hdr_intent.planes[UHDR_PLANE_UV] = p010_image.chroma_data;
2317*89a0ef05SAndroid Build Coastguard Worker   hdr_intent.stride[UHDR_PLANE_UV] = p010_image.chroma_stride;
2318*89a0ef05SAndroid Build Coastguard Worker   hdr_intent.planes[UHDR_PLANE_V] = nullptr;
2319*89a0ef05SAndroid Build Coastguard Worker   hdr_intent.stride[UHDR_PLANE_V] = 0;
2320*89a0ef05SAndroid Build Coastguard Worker 
2321*89a0ef05SAndroid Build Coastguard Worker   jpegr_uncompressed_struct yuv420_image = *yuv420_image_ptr;
2322*89a0ef05SAndroid Build Coastguard Worker   if (yuv420_image.luma_stride == 0) yuv420_image.luma_stride = yuv420_image.width;
2323*89a0ef05SAndroid Build Coastguard Worker   if (!yuv420_image.chroma_data) {
2324*89a0ef05SAndroid Build Coastguard Worker     uint8_t* data = reinterpret_cast<uint8_t*>(yuv420_image.data);
2325*89a0ef05SAndroid Build Coastguard Worker     yuv420_image.chroma_data = data + (size_t)yuv420_image.luma_stride * p010_image.height;
2326*89a0ef05SAndroid Build Coastguard Worker     yuv420_image.chroma_stride = yuv420_image.luma_stride >> 1;
2327*89a0ef05SAndroid Build Coastguard Worker   }
2328*89a0ef05SAndroid Build Coastguard Worker   uhdr_raw_image_t sdrRawImg;
2329*89a0ef05SAndroid Build Coastguard Worker   sdrRawImg.fmt = UHDR_IMG_FMT_12bppYCbCr420;
2330*89a0ef05SAndroid Build Coastguard Worker   sdrRawImg.cg = map_legacy_cg_to_cg(yuv420_image.colorGamut);
2331*89a0ef05SAndroid Build Coastguard Worker   sdrRawImg.ct = UHDR_CT_SRGB;
2332*89a0ef05SAndroid Build Coastguard Worker   sdrRawImg.range = yuv420_image.colorRange;
2333*89a0ef05SAndroid Build Coastguard Worker   sdrRawImg.w = yuv420_image.width;
2334*89a0ef05SAndroid Build Coastguard Worker   sdrRawImg.h = yuv420_image.height;
2335*89a0ef05SAndroid Build Coastguard Worker   sdrRawImg.planes[UHDR_PLANE_Y] = yuv420_image.data;
2336*89a0ef05SAndroid Build Coastguard Worker   sdrRawImg.stride[UHDR_PLANE_Y] = yuv420_image.luma_stride;
2337*89a0ef05SAndroid Build Coastguard Worker   sdrRawImg.planes[UHDR_PLANE_U] = yuv420_image.chroma_data;
2338*89a0ef05SAndroid Build Coastguard Worker   sdrRawImg.stride[UHDR_PLANE_U] = yuv420_image.chroma_stride;
2339*89a0ef05SAndroid Build Coastguard Worker   uint8_t* data = reinterpret_cast<uint8_t*>(yuv420_image.chroma_data);
2340*89a0ef05SAndroid Build Coastguard Worker   data += (yuv420_image.height * yuv420_image.chroma_stride) / 2;
2341*89a0ef05SAndroid Build Coastguard Worker   sdrRawImg.planes[UHDR_PLANE_V] = data;
2342*89a0ef05SAndroid Build Coastguard Worker   sdrRawImg.stride[UHDR_PLANE_V] = yuv420_image.chroma_stride;
2343*89a0ef05SAndroid Build Coastguard Worker   auto sdr_intent = convert_raw_input_to_ycbcr(&sdrRawImg);
2344*89a0ef05SAndroid Build Coastguard Worker 
2345*89a0ef05SAndroid Build Coastguard Worker   uhdr_compressed_image_t input;
2346*89a0ef05SAndroid Build Coastguard Worker   input.data = yuv420jpg_image_ptr->data;
2347*89a0ef05SAndroid Build Coastguard Worker   input.data_sz = yuv420jpg_image_ptr->length;
2348*89a0ef05SAndroid Build Coastguard Worker   input.capacity = yuv420jpg_image_ptr->maxLength;
2349*89a0ef05SAndroid Build Coastguard Worker   input.cg = map_legacy_cg_to_cg(yuv420jpg_image_ptr->colorGamut);
2350*89a0ef05SAndroid Build Coastguard Worker   input.ct = UHDR_CT_UNSPECIFIED;
2351*89a0ef05SAndroid Build Coastguard Worker   input.range = UHDR_CR_UNSPECIFIED;
2352*89a0ef05SAndroid Build Coastguard Worker 
2353*89a0ef05SAndroid Build Coastguard Worker   uhdr_compressed_image_t output;
2354*89a0ef05SAndroid Build Coastguard Worker   output.data = dest->data;
2355*89a0ef05SAndroid Build Coastguard Worker   output.data_sz = 0;
2356*89a0ef05SAndroid Build Coastguard Worker   output.capacity = dest->maxLength;
2357*89a0ef05SAndroid Build Coastguard Worker   output.cg = UHDR_CG_UNSPECIFIED;
2358*89a0ef05SAndroid Build Coastguard Worker   output.ct = UHDR_CT_UNSPECIFIED;
2359*89a0ef05SAndroid Build Coastguard Worker   output.range = UHDR_CR_UNSPECIFIED;
2360*89a0ef05SAndroid Build Coastguard Worker 
2361*89a0ef05SAndroid Build Coastguard Worker   auto result = encodeJPEGR(&hdr_intent, sdr_intent.get(), &input, &output);
2362*89a0ef05SAndroid Build Coastguard Worker   if (result.error_code == UHDR_CODEC_OK) {
2363*89a0ef05SAndroid Build Coastguard Worker     dest->colorGamut = map_cg_to_legacy_cg(output.cg);
2364*89a0ef05SAndroid Build Coastguard Worker     dest->length = output.data_sz;
2365*89a0ef05SAndroid Build Coastguard Worker   }
2366*89a0ef05SAndroid Build Coastguard Worker 
2367*89a0ef05SAndroid Build Coastguard Worker   return result.error_code == UHDR_CODEC_OK ? JPEGR_NO_ERROR : JPEGR_UNKNOWN_ERROR;
2368*89a0ef05SAndroid Build Coastguard Worker }
2369*89a0ef05SAndroid Build Coastguard Worker 
2370*89a0ef05SAndroid Build Coastguard Worker /* Encode API-3 */
encodeJPEGR(jr_uncompressed_ptr p010_image_ptr,jr_compressed_ptr yuv420jpg_image_ptr,ultrahdr_transfer_function hdr_tf,jr_compressed_ptr dest)2371*89a0ef05SAndroid Build Coastguard Worker status_t JpegR::encodeJPEGR(jr_uncompressed_ptr p010_image_ptr,
2372*89a0ef05SAndroid Build Coastguard Worker                             jr_compressed_ptr yuv420jpg_image_ptr,
2373*89a0ef05SAndroid Build Coastguard Worker                             ultrahdr_transfer_function hdr_tf, jr_compressed_ptr dest) {
2374*89a0ef05SAndroid Build Coastguard Worker   // validate input arguments
2375*89a0ef05SAndroid Build Coastguard Worker   if (yuv420jpg_image_ptr == nullptr || yuv420jpg_image_ptr->data == nullptr) {
2376*89a0ef05SAndroid Build Coastguard Worker     ALOGE("received nullptr for compressed jpeg image");
2377*89a0ef05SAndroid Build Coastguard Worker     return ERROR_JPEGR_BAD_PTR;
2378*89a0ef05SAndroid Build Coastguard Worker   }
2379*89a0ef05SAndroid Build Coastguard Worker   JPEGR_CHECK(areInputArgumentsValid(p010_image_ptr, nullptr, hdr_tf, dest))
2380*89a0ef05SAndroid Build Coastguard Worker 
2381*89a0ef05SAndroid Build Coastguard Worker   // clean up input structure for later usage
2382*89a0ef05SAndroid Build Coastguard Worker   jpegr_uncompressed_struct p010_image = *p010_image_ptr;
2383*89a0ef05SAndroid Build Coastguard Worker   if (p010_image.luma_stride == 0) p010_image.luma_stride = p010_image.width;
2384*89a0ef05SAndroid Build Coastguard Worker   if (!p010_image.chroma_data) {
2385*89a0ef05SAndroid Build Coastguard Worker     uint16_t* data = reinterpret_cast<uint16_t*>(p010_image.data);
2386*89a0ef05SAndroid Build Coastguard Worker     p010_image.chroma_data = data + (size_t)p010_image.luma_stride * p010_image.height;
2387*89a0ef05SAndroid Build Coastguard Worker     p010_image.chroma_stride = p010_image.luma_stride;
2388*89a0ef05SAndroid Build Coastguard Worker   }
2389*89a0ef05SAndroid Build Coastguard Worker   uhdr_raw_image_t hdr_intent;
2390*89a0ef05SAndroid Build Coastguard Worker   hdr_intent.fmt = UHDR_IMG_FMT_24bppYCbCrP010;
2391*89a0ef05SAndroid Build Coastguard Worker   hdr_intent.cg = map_legacy_cg_to_cg(p010_image.colorGamut);
2392*89a0ef05SAndroid Build Coastguard Worker   hdr_intent.ct = map_legacy_ct_to_ct(hdr_tf);
2393*89a0ef05SAndroid Build Coastguard Worker   hdr_intent.range = p010_image.colorRange;
2394*89a0ef05SAndroid Build Coastguard Worker   hdr_intent.w = p010_image.width;
2395*89a0ef05SAndroid Build Coastguard Worker   hdr_intent.h = p010_image.height;
2396*89a0ef05SAndroid Build Coastguard Worker   hdr_intent.planes[UHDR_PLANE_Y] = p010_image.data;
2397*89a0ef05SAndroid Build Coastguard Worker   hdr_intent.stride[UHDR_PLANE_Y] = p010_image.luma_stride;
2398*89a0ef05SAndroid Build Coastguard Worker   hdr_intent.planes[UHDR_PLANE_UV] = p010_image.chroma_data;
2399*89a0ef05SAndroid Build Coastguard Worker   hdr_intent.stride[UHDR_PLANE_UV] = p010_image.chroma_stride;
2400*89a0ef05SAndroid Build Coastguard Worker   hdr_intent.planes[UHDR_PLANE_V] = nullptr;
2401*89a0ef05SAndroid Build Coastguard Worker   hdr_intent.stride[UHDR_PLANE_V] = 0;
2402*89a0ef05SAndroid Build Coastguard Worker 
2403*89a0ef05SAndroid Build Coastguard Worker   uhdr_compressed_image_t input;
2404*89a0ef05SAndroid Build Coastguard Worker   input.data = yuv420jpg_image_ptr->data;
2405*89a0ef05SAndroid Build Coastguard Worker   input.data_sz = yuv420jpg_image_ptr->length;
2406*89a0ef05SAndroid Build Coastguard Worker   input.capacity = yuv420jpg_image_ptr->maxLength;
2407*89a0ef05SAndroid Build Coastguard Worker   input.cg = map_legacy_cg_to_cg(yuv420jpg_image_ptr->colorGamut);
2408*89a0ef05SAndroid Build Coastguard Worker   input.ct = UHDR_CT_UNSPECIFIED;
2409*89a0ef05SAndroid Build Coastguard Worker   input.range = UHDR_CR_UNSPECIFIED;
2410*89a0ef05SAndroid Build Coastguard Worker 
2411*89a0ef05SAndroid Build Coastguard Worker   uhdr_compressed_image_t output;
2412*89a0ef05SAndroid Build Coastguard Worker   output.data = dest->data;
2413*89a0ef05SAndroid Build Coastguard Worker   output.data_sz = 0;
2414*89a0ef05SAndroid Build Coastguard Worker   output.capacity = dest->maxLength;
2415*89a0ef05SAndroid Build Coastguard Worker   output.cg = UHDR_CG_UNSPECIFIED;
2416*89a0ef05SAndroid Build Coastguard Worker   output.ct = UHDR_CT_UNSPECIFIED;
2417*89a0ef05SAndroid Build Coastguard Worker   output.range = UHDR_CR_UNSPECIFIED;
2418*89a0ef05SAndroid Build Coastguard Worker 
2419*89a0ef05SAndroid Build Coastguard Worker   auto result = encodeJPEGR(&hdr_intent, &input, &output);
2420*89a0ef05SAndroid Build Coastguard Worker   if (result.error_code == UHDR_CODEC_OK) {
2421*89a0ef05SAndroid Build Coastguard Worker     dest->colorGamut = map_cg_to_legacy_cg(output.cg);
2422*89a0ef05SAndroid Build Coastguard Worker     dest->length = output.data_sz;
2423*89a0ef05SAndroid Build Coastguard Worker   }
2424*89a0ef05SAndroid Build Coastguard Worker 
2425*89a0ef05SAndroid Build Coastguard Worker   return result.error_code == UHDR_CODEC_OK ? JPEGR_NO_ERROR : JPEGR_UNKNOWN_ERROR;
2426*89a0ef05SAndroid Build Coastguard Worker }
2427*89a0ef05SAndroid Build Coastguard Worker 
2428*89a0ef05SAndroid Build Coastguard Worker /* Encode API-4 */
encodeJPEGR(jr_compressed_ptr yuv420jpg_image_ptr,jr_compressed_ptr gainmapjpg_image_ptr,ultrahdr_metadata_ptr metadata,jr_compressed_ptr dest)2429*89a0ef05SAndroid Build Coastguard Worker status_t JpegR::encodeJPEGR(jr_compressed_ptr yuv420jpg_image_ptr,
2430*89a0ef05SAndroid Build Coastguard Worker                             jr_compressed_ptr gainmapjpg_image_ptr, ultrahdr_metadata_ptr metadata,
2431*89a0ef05SAndroid Build Coastguard Worker                             jr_compressed_ptr dest) {
2432*89a0ef05SAndroid Build Coastguard Worker   if (yuv420jpg_image_ptr == nullptr || yuv420jpg_image_ptr->data == nullptr) {
2433*89a0ef05SAndroid Build Coastguard Worker     ALOGE("received nullptr for compressed jpeg image");
2434*89a0ef05SAndroid Build Coastguard Worker     return ERROR_JPEGR_BAD_PTR;
2435*89a0ef05SAndroid Build Coastguard Worker   }
2436*89a0ef05SAndroid Build Coastguard Worker   if (gainmapjpg_image_ptr == nullptr || gainmapjpg_image_ptr->data == nullptr) {
2437*89a0ef05SAndroid Build Coastguard Worker     ALOGE("received nullptr for compressed gain map");
2438*89a0ef05SAndroid Build Coastguard Worker     return ERROR_JPEGR_BAD_PTR;
2439*89a0ef05SAndroid Build Coastguard Worker   }
2440*89a0ef05SAndroid Build Coastguard Worker   if (dest == nullptr || dest->data == nullptr) {
2441*89a0ef05SAndroid Build Coastguard Worker     ALOGE("received nullptr for destination");
2442*89a0ef05SAndroid Build Coastguard Worker     return ERROR_JPEGR_BAD_PTR;
2443*89a0ef05SAndroid Build Coastguard Worker   }
2444*89a0ef05SAndroid Build Coastguard Worker 
2445*89a0ef05SAndroid Build Coastguard Worker   uhdr_compressed_image_t input;
2446*89a0ef05SAndroid Build Coastguard Worker   input.data = yuv420jpg_image_ptr->data;
2447*89a0ef05SAndroid Build Coastguard Worker   input.data_sz = yuv420jpg_image_ptr->length;
2448*89a0ef05SAndroid Build Coastguard Worker   input.capacity = yuv420jpg_image_ptr->maxLength;
2449*89a0ef05SAndroid Build Coastguard Worker   input.cg = map_legacy_cg_to_cg(yuv420jpg_image_ptr->colorGamut);
2450*89a0ef05SAndroid Build Coastguard Worker   input.ct = UHDR_CT_UNSPECIFIED;
2451*89a0ef05SAndroid Build Coastguard Worker   input.range = UHDR_CR_UNSPECIFIED;
2452*89a0ef05SAndroid Build Coastguard Worker 
2453*89a0ef05SAndroid Build Coastguard Worker   uhdr_compressed_image_t gainmap;
2454*89a0ef05SAndroid Build Coastguard Worker   gainmap.data = gainmapjpg_image_ptr->data;
2455*89a0ef05SAndroid Build Coastguard Worker   gainmap.data_sz = gainmapjpg_image_ptr->length;
2456*89a0ef05SAndroid Build Coastguard Worker   gainmap.capacity = gainmapjpg_image_ptr->maxLength;
2457*89a0ef05SAndroid Build Coastguard Worker   gainmap.cg = UHDR_CG_UNSPECIFIED;
2458*89a0ef05SAndroid Build Coastguard Worker   gainmap.ct = UHDR_CT_UNSPECIFIED;
2459*89a0ef05SAndroid Build Coastguard Worker   gainmap.range = UHDR_CR_UNSPECIFIED;
2460*89a0ef05SAndroid Build Coastguard Worker 
2461*89a0ef05SAndroid Build Coastguard Worker   uhdr_compressed_image_t output;
2462*89a0ef05SAndroid Build Coastguard Worker   output.data = dest->data;
2463*89a0ef05SAndroid Build Coastguard Worker   output.data_sz = 0;
2464*89a0ef05SAndroid Build Coastguard Worker   output.capacity = dest->maxLength;
2465*89a0ef05SAndroid Build Coastguard Worker   output.cg = UHDR_CG_UNSPECIFIED;
2466*89a0ef05SAndroid Build Coastguard Worker   output.ct = UHDR_CT_UNSPECIFIED;
2467*89a0ef05SAndroid Build Coastguard Worker   output.range = UHDR_CR_UNSPECIFIED;
2468*89a0ef05SAndroid Build Coastguard Worker 
2469*89a0ef05SAndroid Build Coastguard Worker   uhdr_gainmap_metadata_ext_t meta;
2470*89a0ef05SAndroid Build Coastguard Worker   meta.version = metadata->version;
2471*89a0ef05SAndroid Build Coastguard Worker   meta.hdr_capacity_max = metadata->hdrCapacityMax;
2472*89a0ef05SAndroid Build Coastguard Worker   meta.hdr_capacity_min = metadata->hdrCapacityMin;
2473*89a0ef05SAndroid Build Coastguard Worker   meta.gamma = metadata->gamma;
2474*89a0ef05SAndroid Build Coastguard Worker   meta.offset_sdr = metadata->offsetSdr;
2475*89a0ef05SAndroid Build Coastguard Worker   meta.offset_hdr = metadata->offsetHdr;
2476*89a0ef05SAndroid Build Coastguard Worker   meta.max_content_boost = metadata->maxContentBoost;
2477*89a0ef05SAndroid Build Coastguard Worker   meta.min_content_boost = metadata->minContentBoost;
2478*89a0ef05SAndroid Build Coastguard Worker 
2479*89a0ef05SAndroid Build Coastguard Worker   auto result = encodeJPEGR(&input, &gainmap, &meta, &output);
2480*89a0ef05SAndroid Build Coastguard Worker   if (result.error_code == UHDR_CODEC_OK) {
2481*89a0ef05SAndroid Build Coastguard Worker     dest->colorGamut = map_cg_to_legacy_cg(output.cg);
2482*89a0ef05SAndroid Build Coastguard Worker     dest->length = output.data_sz;
2483*89a0ef05SAndroid Build Coastguard Worker   }
2484*89a0ef05SAndroid Build Coastguard Worker 
2485*89a0ef05SAndroid Build Coastguard Worker   return result.error_code == UHDR_CODEC_OK ? JPEGR_NO_ERROR : JPEGR_UNKNOWN_ERROR;
2486*89a0ef05SAndroid Build Coastguard Worker }
2487*89a0ef05SAndroid Build Coastguard Worker 
2488*89a0ef05SAndroid Build Coastguard Worker /* Decode API */
getJPEGRInfo(jr_compressed_ptr jpegr_image_ptr,jr_info_ptr jpegr_image_info_ptr)2489*89a0ef05SAndroid Build Coastguard Worker status_t JpegR::getJPEGRInfo(jr_compressed_ptr jpegr_image_ptr, jr_info_ptr jpegr_image_info_ptr) {
2490*89a0ef05SAndroid Build Coastguard Worker   if (jpegr_image_ptr == nullptr || jpegr_image_ptr->data == nullptr) {
2491*89a0ef05SAndroid Build Coastguard Worker     ALOGE("received nullptr for compressed jpegr image");
2492*89a0ef05SAndroid Build Coastguard Worker     return ERROR_JPEGR_BAD_PTR;
2493*89a0ef05SAndroid Build Coastguard Worker   }
2494*89a0ef05SAndroid Build Coastguard Worker   if (jpegr_image_info_ptr == nullptr) {
2495*89a0ef05SAndroid Build Coastguard Worker     ALOGE("received nullptr for compressed jpegr info struct");
2496*89a0ef05SAndroid Build Coastguard Worker     return ERROR_JPEGR_BAD_PTR;
2497*89a0ef05SAndroid Build Coastguard Worker   }
2498*89a0ef05SAndroid Build Coastguard Worker 
2499*89a0ef05SAndroid Build Coastguard Worker   uhdr_compressed_image_t input;
2500*89a0ef05SAndroid Build Coastguard Worker   input.data = jpegr_image_ptr->data;
2501*89a0ef05SAndroid Build Coastguard Worker   input.data_sz = jpegr_image_ptr->length;
2502*89a0ef05SAndroid Build Coastguard Worker   input.capacity = jpegr_image_ptr->maxLength;
2503*89a0ef05SAndroid Build Coastguard Worker   input.cg = map_legacy_cg_to_cg(jpegr_image_ptr->colorGamut);
2504*89a0ef05SAndroid Build Coastguard Worker   input.ct = UHDR_CT_UNSPECIFIED;
2505*89a0ef05SAndroid Build Coastguard Worker   input.range = UHDR_CR_UNSPECIFIED;
2506*89a0ef05SAndroid Build Coastguard Worker 
2507*89a0ef05SAndroid Build Coastguard Worker   auto result = getJPEGRInfo(&input, jpegr_image_info_ptr);
2508*89a0ef05SAndroid Build Coastguard Worker 
2509*89a0ef05SAndroid Build Coastguard Worker   return result.error_code == UHDR_CODEC_OK ? JPEGR_NO_ERROR : JPEGR_UNKNOWN_ERROR;
2510*89a0ef05SAndroid Build Coastguard Worker }
2511*89a0ef05SAndroid Build Coastguard Worker 
decodeJPEGR(jr_compressed_ptr jpegr_image_ptr,jr_uncompressed_ptr dest,float max_display_boost,jr_exif_ptr exif,ultrahdr_output_format output_format,jr_uncompressed_ptr gainmap_image_ptr,ultrahdr_metadata_ptr metadata)2512*89a0ef05SAndroid Build Coastguard Worker status_t JpegR::decodeJPEGR(jr_compressed_ptr jpegr_image_ptr, jr_uncompressed_ptr dest,
2513*89a0ef05SAndroid Build Coastguard Worker                             float max_display_boost, jr_exif_ptr exif,
2514*89a0ef05SAndroid Build Coastguard Worker                             ultrahdr_output_format output_format,
2515*89a0ef05SAndroid Build Coastguard Worker                             jr_uncompressed_ptr gainmap_image_ptr, ultrahdr_metadata_ptr metadata) {
2516*89a0ef05SAndroid Build Coastguard Worker   if (jpegr_image_ptr == nullptr || jpegr_image_ptr->data == nullptr) {
2517*89a0ef05SAndroid Build Coastguard Worker     ALOGE("received nullptr for compressed jpegr image");
2518*89a0ef05SAndroid Build Coastguard Worker     return ERROR_JPEGR_BAD_PTR;
2519*89a0ef05SAndroid Build Coastguard Worker   }
2520*89a0ef05SAndroid Build Coastguard Worker   if (dest == nullptr || dest->data == nullptr) {
2521*89a0ef05SAndroid Build Coastguard Worker     ALOGE("received nullptr for dest image");
2522*89a0ef05SAndroid Build Coastguard Worker     return ERROR_JPEGR_BAD_PTR;
2523*89a0ef05SAndroid Build Coastguard Worker   }
2524*89a0ef05SAndroid Build Coastguard Worker   if (max_display_boost < 1.0f) {
2525*89a0ef05SAndroid Build Coastguard Worker     ALOGE("received bad value for max_display_boost %f", max_display_boost);
2526*89a0ef05SAndroid Build Coastguard Worker     return ERROR_JPEGR_INVALID_DISPLAY_BOOST;
2527*89a0ef05SAndroid Build Coastguard Worker   }
2528*89a0ef05SAndroid Build Coastguard Worker   if (exif != nullptr && exif->data == nullptr) {
2529*89a0ef05SAndroid Build Coastguard Worker     ALOGE("received nullptr address for exif data");
2530*89a0ef05SAndroid Build Coastguard Worker     return ERROR_JPEGR_BAD_PTR;
2531*89a0ef05SAndroid Build Coastguard Worker   }
2532*89a0ef05SAndroid Build Coastguard Worker   if (gainmap_image_ptr != nullptr && gainmap_image_ptr->data == nullptr) {
2533*89a0ef05SAndroid Build Coastguard Worker     ALOGE("received nullptr address for gainmap data");
2534*89a0ef05SAndroid Build Coastguard Worker     return ERROR_JPEGR_BAD_PTR;
2535*89a0ef05SAndroid Build Coastguard Worker   }
2536*89a0ef05SAndroid Build Coastguard Worker   if (output_format <= ULTRAHDR_OUTPUT_UNSPECIFIED || output_format > ULTRAHDR_OUTPUT_MAX) {
2537*89a0ef05SAndroid Build Coastguard Worker     ALOGE("received bad value for output format %d", output_format);
2538*89a0ef05SAndroid Build Coastguard Worker     return ERROR_JPEGR_INVALID_OUTPUT_FORMAT;
2539*89a0ef05SAndroid Build Coastguard Worker   }
2540*89a0ef05SAndroid Build Coastguard Worker 
2541*89a0ef05SAndroid Build Coastguard Worker   uhdr_color_transfer_t ct;
2542*89a0ef05SAndroid Build Coastguard Worker   uhdr_img_fmt fmt;
2543*89a0ef05SAndroid Build Coastguard Worker   if (output_format == ULTRAHDR_OUTPUT_HDR_HLG) {
2544*89a0ef05SAndroid Build Coastguard Worker     fmt = UHDR_IMG_FMT_32bppRGBA1010102;
2545*89a0ef05SAndroid Build Coastguard Worker     ct = UHDR_CT_HLG;
2546*89a0ef05SAndroid Build Coastguard Worker   } else if (output_format == ULTRAHDR_OUTPUT_HDR_PQ) {
2547*89a0ef05SAndroid Build Coastguard Worker     fmt = UHDR_IMG_FMT_32bppRGBA1010102;
2548*89a0ef05SAndroid Build Coastguard Worker     ct = UHDR_CT_PQ;
2549*89a0ef05SAndroid Build Coastguard Worker   } else if (output_format == ULTRAHDR_OUTPUT_HDR_LINEAR) {
2550*89a0ef05SAndroid Build Coastguard Worker     fmt = UHDR_IMG_FMT_64bppRGBAHalfFloat;
2551*89a0ef05SAndroid Build Coastguard Worker     ct = UHDR_CT_LINEAR;
2552*89a0ef05SAndroid Build Coastguard Worker   } else if (output_format == ULTRAHDR_OUTPUT_SDR) {
2553*89a0ef05SAndroid Build Coastguard Worker     fmt = UHDR_IMG_FMT_32bppRGBA8888;
2554*89a0ef05SAndroid Build Coastguard Worker     ct = UHDR_CT_SRGB;
2555*89a0ef05SAndroid Build Coastguard Worker   }
2556*89a0ef05SAndroid Build Coastguard Worker 
2557*89a0ef05SAndroid Build Coastguard Worker   uhdr_compressed_image_t input;
2558*89a0ef05SAndroid Build Coastguard Worker   input.data = jpegr_image_ptr->data;
2559*89a0ef05SAndroid Build Coastguard Worker   input.data_sz = jpegr_image_ptr->length;
2560*89a0ef05SAndroid Build Coastguard Worker   input.capacity = jpegr_image_ptr->maxLength;
2561*89a0ef05SAndroid Build Coastguard Worker   input.cg = map_legacy_cg_to_cg(jpegr_image_ptr->colorGamut);
2562*89a0ef05SAndroid Build Coastguard Worker   input.ct = UHDR_CT_UNSPECIFIED;
2563*89a0ef05SAndroid Build Coastguard Worker   input.range = UHDR_CR_UNSPECIFIED;
2564*89a0ef05SAndroid Build Coastguard Worker 
2565*89a0ef05SAndroid Build Coastguard Worker   jpeg_info_struct primary_image;
2566*89a0ef05SAndroid Build Coastguard Worker   jpeg_info_struct gainmap_image;
2567*89a0ef05SAndroid Build Coastguard Worker   jpegr_info_struct jpegr_info;
2568*89a0ef05SAndroid Build Coastguard Worker   jpegr_info.primaryImgInfo = &primary_image;
2569*89a0ef05SAndroid Build Coastguard Worker   jpegr_info.gainmapImgInfo = &gainmap_image;
2570*89a0ef05SAndroid Build Coastguard Worker   if (getJPEGRInfo(&input, &jpegr_info).error_code != UHDR_CODEC_OK) return JPEGR_UNKNOWN_ERROR;
2571*89a0ef05SAndroid Build Coastguard Worker 
2572*89a0ef05SAndroid Build Coastguard Worker   if (exif != nullptr) {
2573*89a0ef05SAndroid Build Coastguard Worker     if (exif->length < primary_image.exifData.size()) {
2574*89a0ef05SAndroid Build Coastguard Worker       return ERROR_JPEGR_BUFFER_TOO_SMALL;
2575*89a0ef05SAndroid Build Coastguard Worker     }
2576*89a0ef05SAndroid Build Coastguard Worker     memcpy(exif->data, primary_image.exifData.data(), primary_image.exifData.size());
2577*89a0ef05SAndroid Build Coastguard Worker     exif->length = primary_image.exifData.size();
2578*89a0ef05SAndroid Build Coastguard Worker   }
2579*89a0ef05SAndroid Build Coastguard Worker 
2580*89a0ef05SAndroid Build Coastguard Worker   uhdr_raw_image_t output;
2581*89a0ef05SAndroid Build Coastguard Worker   output.fmt = fmt;
2582*89a0ef05SAndroid Build Coastguard Worker   output.cg = UHDR_CG_UNSPECIFIED;
2583*89a0ef05SAndroid Build Coastguard Worker   output.ct = UHDR_CT_UNSPECIFIED;
2584*89a0ef05SAndroid Build Coastguard Worker   output.range = UHDR_CR_UNSPECIFIED;
2585*89a0ef05SAndroid Build Coastguard Worker   output.w = jpegr_info.width;
2586*89a0ef05SAndroid Build Coastguard Worker   output.h = jpegr_info.height;
2587*89a0ef05SAndroid Build Coastguard Worker   output.planes[UHDR_PLANE_PACKED] = dest->data;
2588*89a0ef05SAndroid Build Coastguard Worker   output.stride[UHDR_PLANE_PACKED] = jpegr_info.width;
2589*89a0ef05SAndroid Build Coastguard Worker   output.planes[UHDR_PLANE_U] = nullptr;
2590*89a0ef05SAndroid Build Coastguard Worker   output.stride[UHDR_PLANE_U] = 0;
2591*89a0ef05SAndroid Build Coastguard Worker   output.planes[UHDR_PLANE_V] = nullptr;
2592*89a0ef05SAndroid Build Coastguard Worker   output.stride[UHDR_PLANE_V] = 0;
2593*89a0ef05SAndroid Build Coastguard Worker 
2594*89a0ef05SAndroid Build Coastguard Worker   uhdr_raw_image_t output_gm;
2595*89a0ef05SAndroid Build Coastguard Worker   if (gainmap_image_ptr) {
2596*89a0ef05SAndroid Build Coastguard Worker     output.fmt =
2597*89a0ef05SAndroid Build Coastguard Worker         gainmap_image.numComponents == 1 ? UHDR_IMG_FMT_8bppYCbCr400 : UHDR_IMG_FMT_24bppRGB888;
2598*89a0ef05SAndroid Build Coastguard Worker     output.cg = UHDR_CG_UNSPECIFIED;
2599*89a0ef05SAndroid Build Coastguard Worker     output.ct = UHDR_CT_UNSPECIFIED;
2600*89a0ef05SAndroid Build Coastguard Worker     output.range = UHDR_CR_UNSPECIFIED;
2601*89a0ef05SAndroid Build Coastguard Worker     output.w = gainmap_image.width;
2602*89a0ef05SAndroid Build Coastguard Worker     output.h = gainmap_image.height;
2603*89a0ef05SAndroid Build Coastguard Worker     output.planes[UHDR_PLANE_PACKED] = gainmap_image_ptr->data;
2604*89a0ef05SAndroid Build Coastguard Worker     output.stride[UHDR_PLANE_PACKED] = gainmap_image.width;
2605*89a0ef05SAndroid Build Coastguard Worker     output.planes[UHDR_PLANE_U] = nullptr;
2606*89a0ef05SAndroid Build Coastguard Worker     output.stride[UHDR_PLANE_U] = 0;
2607*89a0ef05SAndroid Build Coastguard Worker     output.planes[UHDR_PLANE_V] = nullptr;
2608*89a0ef05SAndroid Build Coastguard Worker     output.stride[UHDR_PLANE_V] = 0;
2609*89a0ef05SAndroid Build Coastguard Worker   }
2610*89a0ef05SAndroid Build Coastguard Worker 
2611*89a0ef05SAndroid Build Coastguard Worker   uhdr_gainmap_metadata_ext_t meta;
2612*89a0ef05SAndroid Build Coastguard Worker   auto result = decodeJPEGR(&input, &output, max_display_boost, ct, fmt,
2613*89a0ef05SAndroid Build Coastguard Worker                             gainmap_image_ptr ? &output_gm : nullptr, metadata ? &meta : nullptr);
2614*89a0ef05SAndroid Build Coastguard Worker 
2615*89a0ef05SAndroid Build Coastguard Worker   if (result.error_code == UHDR_CODEC_OK) {
2616*89a0ef05SAndroid Build Coastguard Worker     dest->width = output.w;
2617*89a0ef05SAndroid Build Coastguard Worker     dest->height = output.h;
2618*89a0ef05SAndroid Build Coastguard Worker     dest->colorGamut = map_cg_to_legacy_cg(output.cg);
2619*89a0ef05SAndroid Build Coastguard Worker     dest->colorRange = output.range;
2620*89a0ef05SAndroid Build Coastguard Worker     dest->pixelFormat = output.fmt;
2621*89a0ef05SAndroid Build Coastguard Worker     dest->chroma_data = nullptr;
2622*89a0ef05SAndroid Build Coastguard Worker     if (gainmap_image_ptr) {
2623*89a0ef05SAndroid Build Coastguard Worker       gainmap_image_ptr->width = output_gm.w;
2624*89a0ef05SAndroid Build Coastguard Worker       gainmap_image_ptr->height = output_gm.h;
2625*89a0ef05SAndroid Build Coastguard Worker       gainmap_image_ptr->colorGamut = map_cg_to_legacy_cg(output_gm.cg);
2626*89a0ef05SAndroid Build Coastguard Worker       gainmap_image_ptr->colorRange = output_gm.range;
2627*89a0ef05SAndroid Build Coastguard Worker       gainmap_image_ptr->pixelFormat = output_gm.fmt;
2628*89a0ef05SAndroid Build Coastguard Worker       gainmap_image_ptr->chroma_data = nullptr;
2629*89a0ef05SAndroid Build Coastguard Worker     }
2630*89a0ef05SAndroid Build Coastguard Worker     if (metadata) {
2631*89a0ef05SAndroid Build Coastguard Worker       metadata->version = meta.version;
2632*89a0ef05SAndroid Build Coastguard Worker       metadata->hdrCapacityMax = meta.hdr_capacity_max;
2633*89a0ef05SAndroid Build Coastguard Worker       metadata->hdrCapacityMin = meta.hdr_capacity_min;
2634*89a0ef05SAndroid Build Coastguard Worker       metadata->gamma = meta.gamma;
2635*89a0ef05SAndroid Build Coastguard Worker       metadata->offsetSdr = meta.offset_sdr;
2636*89a0ef05SAndroid Build Coastguard Worker       metadata->offsetHdr = meta.offset_hdr;
2637*89a0ef05SAndroid Build Coastguard Worker       metadata->maxContentBoost = meta.max_content_boost;
2638*89a0ef05SAndroid Build Coastguard Worker       metadata->minContentBoost = meta.min_content_boost;
2639*89a0ef05SAndroid Build Coastguard Worker     }
2640*89a0ef05SAndroid Build Coastguard Worker   }
2641*89a0ef05SAndroid Build Coastguard Worker 
2642*89a0ef05SAndroid Build Coastguard Worker   return result.error_code == UHDR_CODEC_OK ? JPEGR_NO_ERROR : JPEGR_UNKNOWN_ERROR;
2643*89a0ef05SAndroid Build Coastguard Worker }
2644*89a0ef05SAndroid Build Coastguard Worker 
2645*89a0ef05SAndroid Build Coastguard Worker }  // namespace ultrahdr
2646