xref: /aosp_15_r20/external/libultrahdr/examples/ultrahdr_app.cpp (revision 89a0ef05262152531a00a15832a2d3b1e3990773)
1 /*
2  * Copyright 2023 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 #ifdef _WIN32
18 #include <Windows.h>
19 #else
20 #include <sys/time.h>
21 #endif
22 
23 #include <string.h>
24 
25 #include <algorithm>
26 #include <cfloat>
27 #include <cmath>
28 #include <cstdint>
29 #include <fstream>
30 #include <iostream>
31 #include <sstream>
32 
33 #include "ultrahdr_api.h"
34 
35 const float BT601YUVtoRGBMatrix[9] = {
36     1.f, 0.f, 1.402f, 1.f, (-0.202008f / 0.587f), (-0.419198f / 0.587f), 1.0f, 1.772f, 0.0f};
37 const float BT709YUVtoRGBMatrix[9] = {
38     1.f,  0.f,     1.5748f, 1.f, (-0.13397432f / 0.7152f), (-0.33480248f / 0.7152f),
39     1.0f, 1.8556f, 0.0f};
40 const float BT2020YUVtoRGBMatrix[9] = {
41     1.f, 0.f, 1.4746f, 1.f, (-0.11156702f / 0.6780f), (-0.38737742f / 0.6780f), 1.f, 1.8814f, 0.f};
42 
43 const float BT601RGBtoYUVMatrix[9] = {0.299f,
44                                       0.587f,
45                                       0.114f,
46                                       (-0.299f / 1.772f),
47                                       (-0.587f / 1.772f),
48                                       0.5f,
49                                       0.5f,
50                                       (-0.587f / 1.402f),
51                                       (-0.114f / 1.402f)};
52 const float BT709RGBtoYUVMatrix[9] = {0.2126f,
53                                       0.7152f,
54                                       0.0722f,
55                                       (-0.2126f / 1.8556f),
56                                       (-0.7152f / 1.8556f),
57                                       0.5f,
58                                       0.5f,
59                                       (-0.7152f / 1.5748f),
60                                       (-0.0722f / 1.5748f)};
61 const float BT2020RGBtoYUVMatrix[9] = {0.2627f,
62                                        0.6780f,
63                                        0.0593f,
64                                        (-0.2627f / 1.8814f),
65                                        (-0.6780f / 1.8814f),
66                                        0.5f,
67                                        0.5f,
68                                        (-0.6780f / 1.4746f),
69                                        (-0.0593f / 1.4746f)};
70 
71 // remove these once introduced in ultrahdr_api.h
72 const int UHDR_IMG_FMT_48bppYCbCr444 = 101;
73 
74 int optind_s = 1;
75 int optopt_s = 0;
76 char* optarg_s = nullptr;
77 
getopt_s(int argc,char * const argv[],char * ostr)78 int getopt_s(int argc, char* const argv[], char* ostr) {
79   if (optind_s >= argc) return -1;
80 
81   const char* arg = argv[optind_s];
82   if (arg[0] != '-' || !arg[1]) {
83     std::cerr << "invalid option " << arg << std::endl;
84     return '?';
85   }
86   optopt_s = arg[1];
87   char* oindex = strchr(ostr, optopt_s);
88   if (!oindex) {
89     std::cerr << "unsupported option " << arg << std::endl;
90     return '?';
91   }
92   if (oindex[1] != ':') {
93     optarg_s = nullptr;
94     return optopt_s;
95   }
96 
97   if (argc > ++optind_s) {
98     optarg_s = (char*)argv[optind_s++];
99   } else {
100     std::cerr << "option " << arg << " requires an argument" << std::endl;
101     optarg_s = nullptr;
102     return '?';
103   }
104   return optopt_s;
105 }
106 
107 // #define PROFILE_ENABLE 1
108 #ifdef _WIN32
109 class Profiler {
110  public:
timerStart()111   void timerStart() { QueryPerformanceCounter(&mStartingTime); }
112 
timerStop()113   void timerStop() { QueryPerformanceCounter(&mEndingTime); }
114 
elapsedTime()115   double elapsedTime() {
116     LARGE_INTEGER frequency;
117     LARGE_INTEGER elapsedMicroseconds;
118     QueryPerformanceFrequency(&frequency);
119     elapsedMicroseconds.QuadPart = mEndingTime.QuadPart - mStartingTime.QuadPart;
120     return (double)elapsedMicroseconds.QuadPart / (double)frequency.QuadPart * 1000000;
121   }
122 
123  private:
124   LARGE_INTEGER mStartingTime;
125   LARGE_INTEGER mEndingTime;
126 };
127 #else
128 class Profiler {
129  public:
timerStart()130   void timerStart() { gettimeofday(&mStartingTime, nullptr); }
131 
timerStop()132   void timerStop() { gettimeofday(&mEndingTime, nullptr); }
133 
elapsedTime()134   int64_t elapsedTime() {
135     struct timeval elapsedMicroseconds;
136     elapsedMicroseconds.tv_sec = mEndingTime.tv_sec - mStartingTime.tv_sec;
137     elapsedMicroseconds.tv_usec = mEndingTime.tv_usec - mStartingTime.tv_usec;
138     return elapsedMicroseconds.tv_sec * 1000000 + elapsedMicroseconds.tv_usec;
139   }
140 
141  private:
142   struct timeval mStartingTime;
143   struct timeval mEndingTime;
144 };
145 #endif
146 
147 #define READ_BYTES(DESC, ADDR, LEN)                                                             \
148   DESC.read(static_cast<char*>(ADDR), (LEN));                                                   \
149   if (DESC.gcount() != (LEN)) {                                                                 \
150     std::cerr << "failed to read : " << (LEN) << " bytes, read : " << DESC.gcount() << " bytes" \
151               << std::endl;                                                                     \
152     return false;                                                                               \
153   }
154 
loadFile(const char * filename,void * & result,std::streamoff length)155 static bool loadFile(const char* filename, void*& result, std::streamoff length) {
156   if (length <= 0) {
157     std::cerr << "requested to read invalid length : " << length
158               << " bytes from file : " << filename << std::endl;
159     return false;
160   }
161   std::ifstream ifd(filename, std::ios::binary | std::ios::ate);
162   if (ifd.good()) {
163     auto size = ifd.tellg();
164     if (size < length) {
165       std::cerr << "requested to read " << length << " bytes from file : " << filename
166                 << ", file contains only " << size << " bytes" << std::endl;
167       return false;
168     }
169     ifd.seekg(0, std::ios::beg);
170     result = malloc(length);
171     if (result == nullptr) {
172       std::cerr << "failed to allocate memory to store contents of file : " << filename
173                 << std::endl;
174       return false;
175     }
176     READ_BYTES(ifd, result, length)
177     return true;
178   }
179   std::cerr << "unable to open file : " << filename << std::endl;
180   return false;
181 }
182 
loadFile(const char * filename,uhdr_raw_image_t * handle)183 static bool loadFile(const char* filename, uhdr_raw_image_t* handle) {
184   std::ifstream ifd(filename, std::ios::binary);
185   if (ifd.good()) {
186     if (handle->fmt == UHDR_IMG_FMT_24bppYCbCrP010) {
187       const size_t bpp = 2;
188       READ_BYTES(ifd, handle->planes[UHDR_PLANE_Y], bpp * handle->w * handle->h)
189       READ_BYTES(ifd, handle->planes[UHDR_PLANE_UV], bpp * (handle->w / 2) * (handle->h / 2) * 2)
190       return true;
191     } else if (handle->fmt == UHDR_IMG_FMT_32bppRGBA1010102 ||
192                handle->fmt == UHDR_IMG_FMT_32bppRGBA8888) {
193       const size_t bpp = 4;
194       READ_BYTES(ifd, handle->planes[UHDR_PLANE_PACKED], bpp * handle->w * handle->h)
195       return true;
196     } else if (handle->fmt == UHDR_IMG_FMT_64bppRGBAHalfFloat) {
197       const size_t bpp = 8;
198       READ_BYTES(ifd, handle->planes[UHDR_PLANE_PACKED], bpp * handle->w * handle->h)
199       return true;
200     } else if (handle->fmt == UHDR_IMG_FMT_12bppYCbCr420) {
201       READ_BYTES(ifd, handle->planes[UHDR_PLANE_Y], (size_t)handle->w * handle->h)
202       READ_BYTES(ifd, handle->planes[UHDR_PLANE_U], (size_t)(handle->w / 2) * (handle->h / 2))
203       READ_BYTES(ifd, handle->planes[UHDR_PLANE_V], (size_t)(handle->w / 2) * (handle->h / 2))
204       return true;
205     }
206     return false;
207   }
208   std::cerr << "unable to open file : " << filename << std::endl;
209   return false;
210 }
211 
writeFile(const char * filename,void * & result,size_t length)212 static bool writeFile(const char* filename, void*& result, size_t length) {
213   std::ofstream ofd(filename, std::ios::binary);
214   if (ofd.is_open()) {
215     ofd.write(static_cast<char*>(result), length);
216     return true;
217   }
218   std::cerr << "unable to write to file : " << filename << std::endl;
219   return false;
220 }
221 
writeFile(const char * filename,uhdr_raw_image_t * img)222 static bool writeFile(const char* filename, uhdr_raw_image_t* img) {
223   std::ofstream ofd(filename, std::ios::binary);
224   if (ofd.is_open()) {
225     if (img->fmt == UHDR_IMG_FMT_32bppRGBA8888 || img->fmt == UHDR_IMG_FMT_64bppRGBAHalfFloat ||
226         img->fmt == UHDR_IMG_FMT_32bppRGBA1010102) {
227       char* data = static_cast<char*>(img->planes[UHDR_PLANE_PACKED]);
228       const size_t bpp = img->fmt == UHDR_IMG_FMT_64bppRGBAHalfFloat ? 8 : 4;
229       const size_t stride = img->stride[UHDR_PLANE_PACKED] * bpp;
230       const size_t length = img->w * bpp;
231       for (unsigned i = 0; i < img->h; i++, data += stride) {
232         ofd.write(data, length);
233       }
234       return true;
235     } else if ((int)img->fmt == UHDR_IMG_FMT_24bppYCbCr444 ||
236                (int)img->fmt == UHDR_IMG_FMT_48bppYCbCr444) {
237       char* data = static_cast<char*>(img->planes[UHDR_PLANE_Y]);
238       const size_t bpp = (int)img->fmt == UHDR_IMG_FMT_48bppYCbCr444 ? 2 : 1;
239       size_t stride = img->stride[UHDR_PLANE_Y] * bpp;
240       size_t length = img->w * bpp;
241       for (unsigned i = 0; i < img->h; i++, data += stride) {
242         ofd.write(data, length);
243       }
244       data = static_cast<char*>(img->planes[UHDR_PLANE_U]);
245       stride = img->stride[UHDR_PLANE_U] * bpp;
246       for (unsigned i = 0; i < img->h; i++, data += stride) {
247         ofd.write(data, length);
248       }
249       data = static_cast<char*>(img->planes[UHDR_PLANE_V]);
250       stride = img->stride[UHDR_PLANE_V] * bpp;
251       for (unsigned i = 0; i < img->h; i++, data += stride) {
252         ofd.write(data, length);
253       }
254       return true;
255     }
256     return false;
257   }
258   std::cerr << "unable to write to file : " << filename << std::endl;
259   return false;
260 }
261 
262 class UltraHdrAppInput {
263  public:
UltraHdrAppInput(const char * hdrIntentRawFile,const char * sdrIntentRawFile,const char * sdrIntentCompressedFile,const char * gainmapCompressedFile,const char * gainmapMetadataCfgFile,const char * exifFile,const char * outputFile,int width,int height,uhdr_img_fmt_t hdrCf=UHDR_IMG_FMT_32bppRGBA1010102,uhdr_img_fmt_t sdrCf=UHDR_IMG_FMT_32bppRGBA8888,uhdr_color_gamut_t hdrCg=UHDR_CG_DISPLAY_P3,uhdr_color_gamut_t sdrCg=UHDR_CG_BT_709,uhdr_color_transfer_t hdrTf=UHDR_CT_HLG,int quality=95,uhdr_color_transfer_t oTf=UHDR_CT_HLG,uhdr_img_fmt_t oFmt=UHDR_IMG_FMT_32bppRGBA1010102,bool isHdrCrFull=false,int gainmapScaleFactor=1,int gainmapQuality=95,bool enableMultiChannelGainMap=true,float gamma=1.0f,bool enableGLES=false,uhdr_enc_preset_t encPreset=UHDR_USAGE_BEST_QUALITY,float minContentBoost=FLT_MIN,float maxContentBoost=FLT_MAX,float targetDispPeakBrightness=-1.0f)264   UltraHdrAppInput(const char* hdrIntentRawFile, const char* sdrIntentRawFile,
265                    const char* sdrIntentCompressedFile, const char* gainmapCompressedFile,
266                    const char* gainmapMetadataCfgFile, const char* exifFile, const char* outputFile,
267                    int width, int height, uhdr_img_fmt_t hdrCf = UHDR_IMG_FMT_32bppRGBA1010102,
268                    uhdr_img_fmt_t sdrCf = UHDR_IMG_FMT_32bppRGBA8888,
269                    uhdr_color_gamut_t hdrCg = UHDR_CG_DISPLAY_P3,
270                    uhdr_color_gamut_t sdrCg = UHDR_CG_BT_709,
271                    uhdr_color_transfer_t hdrTf = UHDR_CT_HLG, int quality = 95,
272                    uhdr_color_transfer_t oTf = UHDR_CT_HLG,
273                    uhdr_img_fmt_t oFmt = UHDR_IMG_FMT_32bppRGBA1010102, bool isHdrCrFull = false,
274                    int gainmapScaleFactor = 1, int gainmapQuality = 95,
275                    bool enableMultiChannelGainMap = true, float gamma = 1.0f,
276                    bool enableGLES = false, uhdr_enc_preset_t encPreset = UHDR_USAGE_BEST_QUALITY,
277                    float minContentBoost = FLT_MIN, float maxContentBoost = FLT_MAX,
278                    float targetDispPeakBrightness = -1.0f)
279       : mHdrIntentRawFile(hdrIntentRawFile),
280         mSdrIntentRawFile(sdrIntentRawFile),
281         mSdrIntentCompressedFile(sdrIntentCompressedFile),
282         mGainMapCompressedFile(gainmapCompressedFile),
283         mGainMapMetadataCfgFile(gainmapMetadataCfgFile),
284         mExifFile(exifFile),
285         mUhdrFile(nullptr),
286         mOutputFile(outputFile),
287         mWidth(width),
288         mHeight(height),
289         mHdrCf(hdrCf),
290         mSdrCf(sdrCf),
291         mHdrCg(hdrCg),
292         mSdrCg(sdrCg),
293         mHdrTf(hdrTf),
294         mQuality(quality),
295         mOTf(oTf),
296         mOfmt(oFmt),
297         mFullRange(isHdrCrFull),
298         mMapDimensionScaleFactor(gainmapScaleFactor),
299         mMapCompressQuality(gainmapQuality),
300         mUseMultiChannelGainMap(enableMultiChannelGainMap),
301         mGamma(gamma),
302         mEnableGLES(enableGLES),
303         mEncPreset(encPreset),
304         mMinContentBoost(minContentBoost),
305         mMaxContentBoost(maxContentBoost),
306         mTargetDispPeakBrightness(targetDispPeakBrightness),
307         mMode(0){};
308 
UltraHdrAppInput(const char * gainmapMetadataCfgFile,const char * uhdrFile,const char * outputFile,uhdr_color_transfer_t oTf=UHDR_CT_HLG,uhdr_img_fmt_t oFmt=UHDR_IMG_FMT_32bppRGBA1010102,bool enableGLES=false)309   UltraHdrAppInput(const char* gainmapMetadataCfgFile, const char* uhdrFile, const char* outputFile,
310                    uhdr_color_transfer_t oTf = UHDR_CT_HLG,
311                    uhdr_img_fmt_t oFmt = UHDR_IMG_FMT_32bppRGBA1010102, bool enableGLES = false)
312       : mHdrIntentRawFile(nullptr),
313         mSdrIntentRawFile(nullptr),
314         mSdrIntentCompressedFile(nullptr),
315         mGainMapCompressedFile(nullptr),
316         mGainMapMetadataCfgFile(gainmapMetadataCfgFile),
317         mExifFile(nullptr),
318         mUhdrFile(uhdrFile),
319         mOutputFile(outputFile),
320         mWidth(0),
321         mHeight(0),
322         mHdrCf(UHDR_IMG_FMT_UNSPECIFIED),
323         mSdrCf(UHDR_IMG_FMT_UNSPECIFIED),
324         mHdrCg(UHDR_CG_UNSPECIFIED),
325         mSdrCg(UHDR_CG_UNSPECIFIED),
326         mHdrTf(UHDR_CT_UNSPECIFIED),
327         mQuality(95),
328         mOTf(oTf),
329         mOfmt(oFmt),
330         mFullRange(false),
331         mMapDimensionScaleFactor(1),
332         mMapCompressQuality(95),
333         mUseMultiChannelGainMap(true),
334         mGamma(1.0f),
335         mEnableGLES(enableGLES),
336         mEncPreset(UHDR_USAGE_BEST_QUALITY),
337         mMinContentBoost(FLT_MIN),
338         mMaxContentBoost(FLT_MAX),
339         mTargetDispPeakBrightness(-1.0f),
340         mMode(1){};
341 
~UltraHdrAppInput()342   ~UltraHdrAppInput() {
343     int count = sizeof mRawP010Image.planes / sizeof mRawP010Image.planes[UHDR_PLANE_Y];
344     for (int i = 0; i < count; i++) {
345       if (mRawP010Image.planes[i]) {
346         free(mRawP010Image.planes[i]);
347         mRawP010Image.planes[i] = nullptr;
348       }
349       if (mRawRgba1010102Image.planes[i]) {
350         free(mRawRgba1010102Image.planes[i]);
351         mRawRgba1010102Image.planes[i] = nullptr;
352       }
353       if (mRawRgbaF16Image.planes[i]) {
354         free(mRawRgbaF16Image.planes[i]);
355         mRawRgbaF16Image.planes[i] = nullptr;
356       }
357       if (mRawYuv420Image.planes[i]) {
358         free(mRawYuv420Image.planes[i]);
359         mRawYuv420Image.planes[i] = nullptr;
360       }
361       if (mRawRgba8888Image.planes[i]) {
362         free(mRawRgba8888Image.planes[i]);
363         mRawRgba8888Image.planes[i] = nullptr;
364       }
365       if (mDecodedUhdrRgbImage.planes[i]) {
366         free(mDecodedUhdrRgbImage.planes[i]);
367         mDecodedUhdrRgbImage.planes[i] = nullptr;
368       }
369       if (mDecodedUhdrYuv444Image.planes[i]) {
370         free(mDecodedUhdrYuv444Image.planes[i]);
371         mDecodedUhdrYuv444Image.planes[i] = nullptr;
372       }
373     }
374     if (mExifBlock.data) free(mExifBlock.data);
375     if (mUhdrImage.data) free(mUhdrImage.data);
376   }
377 
378   bool fillUhdrImageHandle();
379   bool fillP010ImageHandle();
380   bool fillRGBA1010102ImageHandle();
381   bool fillRGBAF16ImageHandle();
382   bool convertP010ToRGBImage();
383   bool fillYuv420ImageHandle();
384   bool fillRGBA8888ImageHandle();
385   bool convertYuv420ToRGBImage();
386   bool fillSdrCompressedImageHandle();
387   bool fillGainMapCompressedImageHandle();
388   bool fillGainMapMetadataDescriptor();
389   bool fillExifMemoryBlock();
390   bool writeGainMapMetadataToFile(uhdr_gainmap_metadata_t* metadata);
391   bool convertRgba8888ToYUV444Image();
392   bool convertRgba1010102ToYUV444Image();
393   bool encode();
394   bool decode();
395   void computeRGBHdrPSNR();
396   void computeRGBSdrPSNR();
397   void computeYUVHdrPSNR();
398   void computeYUVSdrPSNR();
399 
400   const char* mHdrIntentRawFile;
401   const char* mSdrIntentRawFile;
402   const char* mSdrIntentCompressedFile;
403   const char* mGainMapCompressedFile;
404   const char* mGainMapMetadataCfgFile;
405   const char* mExifFile;
406   const char* mUhdrFile;
407   const char* mOutputFile;
408   const int mWidth;
409   const int mHeight;
410   const uhdr_img_fmt_t mHdrCf;
411   const uhdr_img_fmt_t mSdrCf;
412   const uhdr_color_gamut_t mHdrCg;
413   const uhdr_color_gamut_t mSdrCg;
414   const uhdr_color_transfer_t mHdrTf;
415   const int mQuality;
416   const uhdr_color_transfer_t mOTf;
417   const uhdr_img_fmt_t mOfmt;
418   const bool mFullRange;
419   const int mMapDimensionScaleFactor;
420   const int mMapCompressQuality;
421   const bool mUseMultiChannelGainMap;
422   const float mGamma;
423   const bool mEnableGLES;
424   const uhdr_enc_preset_t mEncPreset;
425   const float mMinContentBoost;
426   const float mMaxContentBoost;
427   const float mTargetDispPeakBrightness;
428   const int mMode;
429 
430   uhdr_raw_image_t mRawP010Image{};
431   uhdr_raw_image_t mRawRgba1010102Image{};
432   uhdr_raw_image_t mRawRgbaF16Image{};
433   uhdr_raw_image_t mRawYuv420Image{};
434   uhdr_raw_image_t mRawRgba8888Image{};
435   uhdr_compressed_image_t mSdrIntentCompressedImage{};
436   uhdr_compressed_image_t mGainMapCompressedImage{};
437   uhdr_gainmap_metadata mGainMapMetadata{};
438   uhdr_mem_block_t mExifBlock{};
439   uhdr_compressed_image_t mUhdrImage{};
440   uhdr_raw_image_t mDecodedUhdrRgbImage{};
441   uhdr_raw_image_t mDecodedUhdrYuv444Image{};
442   double mPsnr[3]{};
443 };
444 
fillP010ImageHandle()445 bool UltraHdrAppInput::fillP010ImageHandle() {
446   const size_t bpp = 2;
447   size_t p010Size = bpp * mWidth * mHeight * 3 / 2;
448   mRawP010Image.fmt = UHDR_IMG_FMT_24bppYCbCrP010;
449   mRawP010Image.cg = mHdrCg;
450   mRawP010Image.ct = mHdrTf;
451 
452   mRawP010Image.range = mFullRange ? UHDR_CR_FULL_RANGE : UHDR_CR_LIMITED_RANGE;
453   mRawP010Image.w = mWidth;
454   mRawP010Image.h = mHeight;
455   mRawP010Image.planes[UHDR_PLANE_Y] = malloc(bpp * mWidth * mHeight);
456   mRawP010Image.planes[UHDR_PLANE_UV] = malloc(bpp * (mWidth / 2) * (mHeight / 2) * 2);
457   mRawP010Image.planes[UHDR_PLANE_V] = nullptr;
458   mRawP010Image.stride[UHDR_PLANE_Y] = mWidth;
459   mRawP010Image.stride[UHDR_PLANE_UV] = mWidth;
460   mRawP010Image.stride[UHDR_PLANE_V] = 0;
461   return loadFile(mHdrIntentRawFile, &mRawP010Image);
462 }
463 
fillYuv420ImageHandle()464 bool UltraHdrAppInput::fillYuv420ImageHandle() {
465   size_t yuv420Size = (size_t)mWidth * mHeight * 3 / 2;
466   mRawYuv420Image.fmt = UHDR_IMG_FMT_12bppYCbCr420;
467   mRawYuv420Image.cg = mSdrCg;
468   mRawYuv420Image.ct = UHDR_CT_SRGB;
469   mRawYuv420Image.range = UHDR_CR_FULL_RANGE;
470   mRawYuv420Image.w = mWidth;
471   mRawYuv420Image.h = mHeight;
472   mRawYuv420Image.planes[UHDR_PLANE_Y] = malloc((size_t)mWidth * mHeight);
473   mRawYuv420Image.planes[UHDR_PLANE_U] = malloc((size_t)(mWidth / 2) * (mHeight / 2));
474   mRawYuv420Image.planes[UHDR_PLANE_V] = malloc((size_t)(mWidth / 2) * (mHeight / 2));
475   mRawYuv420Image.stride[UHDR_PLANE_Y] = mWidth;
476   mRawYuv420Image.stride[UHDR_PLANE_U] = mWidth / 2;
477   mRawYuv420Image.stride[UHDR_PLANE_V] = mWidth / 2;
478   return loadFile(mSdrIntentRawFile, &mRawYuv420Image);
479 }
480 
fillRGBA1010102ImageHandle()481 bool UltraHdrAppInput::fillRGBA1010102ImageHandle() {
482   const size_t bpp = 4;
483   mRawRgba1010102Image.fmt = UHDR_IMG_FMT_32bppRGBA1010102;
484   mRawRgba1010102Image.cg = mHdrCg;
485   mRawRgba1010102Image.ct = mHdrTf;
486   mRawRgba1010102Image.range = UHDR_CR_FULL_RANGE;
487   mRawRgba1010102Image.w = mWidth;
488   mRawRgba1010102Image.h = mHeight;
489   mRawRgba1010102Image.planes[UHDR_PLANE_PACKED] = malloc(bpp * mWidth * mHeight);
490   mRawRgba1010102Image.planes[UHDR_PLANE_UV] = nullptr;
491   mRawRgba1010102Image.planes[UHDR_PLANE_V] = nullptr;
492   mRawRgba1010102Image.stride[UHDR_PLANE_PACKED] = mWidth;
493   mRawRgba1010102Image.stride[UHDR_PLANE_UV] = 0;
494   mRawRgba1010102Image.stride[UHDR_PLANE_V] = 0;
495   return loadFile(mHdrIntentRawFile, &mRawRgba1010102Image);
496 }
497 
fillRGBAF16ImageHandle()498 bool UltraHdrAppInput::fillRGBAF16ImageHandle() {
499   const size_t bpp = 8;
500   mRawRgbaF16Image.fmt = UHDR_IMG_FMT_64bppRGBAHalfFloat;
501   mRawRgbaF16Image.cg = mHdrCg;
502   mRawRgbaF16Image.ct = mHdrTf;
503   mRawRgbaF16Image.range = UHDR_CR_FULL_RANGE;
504   mRawRgbaF16Image.w = mWidth;
505   mRawRgbaF16Image.h = mHeight;
506   mRawRgbaF16Image.planes[UHDR_PLANE_PACKED] = malloc(bpp * mWidth * mHeight);
507   mRawRgbaF16Image.planes[UHDR_PLANE_UV] = nullptr;
508   mRawRgbaF16Image.planes[UHDR_PLANE_V] = nullptr;
509   mRawRgbaF16Image.stride[UHDR_PLANE_PACKED] = mWidth;
510   mRawRgbaF16Image.stride[UHDR_PLANE_UV] = 0;
511   mRawRgbaF16Image.stride[UHDR_PLANE_V] = 0;
512   return loadFile(mHdrIntentRawFile, &mRawRgbaF16Image);
513 }
514 
fillRGBA8888ImageHandle()515 bool UltraHdrAppInput::fillRGBA8888ImageHandle() {
516   const size_t bpp = 4;
517   mRawRgba8888Image.fmt = UHDR_IMG_FMT_32bppRGBA8888;
518   mRawRgba8888Image.cg = mSdrCg;
519   mRawRgba8888Image.ct = UHDR_CT_SRGB;
520   mRawRgba8888Image.range = UHDR_CR_FULL_RANGE;
521   mRawRgba8888Image.w = mWidth;
522   mRawRgba8888Image.h = mHeight;
523   mRawRgba8888Image.planes[UHDR_PLANE_PACKED] = malloc(bpp * mWidth * mHeight);
524   mRawRgba8888Image.planes[UHDR_PLANE_U] = nullptr;
525   mRawRgba8888Image.planes[UHDR_PLANE_V] = nullptr;
526   mRawRgba8888Image.stride[UHDR_PLANE_Y] = mWidth;
527   mRawRgba8888Image.stride[UHDR_PLANE_U] = 0;
528   mRawRgba8888Image.stride[UHDR_PLANE_V] = 0;
529   return loadFile(mSdrIntentRawFile, &mRawRgba8888Image);
530 }
531 
fillSdrCompressedImageHandle()532 bool UltraHdrAppInput::fillSdrCompressedImageHandle() {
533   std::ifstream ifd(mSdrIntentCompressedFile, std::ios::binary | std::ios::ate);
534   if (ifd.good()) {
535     auto size = ifd.tellg();
536     mSdrIntentCompressedImage.capacity = size;
537     mSdrIntentCompressedImage.data_sz = size;
538     mSdrIntentCompressedImage.data = nullptr;
539     mSdrIntentCompressedImage.cg = mSdrCg;
540     mSdrIntentCompressedImage.ct = UHDR_CT_UNSPECIFIED;
541     mSdrIntentCompressedImage.range = UHDR_CR_UNSPECIFIED;
542     ifd.close();
543     return loadFile(mSdrIntentCompressedFile, mSdrIntentCompressedImage.data, size);
544   }
545   return false;
546 }
547 
fillGainMapCompressedImageHandle()548 bool UltraHdrAppInput::fillGainMapCompressedImageHandle() {
549   std::ifstream ifd(mGainMapCompressedFile, std::ios::binary | std::ios::ate);
550   if (ifd.good()) {
551     auto size = ifd.tellg();
552     mGainMapCompressedImage.capacity = size;
553     mGainMapCompressedImage.data_sz = size;
554     mGainMapCompressedImage.data = nullptr;
555     mGainMapCompressedImage.cg = UHDR_CG_UNSPECIFIED;
556     mGainMapCompressedImage.ct = UHDR_CT_UNSPECIFIED;
557     mGainMapCompressedImage.range = UHDR_CR_UNSPECIFIED;
558     ifd.close();
559     return loadFile(mGainMapCompressedFile, mGainMapCompressedImage.data, size);
560   }
561   return false;
562 }
563 
parse_argument(uhdr_gainmap_metadata * metadata,char * argument,float * value)564 void parse_argument(uhdr_gainmap_metadata* metadata, char* argument, float* value) {
565   if (!strcmp(argument, "maxContentBoost"))
566     metadata->max_content_boost = *value;
567   else if (!strcmp(argument, "minContentBoost"))
568     metadata->min_content_boost = *value;
569   else if (!strcmp(argument, "gamma"))
570     metadata->gamma = *value;
571   else if (!strcmp(argument, "offsetSdr"))
572     metadata->offset_sdr = *value;
573   else if (!strcmp(argument, "offsetHdr"))
574     metadata->offset_hdr = *value;
575   else if (!strcmp(argument, "hdrCapacityMin"))
576     metadata->hdr_capacity_min = *value;
577   else if (!strcmp(argument, "hdrCapacityMax"))
578     metadata->hdr_capacity_max = *value;
579   else
580     std::cout << " Ignoring argument " << argument << std::endl;
581 }
582 
fillGainMapMetadataDescriptor()583 bool UltraHdrAppInput::fillGainMapMetadataDescriptor() {
584   std::ifstream file(mGainMapMetadataCfgFile);
585   if (!file.is_open()) {
586     return false;
587   }
588   std::string line;
589   char argument[128];
590   float value;
591   while (std::getline(file, line)) {
592     if (sscanf(line.c_str(), "--%s %f", argument, &value) == 2) {
593       parse_argument(&mGainMapMetadata, argument, &value);
594     }
595   }
596   file.close();
597   return true;
598 }
599 
fillExifMemoryBlock()600 bool UltraHdrAppInput::fillExifMemoryBlock() {
601   std::ifstream ifd(mExifFile, std::ios::binary | std::ios::ate);
602   if (ifd.good()) {
603     auto size = ifd.tellg();
604     ifd.close();
605     return loadFile(mExifFile, mExifBlock.data, size);
606   }
607   return false;
608 }
609 
writeGainMapMetadataToFile(uhdr_gainmap_metadata_t * metadata)610 bool UltraHdrAppInput::writeGainMapMetadataToFile(uhdr_gainmap_metadata_t* metadata) {
611   std::ofstream file(mGainMapMetadataCfgFile);
612   if (!file.is_open()) {
613     return false;
614   }
615   file << "--maxContentBoost " << metadata->max_content_boost << std::endl;
616   file << "--minContentBoost " << metadata->min_content_boost << std::endl;
617   file << "--gamma " << metadata->gamma << std::endl;
618   file << "--offsetSdr " << metadata->offset_sdr << std::endl;
619   file << "--offsetHdr " << metadata->offset_hdr << std::endl;
620   file << "--hdrCapacityMin " << metadata->hdr_capacity_min << std::endl;
621   file << "--hdrCapacityMax " << metadata->hdr_capacity_max << std::endl;
622   file.close();
623   return true;
624 }
625 
fillUhdrImageHandle()626 bool UltraHdrAppInput::fillUhdrImageHandle() {
627   std::ifstream ifd(mUhdrFile, std::ios::binary | std::ios::ate);
628   if (ifd.good()) {
629     auto size = ifd.tellg();
630     mUhdrImage.capacity = size;
631     mUhdrImage.data_sz = size;
632     mUhdrImage.data = nullptr;
633     mUhdrImage.cg = UHDR_CG_UNSPECIFIED;
634     mUhdrImage.ct = UHDR_CT_UNSPECIFIED;
635     mUhdrImage.range = UHDR_CR_UNSPECIFIED;
636     ifd.close();
637     return loadFile(mUhdrFile, mUhdrImage.data, size);
638   }
639   return false;
640 }
641 
encode()642 bool UltraHdrAppInput::encode() {
643   if (mHdrIntentRawFile != nullptr) {
644     if (mHdrCf == UHDR_IMG_FMT_24bppYCbCrP010) {
645       if (!fillP010ImageHandle()) {
646         std::cerr << " failed to load file " << mHdrIntentRawFile << std::endl;
647         return false;
648       }
649     } else if (mHdrCf == UHDR_IMG_FMT_32bppRGBA1010102) {
650       if (!fillRGBA1010102ImageHandle()) {
651         std::cerr << " failed to load file " << mHdrIntentRawFile << std::endl;
652         return false;
653       }
654     } else if (mHdrCf == UHDR_IMG_FMT_64bppRGBAHalfFloat) {
655       if (!fillRGBAF16ImageHandle()) {
656         std::cerr << " failed to load file " << mHdrIntentRawFile << std::endl;
657         return false;
658       }
659     } else {
660       std::cerr << " invalid hdr intent color format " << mHdrCf << std::endl;
661       return false;
662     }
663   }
664   if (mSdrIntentRawFile != nullptr) {
665     if (mSdrCf == UHDR_IMG_FMT_12bppYCbCr420) {
666       if (!fillYuv420ImageHandle()) {
667         std::cerr << " failed to load file " << mSdrIntentRawFile << std::endl;
668         return false;
669       }
670     } else if (mSdrCf == UHDR_IMG_FMT_32bppRGBA8888) {
671       if (!fillRGBA8888ImageHandle()) {
672         std::cerr << " failed to load file " << mSdrIntentRawFile << std::endl;
673         return false;
674       }
675     } else {
676       std::cerr << " invalid sdr intent color format " << mSdrCf << std::endl;
677       return false;
678     }
679   }
680   if (mSdrIntentCompressedFile != nullptr) {
681     if (!fillSdrCompressedImageHandle()) {
682       std::cerr << " failed to load file " << mSdrIntentCompressedFile << std::endl;
683       return false;
684     }
685   }
686   if (mGainMapCompressedFile != nullptr && mGainMapMetadataCfgFile != nullptr) {
687     if (!fillGainMapCompressedImageHandle()) {
688       std::cerr << " failed to load file " << mGainMapCompressedFile << std::endl;
689       return false;
690     }
691     if (!fillGainMapMetadataDescriptor()) {
692       std::cerr << " failed to read config file " << mGainMapMetadataCfgFile << std::endl;
693       return false;
694     }
695   }
696   if (mExifFile != nullptr) {
697     if (!fillExifMemoryBlock()) {
698       std::cerr << " failed to load file " << mExifFile << std::endl;
699       return false;
700     }
701   }
702 
703 #define RET_IF_ERR(x)                            \
704   {                                              \
705     uhdr_error_info_t status = (x);              \
706     if (status.error_code != UHDR_CODEC_OK) {    \
707       if (status.has_detail) {                   \
708         std::cerr << status.detail << std::endl; \
709       }                                          \
710       uhdr_release_encoder(handle);              \
711       return false;                              \
712     }                                            \
713   }
714   uhdr_codec_private_t* handle = uhdr_create_encoder();
715   if (mHdrIntentRawFile != nullptr) {
716     if (mHdrCf == UHDR_IMG_FMT_24bppYCbCrP010) {
717       RET_IF_ERR(uhdr_enc_set_raw_image(handle, &mRawP010Image, UHDR_HDR_IMG))
718     } else if (mHdrCf == UHDR_IMG_FMT_32bppRGBA1010102) {
719       RET_IF_ERR(uhdr_enc_set_raw_image(handle, &mRawRgba1010102Image, UHDR_HDR_IMG))
720     } else if (mHdrCf == UHDR_IMG_FMT_64bppRGBAHalfFloat) {
721       RET_IF_ERR(uhdr_enc_set_raw_image(handle, &mRawRgbaF16Image, UHDR_HDR_IMG))
722     }
723   }
724   if (mSdrIntentRawFile != nullptr) {
725     if (mSdrCf == UHDR_IMG_FMT_12bppYCbCr420) {
726       RET_IF_ERR(uhdr_enc_set_raw_image(handle, &mRawYuv420Image, UHDR_SDR_IMG))
727     } else if (mSdrCf == UHDR_IMG_FMT_32bppRGBA8888) {
728       RET_IF_ERR(uhdr_enc_set_raw_image(handle, &mRawRgba8888Image, UHDR_SDR_IMG))
729     }
730   }
731   if (mSdrIntentCompressedFile != nullptr) {
732     RET_IF_ERR(uhdr_enc_set_compressed_image(
733         handle, &mSdrIntentCompressedImage,
734         (mGainMapCompressedFile != nullptr && mGainMapMetadataCfgFile != nullptr) ? UHDR_BASE_IMG
735                                                                                   : UHDR_SDR_IMG))
736   }
737   if (mGainMapCompressedFile != nullptr && mGainMapMetadataCfgFile != nullptr) {
738     RET_IF_ERR(uhdr_enc_set_gainmap_image(handle, &mGainMapCompressedImage, &mGainMapMetadata))
739   }
740   if (mExifFile != nullptr) {
741     RET_IF_ERR(uhdr_enc_set_exif_data(handle, &mExifBlock))
742   }
743 
744   RET_IF_ERR(uhdr_enc_set_quality(handle, mQuality, UHDR_BASE_IMG))
745   RET_IF_ERR(uhdr_enc_set_quality(handle, mMapCompressQuality, UHDR_GAIN_MAP_IMG))
746   RET_IF_ERR(uhdr_enc_set_using_multi_channel_gainmap(handle, mUseMultiChannelGainMap))
747   RET_IF_ERR(uhdr_enc_set_gainmap_scale_factor(handle, mMapDimensionScaleFactor))
748   RET_IF_ERR(uhdr_enc_set_gainmap_gamma(handle, mGamma))
749   RET_IF_ERR(uhdr_enc_set_preset(handle, mEncPreset))
750   if (mMinContentBoost != FLT_MIN || mMaxContentBoost != FLT_MAX) {
751     RET_IF_ERR(uhdr_enc_set_min_max_content_boost(handle, mMinContentBoost, mMaxContentBoost))
752   }
753   if (mTargetDispPeakBrightness != -1.0f) {
754     RET_IF_ERR(uhdr_enc_set_target_display_peak_brightness(handle, mTargetDispPeakBrightness))
755   }
756   if (mEnableGLES) {
757     RET_IF_ERR(uhdr_enable_gpu_acceleration(handle, mEnableGLES))
758   }
759 #ifdef PROFILE_ENABLE
760   Profiler profileEncode;
761   profileEncode.timerStart();
762 #endif
763   RET_IF_ERR(uhdr_encode(handle))
764 #ifdef PROFILE_ENABLE
765   profileEncode.timerStop();
766   auto avgEncTime = profileEncode.elapsedTime() / 1000.f;
767   printf("Average encode time for res %d x %d is %f ms \n", mWidth, mHeight, avgEncTime);
768 #endif
769 
770 #undef RET_IF_ERR
771 
772   auto output = uhdr_get_encoded_stream(handle);
773 
774   // for decoding
775   mUhdrImage.data = malloc(output->data_sz);
776   memcpy(mUhdrImage.data, output->data, output->data_sz);
777   mUhdrImage.capacity = mUhdrImage.data_sz = output->data_sz;
778   mUhdrImage.cg = output->cg;
779   mUhdrImage.ct = output->ct;
780   mUhdrImage.range = output->range;
781   uhdr_release_encoder(handle);
782 
783   return writeFile(mOutputFile, mUhdrImage.data, mUhdrImage.data_sz);
784 }
785 
decode()786 bool UltraHdrAppInput::decode() {
787   if (mMode == 1 && !fillUhdrImageHandle()) {
788     std::cerr << " failed to load file " << mUhdrFile << std::endl;
789     return false;
790   }
791 
792 #define RET_IF_ERR(x)                            \
793   {                                              \
794     uhdr_error_info_t status = (x);              \
795     if (status.error_code != UHDR_CODEC_OK) {    \
796       if (status.has_detail) {                   \
797         std::cerr << status.detail << std::endl; \
798       }                                          \
799       uhdr_release_decoder(handle);              \
800       return false;                              \
801     }                                            \
802   }
803 
804   uhdr_codec_private_t* handle = uhdr_create_decoder();
805   RET_IF_ERR(uhdr_dec_set_image(handle, &mUhdrImage))
806   RET_IF_ERR(uhdr_dec_set_out_color_transfer(handle, mOTf))
807   RET_IF_ERR(uhdr_dec_set_out_img_format(handle, mOfmt))
808   if (mEnableGLES) {
809     RET_IF_ERR(uhdr_enable_gpu_acceleration(handle, mEnableGLES))
810   }
811   RET_IF_ERR(uhdr_dec_probe(handle))
812   if (mGainMapMetadataCfgFile != nullptr) {
813     uhdr_gainmap_metadata_t* metadata = uhdr_dec_get_gainmap_metadata(handle);
814     if (!writeGainMapMetadataToFile(metadata)) {
815       std::cerr << "failed to write gainmap metadata to file: " << mGainMapMetadataCfgFile
816                 << std::endl;
817     }
818   }
819 
820 #ifdef PROFILE_ENABLE
821   Profiler profileDecode;
822   profileDecode.timerStart();
823 #endif
824   RET_IF_ERR(uhdr_decode(handle))
825 #ifdef PROFILE_ENABLE
826   profileDecode.timerStop();
827   auto avgDecTime = profileDecode.elapsedTime() / 1000.f;
828   printf("Average decode time for res %d x %d is %f ms \n", uhdr_dec_get_image_width(handle),
829          uhdr_dec_get_image_height(handle), avgDecTime);
830 #endif
831 
832 #undef RET_IF_ERR
833 
834   uhdr_raw_image_t* output = uhdr_get_decoded_image(handle);
835 
836   mDecodedUhdrRgbImage.fmt = output->fmt;
837   mDecodedUhdrRgbImage.cg = output->cg;
838   mDecodedUhdrRgbImage.ct = output->ct;
839   mDecodedUhdrRgbImage.range = output->range;
840   mDecodedUhdrRgbImage.w = output->w;
841   mDecodedUhdrRgbImage.h = output->h;
842   size_t bpp = (output->fmt == UHDR_IMG_FMT_64bppRGBAHalfFloat) ? 8 : 4;
843   mDecodedUhdrRgbImage.planes[UHDR_PLANE_PACKED] = malloc(bpp * output->w * output->h);
844   char* inData = static_cast<char*>(output->planes[UHDR_PLANE_PACKED]);
845   char* outData = static_cast<char*>(mDecodedUhdrRgbImage.planes[UHDR_PLANE_PACKED]);
846   const size_t inStride = output->stride[UHDR_PLANE_PACKED] * bpp;
847   const size_t outStride = output->w * bpp;
848   mDecodedUhdrRgbImage.stride[UHDR_PLANE_PACKED] = output->w;
849   const size_t length = output->w * bpp;
850   for (unsigned i = 0; i < output->h; i++, inData += inStride, outData += outStride) {
851     memcpy(outData, inData, length);
852   }
853   uhdr_release_decoder(handle);
854 
855   return mMode == 1 ? writeFile(mOutputFile, &mDecodedUhdrRgbImage) : true;
856 }
857 
858 #define CLIP3(x, min, max) ((x) < (min)) ? (min) : ((x) > (max)) ? (max) : (x)
convertP010ToRGBImage()859 bool UltraHdrAppInput::convertP010ToRGBImage() {
860   const float* coeffs = BT2020YUVtoRGBMatrix;
861   if (mHdrCg == UHDR_CG_BT_709) {
862     coeffs = BT709YUVtoRGBMatrix;
863   } else if (mHdrCg == UHDR_CG_BT_2100) {
864     coeffs = BT2020YUVtoRGBMatrix;
865   } else if (mHdrCg == UHDR_CG_DISPLAY_P3) {
866     coeffs = BT601YUVtoRGBMatrix;
867   } else {
868     std::cerr << "color matrix not present for gamut " << mHdrCg << " using BT2020Matrix"
869               << std::endl;
870   }
871 
872   size_t bpp = 4;
873   mRawRgba1010102Image.fmt = UHDR_IMG_FMT_32bppRGBA1010102;
874   mRawRgba1010102Image.cg = mRawP010Image.cg;
875   mRawRgba1010102Image.ct = mRawP010Image.ct;
876   mRawRgba1010102Image.range = UHDR_CR_FULL_RANGE;
877   mRawRgba1010102Image.w = mRawP010Image.w;
878   mRawRgba1010102Image.h = mRawP010Image.h;
879   mRawRgba1010102Image.planes[UHDR_PLANE_PACKED] = malloc(bpp * mRawP010Image.w * mRawP010Image.h);
880   mRawRgba1010102Image.planes[UHDR_PLANE_U] = nullptr;
881   mRawRgba1010102Image.planes[UHDR_PLANE_V] = nullptr;
882   mRawRgba1010102Image.stride[UHDR_PLANE_PACKED] = mWidth;
883   mRawRgba1010102Image.stride[UHDR_PLANE_U] = 0;
884   mRawRgba1010102Image.stride[UHDR_PLANE_V] = 0;
885 
886   uint32_t* rgbData = static_cast<uint32_t*>(mRawRgba1010102Image.planes[UHDR_PLANE_PACKED]);
887   uint16_t* y = static_cast<uint16_t*>(mRawP010Image.planes[UHDR_PLANE_Y]);
888   uint16_t* u = static_cast<uint16_t*>(mRawP010Image.planes[UHDR_PLANE_UV]);
889   uint16_t* v = u + 1;
890 
891   for (size_t i = 0; i < mRawP010Image.h; i++) {
892     for (size_t j = 0; j < mRawP010Image.w; j++) {
893       float y0 = float(y[mRawP010Image.stride[UHDR_PLANE_Y] * i + j] >> 6);
894       float u0 = float(u[mRawP010Image.stride[UHDR_PLANE_UV] * (i / 2) + (j / 2) * 2] >> 6);
895       float v0 = float(v[mRawP010Image.stride[UHDR_PLANE_UV] * (i / 2) + (j / 2) * 2] >> 6);
896 
897       if (mRawP010Image.range == UHDR_CR_FULL_RANGE) {
898         y0 = CLIP3(y0, 0.0f, 1023.0f);
899         u0 = CLIP3(u0, 0.0f, 1023.0f);
900         v0 = CLIP3(v0, 0.0f, 1023.0f);
901 
902         y0 = y0 / 1023.0f;
903         u0 = u0 / 1023.0f - 0.5f;
904         v0 = v0 / 1023.0f - 0.5f;
905       } else {
906         y0 = CLIP3(y0, 64.0f, 940.0f);
907         u0 = CLIP3(u0, 64.0f, 960.0f);
908         v0 = CLIP3(v0, 64.0f, 960.0f);
909 
910         y0 = (y0 - 64.0f) / 876.0f;
911         u0 = (u0 - 512.0f) / 896.0f;
912         v0 = (v0 - 512.0f) / 896.0f;
913       }
914 
915       float r = coeffs[0] * y0 + coeffs[1] * u0 + coeffs[2] * v0;
916       float g = coeffs[3] * y0 + coeffs[4] * u0 + coeffs[5] * v0;
917       float b = coeffs[6] * y0 + coeffs[7] * u0 + coeffs[8] * v0;
918 
919       r = CLIP3(r * 1023.0f + 0.5f, 0.0f, 1023.0f);
920       g = CLIP3(g * 1023.0f + 0.5f, 0.0f, 1023.0f);
921       b = CLIP3(b * 1023.0f + 0.5f, 0.0f, 1023.0f);
922 
923       int32_t r0 = int32_t(r);
924       int32_t g0 = int32_t(g);
925       int32_t b0 = int32_t(b);
926       *rgbData = (0x3ff & r0) | ((0x3ff & g0) << 10) | ((0x3ff & b0) << 20) |
927                  (0x3 << 30);  // Set alpha to 1.0
928 
929       rgbData++;
930     }
931   }
932 #ifdef DUMP_DEBUG_DATA
933   writeFile("inRgba1010102.raw", &mRawRgba1010102Image);
934 #endif
935   return true;
936 }
937 
convertYuv420ToRGBImage()938 bool UltraHdrAppInput::convertYuv420ToRGBImage() {
939   size_t bpp = 4;
940   mRawRgba8888Image.fmt = UHDR_IMG_FMT_32bppRGBA8888;
941   mRawRgba8888Image.cg = mRawYuv420Image.cg;
942   mRawRgba8888Image.ct = mRawYuv420Image.ct;
943   mRawRgba8888Image.range = UHDR_CR_FULL_RANGE;
944   mRawRgba8888Image.w = mRawYuv420Image.w;
945   mRawRgba8888Image.h = mRawYuv420Image.h;
946   mRawRgba8888Image.planes[UHDR_PLANE_PACKED] = malloc(bpp * mRawYuv420Image.w * mRawYuv420Image.h);
947   mRawRgba8888Image.planes[UHDR_PLANE_U] = nullptr;
948   mRawRgba8888Image.planes[UHDR_PLANE_V] = nullptr;
949   mRawRgba8888Image.stride[UHDR_PLANE_PACKED] = mWidth;
950   mRawRgba8888Image.stride[UHDR_PLANE_U] = 0;
951   mRawRgba8888Image.stride[UHDR_PLANE_V] = 0;
952 
953   uint32_t* rgbData = static_cast<uint32_t*>(mRawRgba8888Image.planes[UHDR_PLANE_PACKED]);
954   uint8_t* y = static_cast<uint8_t*>(mRawYuv420Image.planes[UHDR_PLANE_Y]);
955   uint8_t* u = static_cast<uint8_t*>(mRawYuv420Image.planes[UHDR_PLANE_U]);
956   uint8_t* v = static_cast<uint8_t*>(mRawYuv420Image.planes[UHDR_PLANE_V]);
957 
958   const float* coeffs = BT601YUVtoRGBMatrix;
959   if (mSdrCg == UHDR_CG_BT_709) {
960     coeffs = BT709YUVtoRGBMatrix;
961   } else if (mSdrCg == UHDR_CG_BT_2100) {
962     coeffs = BT2020YUVtoRGBMatrix;
963   } else if (mSdrCg == UHDR_CG_DISPLAY_P3) {
964     coeffs = BT601YUVtoRGBMatrix;
965   } else {
966     std::cerr << "color matrix not present for gamut " << mSdrCg << " using BT601Matrix"
967               << std::endl;
968   }
969   for (size_t i = 0; i < mRawYuv420Image.h; i++) {
970     for (size_t j = 0; j < mRawYuv420Image.w; j++) {
971       float y0 = float(y[mRawYuv420Image.stride[UHDR_PLANE_Y] * i + j]);
972       float u0 = float(u[mRawYuv420Image.stride[UHDR_PLANE_U] * (i / 2) + (j / 2)] - 128);
973       float v0 = float(v[mRawYuv420Image.stride[UHDR_PLANE_V] * (i / 2) + (j / 2)] - 128);
974 
975       y0 /= 255.0f;
976       u0 /= 255.0f;
977       v0 /= 255.0f;
978 
979       float r = coeffs[0] * y0 + coeffs[1] * u0 + coeffs[2] * v0;
980       float g = coeffs[3] * y0 + coeffs[4] * u0 + coeffs[5] * v0;
981       float b = coeffs[6] * y0 + coeffs[7] * u0 + coeffs[8] * v0;
982 
983       r = r * 255.0f + 0.5f;
984       g = g * 255.0f + 0.5f;
985       b = b * 255.0f + 0.5f;
986 
987       r = CLIP3(r, 0.0f, 255.0f);
988       g = CLIP3(g, 0.0f, 255.0f);
989       b = CLIP3(b, 0.0f, 255.0f);
990 
991       int32_t r0 = int32_t(r);
992       int32_t g0 = int32_t(g);
993       int32_t b0 = int32_t(b);
994       *rgbData = r0 | (g0 << 8) | (b0 << 16) | (255 << 24);  // Set alpha to 1.0
995 
996       rgbData++;
997     }
998   }
999 #ifdef DUMP_DEBUG_DATA
1000   writeFile("inRgba8888.raw", &mRawRgba8888Image);
1001 #endif
1002   return true;
1003 }
1004 
convertRgba8888ToYUV444Image()1005 bool UltraHdrAppInput::convertRgba8888ToYUV444Image() {
1006   mDecodedUhdrYuv444Image.fmt = static_cast<uhdr_img_fmt_t>(UHDR_IMG_FMT_24bppYCbCr444);
1007   mDecodedUhdrYuv444Image.cg = mDecodedUhdrRgbImage.cg;
1008   mDecodedUhdrYuv444Image.ct = mDecodedUhdrRgbImage.ct;
1009   mDecodedUhdrYuv444Image.range = UHDR_CR_FULL_RANGE;
1010   mDecodedUhdrYuv444Image.w = mDecodedUhdrRgbImage.w;
1011   mDecodedUhdrYuv444Image.h = mDecodedUhdrRgbImage.h;
1012   mDecodedUhdrYuv444Image.planes[UHDR_PLANE_Y] =
1013       malloc((size_t)mDecodedUhdrRgbImage.w * mDecodedUhdrRgbImage.h);
1014   mDecodedUhdrYuv444Image.planes[UHDR_PLANE_U] =
1015       malloc((size_t)mDecodedUhdrRgbImage.w * mDecodedUhdrRgbImage.h);
1016   mDecodedUhdrYuv444Image.planes[UHDR_PLANE_V] =
1017       malloc((size_t)mDecodedUhdrRgbImage.w * mDecodedUhdrRgbImage.h);
1018   mDecodedUhdrYuv444Image.stride[UHDR_PLANE_Y] = mWidth;
1019   mDecodedUhdrYuv444Image.stride[UHDR_PLANE_U] = mWidth;
1020   mDecodedUhdrYuv444Image.stride[UHDR_PLANE_V] = mWidth;
1021 
1022   uint32_t* rgbData = static_cast<uint32_t*>(mDecodedUhdrRgbImage.planes[UHDR_PLANE_PACKED]);
1023 
1024   uint8_t* yData = static_cast<uint8_t*>(mDecodedUhdrYuv444Image.planes[UHDR_PLANE_Y]);
1025   uint8_t* uData = static_cast<uint8_t*>(mDecodedUhdrYuv444Image.planes[UHDR_PLANE_U]);
1026   uint8_t* vData = static_cast<uint8_t*>(mDecodedUhdrYuv444Image.planes[UHDR_PLANE_V]);
1027 
1028   const float* coeffs = BT601RGBtoYUVMatrix;
1029   if (mDecodedUhdrRgbImage.cg == UHDR_CG_BT_709) {
1030     coeffs = BT709RGBtoYUVMatrix;
1031   } else if (mDecodedUhdrRgbImage.cg == UHDR_CG_BT_2100) {
1032     coeffs = BT2020RGBtoYUVMatrix;
1033   } else if (mDecodedUhdrRgbImage.cg == UHDR_CG_DISPLAY_P3) {
1034     coeffs = BT601RGBtoYUVMatrix;
1035   } else {
1036     std::cerr << "color matrix not present for gamut " << mDecodedUhdrRgbImage.cg
1037               << " using BT601Matrix" << std::endl;
1038   }
1039 
1040   for (size_t i = 0; i < mDecodedUhdrRgbImage.h; i++) {
1041     for (size_t j = 0; j < mDecodedUhdrRgbImage.w; j++) {
1042       float r0 = float(rgbData[mDecodedUhdrRgbImage.stride[UHDR_PLANE_PACKED] * i + j] & 0xff);
1043       float g0 =
1044           float((rgbData[mDecodedUhdrRgbImage.stride[UHDR_PLANE_PACKED] * i + j] >> 8) & 0xff);
1045       float b0 =
1046           float((rgbData[mDecodedUhdrRgbImage.stride[UHDR_PLANE_PACKED] * i + j] >> 16) & 0xff);
1047 
1048       r0 /= 255.0f;
1049       g0 /= 255.0f;
1050       b0 /= 255.0f;
1051 
1052       float y = coeffs[0] * r0 + coeffs[1] * g0 + coeffs[2] * b0;
1053       float u = coeffs[3] * r0 + coeffs[4] * g0 + coeffs[5] * b0;
1054       float v = coeffs[6] * r0 + coeffs[7] * g0 + coeffs[8] * b0;
1055 
1056       y = y * 255.0f + 0.5f;
1057       u = u * 255.0f + 0.5f + 128.0f;
1058       v = v * 255.0f + 0.5f + 128.0f;
1059 
1060       y = CLIP3(y, 0.0f, 255.0f);
1061       u = CLIP3(u, 0.0f, 255.0f);
1062       v = CLIP3(v, 0.0f, 255.0f);
1063 
1064       yData[mDecodedUhdrYuv444Image.stride[UHDR_PLANE_Y] * i + j] = uint8_t(y);
1065       uData[mDecodedUhdrYuv444Image.stride[UHDR_PLANE_U] * i + j] = uint8_t(u);
1066       vData[mDecodedUhdrYuv444Image.stride[UHDR_PLANE_V] * i + j] = uint8_t(v);
1067     }
1068   }
1069 #ifdef DUMP_DEBUG_DATA
1070   writeFile("outyuv444.yuv", &mDecodedUhdrYuv444Image);
1071 #endif
1072   return true;
1073 }
1074 
convertRgba1010102ToYUV444Image()1075 bool UltraHdrAppInput::convertRgba1010102ToYUV444Image() {
1076   const float* coeffs = BT2020RGBtoYUVMatrix;
1077   if (mDecodedUhdrRgbImage.cg == UHDR_CG_BT_709) {
1078     coeffs = BT709RGBtoYUVMatrix;
1079   } else if (mDecodedUhdrRgbImage.cg == UHDR_CG_BT_2100) {
1080     coeffs = BT2020RGBtoYUVMatrix;
1081   } else if (mDecodedUhdrRgbImage.cg == UHDR_CG_DISPLAY_P3) {
1082     coeffs = BT601RGBtoYUVMatrix;
1083   } else {
1084     std::cerr << "color matrix not present for gamut " << mDecodedUhdrRgbImage.cg
1085               << " using BT2020Matrix" << std::endl;
1086   }
1087 
1088   size_t bpp = 2;
1089   mDecodedUhdrYuv444Image.fmt = static_cast<uhdr_img_fmt_t>(UHDR_IMG_FMT_48bppYCbCr444);
1090   mDecodedUhdrYuv444Image.cg = mDecodedUhdrRgbImage.cg;
1091   mDecodedUhdrYuv444Image.ct = mDecodedUhdrRgbImage.ct;
1092   mDecodedUhdrYuv444Image.range = mRawP010Image.range;
1093   mDecodedUhdrYuv444Image.w = mDecodedUhdrRgbImage.w;
1094   mDecodedUhdrYuv444Image.h = mDecodedUhdrRgbImage.h;
1095   mDecodedUhdrYuv444Image.planes[UHDR_PLANE_Y] =
1096       malloc(bpp * mDecodedUhdrRgbImage.w * mDecodedUhdrRgbImage.h);
1097   mDecodedUhdrYuv444Image.planes[UHDR_PLANE_U] =
1098       malloc(bpp * mDecodedUhdrRgbImage.w * mDecodedUhdrRgbImage.h);
1099   mDecodedUhdrYuv444Image.planes[UHDR_PLANE_V] =
1100       malloc(bpp * mDecodedUhdrRgbImage.w * mDecodedUhdrRgbImage.h);
1101   mDecodedUhdrYuv444Image.stride[UHDR_PLANE_Y] = mWidth;
1102   mDecodedUhdrYuv444Image.stride[UHDR_PLANE_U] = mWidth;
1103   mDecodedUhdrYuv444Image.stride[UHDR_PLANE_V] = mWidth;
1104 
1105   uint32_t* rgbData = static_cast<uint32_t*>(mDecodedUhdrRgbImage.planes[UHDR_PLANE_PACKED]);
1106 
1107   uint16_t* yData = static_cast<uint16_t*>(mDecodedUhdrYuv444Image.planes[UHDR_PLANE_Y]);
1108   uint16_t* uData = static_cast<uint16_t*>(mDecodedUhdrYuv444Image.planes[UHDR_PLANE_U]);
1109   uint16_t* vData = static_cast<uint16_t*>(mDecodedUhdrYuv444Image.planes[UHDR_PLANE_V]);
1110 
1111   for (size_t i = 0; i < mDecodedUhdrRgbImage.h; i++) {
1112     for (size_t j = 0; j < mDecodedUhdrRgbImage.w; j++) {
1113       float r0 = float(rgbData[mDecodedUhdrRgbImage.stride[UHDR_PLANE_PACKED] * i + j] & 0x3ff);
1114       float g0 =
1115           float((rgbData[mDecodedUhdrRgbImage.stride[UHDR_PLANE_PACKED] * i + j] >> 10) & 0x3ff);
1116       float b0 =
1117           float((rgbData[mDecodedUhdrRgbImage.stride[UHDR_PLANE_PACKED] * i + j] >> 20) & 0x3ff);
1118 
1119       r0 /= 1023.0f;
1120       g0 /= 1023.0f;
1121       b0 /= 1023.0f;
1122 
1123       float y = coeffs[0] * r0 + coeffs[1] * g0 + coeffs[2] * b0;
1124       float u = coeffs[3] * r0 + coeffs[4] * g0 + coeffs[5] * b0;
1125       float v = coeffs[6] * r0 + coeffs[7] * g0 + coeffs[8] * b0;
1126 
1127       if (mRawP010Image.range == UHDR_CR_FULL_RANGE) {
1128         y = y * 1023.0f + 0.5f;
1129         u = (u + 0.5f) * 1023.0f + 0.5f;
1130         v = (v + 0.5f) * 1023.0f + 0.5f;
1131 
1132         y = CLIP3(y, 0.0f, 1023.0f);
1133         u = CLIP3(u, 0.0f, 1023.0f);
1134         v = CLIP3(v, 0.0f, 1023.0f);
1135       } else {
1136         y = (y * 876.0f) + 64.0f + 0.5f;
1137         u = (u * 896.0f) + 512.0f + 0.5f;
1138         v = (v * 896.0f) + 512.0f + 0.5f;
1139 
1140         y = CLIP3(y, 64.0f, 940.0f);
1141         u = CLIP3(u, 64.0f, 960.0f);
1142         v = CLIP3(v, 64.0f, 960.0f);
1143       }
1144 
1145       yData[mDecodedUhdrYuv444Image.stride[UHDR_PLANE_Y] * i + j] = uint16_t(y);
1146       uData[mDecodedUhdrYuv444Image.stride[UHDR_PLANE_U] * i + j] = uint16_t(u);
1147       vData[mDecodedUhdrYuv444Image.stride[UHDR_PLANE_V] * i + j] = uint16_t(v);
1148     }
1149   }
1150 #ifdef DUMP_DEBUG_DATA
1151   writeFile("outyuv444.yuv", &mDecodedUhdrYuv444Image);
1152 #endif
1153   return true;
1154 }
1155 
computeRGBHdrPSNR()1156 void UltraHdrAppInput::computeRGBHdrPSNR() {
1157   if (mOfmt != UHDR_IMG_FMT_32bppRGBA1010102) {
1158     std::cout << "psnr not supported for output format " << mOfmt << std::endl;
1159     return;
1160   }
1161   uint32_t* rgbDataSrc = static_cast<uint32_t*>(mRawRgba1010102Image.planes[UHDR_PLANE_PACKED]);
1162   uint32_t* rgbDataDst = static_cast<uint32_t*>(mDecodedUhdrRgbImage.planes[UHDR_PLANE_PACKED]);
1163   if (rgbDataSrc == nullptr || rgbDataDst == nullptr) {
1164     std::cerr << "invalid src or dst pointer for psnr computation " << std::endl;
1165     return;
1166   }
1167   if (mRawRgba1010102Image.ct != mDecodedUhdrRgbImage.ct) {
1168     std::cout << "input color transfer and output color transfer are not identical, rgb psnr "
1169                  "results may be unreliable"
1170               << std::endl;
1171   }
1172   if (mRawRgba1010102Image.cg != mDecodedUhdrRgbImage.cg) {
1173     std::cout << "input color gamut and output color gamut are not identical, rgb psnr results "
1174                  "may be unreliable"
1175               << std::endl;
1176   }
1177   uint64_t rSqError = 0, gSqError = 0, bSqError = 0;
1178   for (size_t i = 0; i < (size_t)mDecodedUhdrRgbImage.w * mDecodedUhdrRgbImage.h; i++) {
1179     int rSrc = *rgbDataSrc & 0x3ff;
1180     int rDst = *rgbDataDst & 0x3ff;
1181     rSqError += (rSrc - rDst) * (rSrc - rDst);
1182 
1183     int gSrc = (*rgbDataSrc >> 10) & 0x3ff;
1184     int gDst = (*rgbDataDst >> 10) & 0x3ff;
1185     gSqError += (gSrc - gDst) * (gSrc - gDst);
1186 
1187     int bSrc = (*rgbDataSrc >> 20) & 0x3ff;
1188     int bDst = (*rgbDataDst >> 20) & 0x3ff;
1189     bSqError += (bSrc - bDst) * (bSrc - bDst);
1190 
1191     rgbDataSrc++;
1192     rgbDataDst++;
1193   }
1194   double meanSquareError =
1195       (double)rSqError / ((size_t)mDecodedUhdrRgbImage.w * mDecodedUhdrRgbImage.h);
1196   mPsnr[0] = meanSquareError ? 10 * log10((double)1023 * 1023 / meanSquareError) : 100;
1197 
1198   meanSquareError = (double)gSqError / ((size_t)mDecodedUhdrRgbImage.w * mDecodedUhdrRgbImage.h);
1199   mPsnr[1] = meanSquareError ? 10 * log10((double)1023 * 1023 / meanSquareError) : 100;
1200 
1201   meanSquareError = (double)bSqError / ((size_t)mDecodedUhdrRgbImage.w * mDecodedUhdrRgbImage.h);
1202   mPsnr[2] = meanSquareError ? 10 * log10((double)1023 * 1023 / meanSquareError) : 100;
1203 
1204   std::cout << "psnr rgb: \t" << mPsnr[0] << " \t " << mPsnr[1] << " \t " << mPsnr[2] << std::endl;
1205 }
1206 
computeRGBSdrPSNR()1207 void UltraHdrAppInput::computeRGBSdrPSNR() {
1208   if (mOfmt != UHDR_IMG_FMT_32bppRGBA8888) {
1209     std::cout << "psnr not supported for output format " << mOfmt << std::endl;
1210     return;
1211   }
1212   uint32_t* rgbDataSrc = static_cast<uint32_t*>(mRawRgba8888Image.planes[UHDR_PLANE_PACKED]);
1213   uint32_t* rgbDataDst = static_cast<uint32_t*>(mDecodedUhdrRgbImage.planes[UHDR_PLANE_PACKED]);
1214   if (rgbDataSrc == nullptr || rgbDataDst == nullptr) {
1215     std::cerr << "invalid src or dst pointer for psnr computation " << std::endl;
1216     return;
1217   }
1218 
1219   uint64_t rSqError = 0, gSqError = 0, bSqError = 0;
1220   for (size_t i = 0; i < (size_t)mDecodedUhdrRgbImage.w * mDecodedUhdrRgbImage.h; i++) {
1221     int rSrc = *rgbDataSrc & 0xff;
1222     int rDst = *rgbDataDst & 0xff;
1223     rSqError += (rSrc - rDst) * (rSrc - rDst);
1224 
1225     int gSrc = (*rgbDataSrc >> 8) & 0xff;
1226     int gDst = (*rgbDataDst >> 8) & 0xff;
1227     gSqError += (gSrc - gDst) * (gSrc - gDst);
1228 
1229     int bSrc = (*rgbDataSrc >> 16) & 0xff;
1230     int bDst = (*rgbDataDst >> 16) & 0xff;
1231     bSqError += (bSrc - bDst) * (bSrc - bDst);
1232 
1233     rgbDataSrc++;
1234     rgbDataDst++;
1235   }
1236   double meanSquareError =
1237       (double)rSqError / ((size_t)mDecodedUhdrRgbImage.w * mDecodedUhdrRgbImage.h);
1238   mPsnr[0] = meanSquareError ? 10 * log10((double)255 * 255 / meanSquareError) : 100;
1239 
1240   meanSquareError = (double)gSqError / ((size_t)mDecodedUhdrRgbImage.w * mDecodedUhdrRgbImage.h);
1241   mPsnr[1] = meanSquareError ? 10 * log10((double)255 * 255 / meanSquareError) : 100;
1242 
1243   meanSquareError = (double)bSqError / ((size_t)mDecodedUhdrRgbImage.w * mDecodedUhdrRgbImage.h);
1244   mPsnr[2] = meanSquareError ? 10 * log10((double)255 * 255 / meanSquareError) : 100;
1245 
1246   std::cout << "psnr rgb: \t" << mPsnr[0] << " \t " << mPsnr[1] << " \t " << mPsnr[2] << std::endl;
1247 }
1248 
computeYUVHdrPSNR()1249 void UltraHdrAppInput::computeYUVHdrPSNR() {
1250   if (mOfmt != UHDR_IMG_FMT_32bppRGBA1010102) {
1251     std::cout << "psnr not supported for output format " << mOfmt << std::endl;
1252     return;
1253   }
1254   uint16_t* yDataSrc = static_cast<uint16_t*>(mRawP010Image.planes[UHDR_PLANE_Y]);
1255   uint16_t* uDataSrc = static_cast<uint16_t*>(mRawP010Image.planes[UHDR_PLANE_UV]);
1256   uint16_t* vDataSrc = uDataSrc + 1;
1257 
1258   uint16_t* yDataDst = static_cast<uint16_t*>(mDecodedUhdrYuv444Image.planes[UHDR_PLANE_Y]);
1259   uint16_t* uDataDst = static_cast<uint16_t*>(mDecodedUhdrYuv444Image.planes[UHDR_PLANE_U]);
1260   uint16_t* vDataDst = static_cast<uint16_t*>(mDecodedUhdrYuv444Image.planes[UHDR_PLANE_V]);
1261   if (yDataSrc == nullptr || uDataSrc == nullptr || yDataDst == nullptr || uDataDst == nullptr ||
1262       vDataDst == nullptr) {
1263     std::cerr << "invalid src or dst pointer for psnr computation " << std::endl;
1264     return;
1265   }
1266   if (mRawP010Image.ct != mDecodedUhdrYuv444Image.ct) {
1267     std::cout << "input color transfer and output color transfer are not identical, yuv psnr "
1268                  "results may be unreliable"
1269               << std::endl;
1270   }
1271   if (mRawP010Image.cg != mDecodedUhdrYuv444Image.cg) {
1272     std::cout << "input color gamut and output color gamut are not identical, yuv psnr results "
1273                  "may be unreliable"
1274               << std::endl;
1275   }
1276   if (mRawP010Image.range != mDecodedUhdrYuv444Image.range) {
1277     std::cout << "input range and output range are not identical, yuv psnr results "
1278                  "may be unreliable"
1279               << std::endl;
1280   }
1281 
1282   uint64_t ySqError = 0, uSqError = 0, vSqError = 0;
1283   for (size_t i = 0; i < mDecodedUhdrYuv444Image.h; i++) {
1284     for (size_t j = 0; j < mDecodedUhdrYuv444Image.w; j++) {
1285       int ySrc = (yDataSrc[mRawP010Image.stride[UHDR_PLANE_Y] * i + j] >> 6) & 0x3ff;
1286       if (mRawP010Image.range == UHDR_CR_LIMITED_RANGE) ySrc = CLIP3(ySrc, 64, 940);
1287       int yDst = yDataDst[mDecodedUhdrYuv444Image.stride[UHDR_PLANE_Y] * i + j] & 0x3ff;
1288       ySqError += (ySrc - yDst) * (ySrc - yDst);
1289 
1290       if (i % 2 == 0 && j % 2 == 0) {
1291         int uSrc =
1292             (uDataSrc[mRawP010Image.stride[UHDR_PLANE_UV] * (i / 2) + (j / 2) * 2] >> 6) & 0x3ff;
1293         if (mRawP010Image.range == UHDR_CR_LIMITED_RANGE) uSrc = CLIP3(uSrc, 64, 960);
1294         int uDst = uDataDst[mDecodedUhdrYuv444Image.stride[UHDR_PLANE_U] * i + j] & 0x3ff;
1295         uDst += uDataDst[mDecodedUhdrYuv444Image.stride[UHDR_PLANE_U] * i + j + 1] & 0x3ff;
1296         uDst += uDataDst[mDecodedUhdrYuv444Image.stride[UHDR_PLANE_U] * (i + 1) + j] & 0x3ff;
1297         uDst += uDataDst[mDecodedUhdrYuv444Image.stride[UHDR_PLANE_U] * (i + 1) + j + 1] & 0x3ff;
1298         uDst = (uDst + 2) >> 2;
1299         uSqError += (uSrc - uDst) * (uSrc - uDst);
1300 
1301         int vSrc =
1302             (vDataSrc[mRawP010Image.stride[UHDR_PLANE_UV] * (i / 2) + (j / 2) * 2] >> 6) & 0x3ff;
1303         if (mRawP010Image.range == UHDR_CR_LIMITED_RANGE) vSrc = CLIP3(vSrc, 64, 960);
1304         int vDst = vDataDst[mDecodedUhdrYuv444Image.stride[UHDR_PLANE_V] * i + j] & 0x3ff;
1305         vDst += vDataDst[mDecodedUhdrYuv444Image.stride[UHDR_PLANE_V] * i + j + 1] & 0x3ff;
1306         vDst += vDataDst[mDecodedUhdrYuv444Image.stride[UHDR_PLANE_V] * (i + 1) + j] & 0x3ff;
1307         vDst += vDataDst[mDecodedUhdrYuv444Image.stride[UHDR_PLANE_V] * (i + 1) + j + 1] & 0x3ff;
1308         vDst = (vDst + 2) >> 2;
1309         vSqError += (vSrc - vDst) * (vSrc - vDst);
1310       }
1311     }
1312   }
1313 
1314   double meanSquareError =
1315       (double)ySqError / ((size_t)mDecodedUhdrYuv444Image.w * mDecodedUhdrYuv444Image.h);
1316   mPsnr[0] = meanSquareError ? 10 * log10((double)1023 * 1023 / meanSquareError) : 100;
1317 
1318   meanSquareError =
1319       (double)uSqError / ((size_t)mDecodedUhdrYuv444Image.w * mDecodedUhdrYuv444Image.h / 4);
1320   mPsnr[1] = meanSquareError ? 10 * log10((double)1023 * 1023 / meanSquareError) : 100;
1321 
1322   meanSquareError =
1323       (double)vSqError / ((size_t)mDecodedUhdrYuv444Image.w * mDecodedUhdrYuv444Image.h / 4);
1324   mPsnr[2] = meanSquareError ? 10 * log10((double)1023 * 1023 / meanSquareError) : 100;
1325 
1326   std::cout << "psnr yuv: \t" << mPsnr[0] << " \t " << mPsnr[1] << " \t " << mPsnr[2] << std::endl;
1327 }
1328 
computeYUVSdrPSNR()1329 void UltraHdrAppInput::computeYUVSdrPSNR() {
1330   if (mOfmt != UHDR_IMG_FMT_32bppRGBA8888) {
1331     std::cout << "psnr not supported for output format " << mOfmt << std::endl;
1332     return;
1333   }
1334 
1335   uint8_t* yDataSrc = static_cast<uint8_t*>(mRawYuv420Image.planes[UHDR_PLANE_Y]);
1336   uint8_t* uDataSrc = static_cast<uint8_t*>(mRawYuv420Image.planes[UHDR_PLANE_U]);
1337   uint8_t* vDataSrc = static_cast<uint8_t*>(mRawYuv420Image.planes[UHDR_PLANE_V]);
1338 
1339   uint8_t* yDataDst = static_cast<uint8_t*>(mDecodedUhdrYuv444Image.planes[UHDR_PLANE_Y]);
1340   uint8_t* uDataDst = static_cast<uint8_t*>(mDecodedUhdrYuv444Image.planes[UHDR_PLANE_U]);
1341   uint8_t* vDataDst = static_cast<uint8_t*>(mDecodedUhdrYuv444Image.planes[UHDR_PLANE_V]);
1342 
1343   uint64_t ySqError = 0, uSqError = 0, vSqError = 0;
1344   for (size_t i = 0; i < mDecodedUhdrYuv444Image.h; i++) {
1345     for (size_t j = 0; j < mDecodedUhdrYuv444Image.w; j++) {
1346       int ySrc = yDataSrc[mRawYuv420Image.stride[UHDR_PLANE_Y] * i + j];
1347       int yDst = yDataDst[mDecodedUhdrYuv444Image.stride[UHDR_PLANE_Y] * i + j];
1348       ySqError += (ySrc - yDst) * (ySrc - yDst);
1349 
1350       if (i % 2 == 0 && j % 2 == 0) {
1351         int uSrc = uDataSrc[mRawYuv420Image.stride[UHDR_PLANE_U] * (i / 2) + j / 2];
1352         int uDst = uDataDst[mDecodedUhdrYuv444Image.stride[UHDR_PLANE_U] * i + j];
1353         uDst += uDataDst[mDecodedUhdrYuv444Image.stride[UHDR_PLANE_U] * i + j + 1];
1354         uDst += uDataDst[mDecodedUhdrYuv444Image.stride[UHDR_PLANE_U] * (i + 1) + j];
1355         uDst += uDataDst[mDecodedUhdrYuv444Image.stride[UHDR_PLANE_U] * (i + 1) + j + 1];
1356         uDst = (uDst + 2) >> 2;
1357         uSqError += (uSrc - uDst) * (uSrc - uDst);
1358 
1359         int vSrc = vDataSrc[mRawYuv420Image.stride[UHDR_PLANE_V] * (i / 2) + j / 2];
1360         int vDst = vDataDst[mDecodedUhdrYuv444Image.stride[UHDR_PLANE_V] * i + j];
1361         vDst += vDataDst[mDecodedUhdrYuv444Image.stride[UHDR_PLANE_V] * i + j + 1];
1362         vDst += vDataDst[mDecodedUhdrYuv444Image.stride[UHDR_PLANE_V] * (i + 1) + j];
1363         vDst += vDataDst[mDecodedUhdrYuv444Image.stride[UHDR_PLANE_V] * (i + 1) + j + 1];
1364         vDst = (vDst + 2) >> 2;
1365         vSqError += (vSrc - vDst) * (vSrc - vDst);
1366       }
1367     }
1368   }
1369   double meanSquareError =
1370       (double)ySqError / ((size_t)mDecodedUhdrYuv444Image.w * mDecodedUhdrYuv444Image.h);
1371   mPsnr[0] = meanSquareError ? 10 * log10((double)255 * 255 / meanSquareError) : 100;
1372 
1373   meanSquareError =
1374       (double)uSqError / ((size_t)mDecodedUhdrYuv444Image.w * mDecodedUhdrYuv444Image.h / 4);
1375   mPsnr[1] = meanSquareError ? 10 * log10((double)255 * 255 / meanSquareError) : 100;
1376 
1377   meanSquareError =
1378       (double)vSqError / ((size_t)mDecodedUhdrYuv444Image.w * mDecodedUhdrYuv444Image.h / 4);
1379   mPsnr[2] = meanSquareError ? 10 * log10((double)255 * 255 / meanSquareError) : 100;
1380 
1381   std::cout << "psnr yuv: \t" << mPsnr[0] << " \t " << mPsnr[1] << " \t " << mPsnr[2] << std::endl;
1382 }
1383 
usage(const char * name)1384 static void usage(const char* name) {
1385   fprintf(stderr, "\n## ultra hdr demo application. lib version: v%s \nUsage : %s \n",
1386           UHDR_LIB_VERSION_STR, name);
1387   fprintf(stderr, "    -m    mode of operation. [0:encode, 1:decode] \n");
1388   fprintf(stderr, "\n## encoder options : \n");
1389   fprintf(stderr,
1390           "    -p    raw hdr intent input resource (10-bit), required for encoding scenarios 0, 1, "
1391           "2, 3. \n");
1392   fprintf(
1393       stderr,
1394       "    -y    raw sdr intent input resource (8-bit), required for encoding scenarios 1, 2. \n");
1395   fprintf(stderr,
1396           "    -a    raw hdr intent color format, optional. [0:p010, 4: rgbahalffloat, "
1397           "5:rgba1010102 (default)] \n");
1398   fprintf(stderr,
1399           "    -b    raw sdr intent color format, optional. [1:yuv420, 3:rgba8888 (default)] \n");
1400   fprintf(stderr,
1401           "    -i    compressed sdr intent input resource (jpeg), required for encoding scenarios "
1402           "2, 3, 4. \n");
1403   fprintf(
1404       stderr,
1405       "    -g    compressed gainmap input resource (jpeg), required for encoding scenario 4. \n");
1406   fprintf(stderr, "    -w    input file width, required for encoding scenarios 0, 1, 2, 3. \n");
1407   fprintf(stderr, "    -h    input file height, required for encoding scenarios 0, 1, 2, 3. \n");
1408   fprintf(stderr,
1409           "    -C    hdr intent color gamut, optional. [0:bt709, 1:p3 (default), 2:bt2100] \n");
1410   fprintf(stderr,
1411           "    -c    sdr intent color gamut, optional. [0:bt709 (default), 1:p3, 2:bt2100] \n");
1412   fprintf(stderr,
1413           "    -t    hdr intent color transfer, optional. [0:linear, 1:hlg (default), 2:pq] \n");
1414   fprintf(stderr,
1415           "          It should be noted that not all combinations of input color format and input "
1416           "color transfer are supported. \n"
1417           "          srgb color transfer shall be paired with rgba8888 or yuv420 only. \n"
1418           "          hlg, pq shall be paired with rgba1010102 or p010. \n"
1419           "          linear shall be paired with rgbahalffloat. \n");
1420   fprintf(stderr,
1421           "    -q    quality factor to be used while encoding sdr intent, optional. [0-100], 95 : "
1422           "default.\n");
1423   fprintf(stderr, "    -e    compute psnr, optional. [0:no (default), 1:yes] \n");
1424   fprintf(stderr,
1425           "    -R    color range of hdr intent, optional. [0:narrow-range (default), "
1426           "1:full-range]. \n");
1427   fprintf(stderr,
1428           "    -s    gainmap image downsample factor, optional. [integer values in range [1 - 128] "
1429           "(1 : default)]. \n");
1430   fprintf(stderr,
1431           "    -Q    quality factor to be used while encoding gain map image, optional. [0-100], "
1432           "95 : default. \n");
1433   fprintf(stderr,
1434           "    -G    gamma correction to be applied on the gainmap image, optional. [any positive "
1435           "real number (1.0 : default)].\n");
1436   fprintf(stderr,
1437           "    -M    select multi channel gain map, optional. [0:disable, 1:enable (default)]. \n");
1438   fprintf(
1439       stderr,
1440       "    -D    select encoding preset, optional. [0:real time, 1:best quality (default)]. \n");
1441   fprintf(stderr,
1442           "    -k    min content boost recommendation, must be in linear scale, optional. [any "
1443           "positive real number] \n");
1444   fprintf(stderr,
1445           "    -K    max content boost recommendation, must be in linear scale, optional.[any "
1446           "positive real number] \n");
1447   fprintf(stderr,
1448           "    -L    set target display peak brightness in nits, optional. \n"
1449           "          For HLG content, this defaults to 1000 nits. \n"
1450           "          For PQ content, this defaults to 10000 nits. \n"
1451           "          any real number in range [203, 10000]. \n");
1452   fprintf(stderr, "    -x    binary input resource containing exif data to insert, optional. \n");
1453   fprintf(stderr, "\n## decoder options : \n");
1454   fprintf(stderr, "    -j    ultra hdr compressed input resource, required. \n");
1455   fprintf(
1456       stderr,
1457       "    -o    output transfer function, optional. [0:linear, 1:hlg (default), 2:pq, 3:srgb] \n");
1458   fprintf(
1459       stderr,
1460       "    -O    output color format, optional. [3:rgba8888, 4:rgbahalffloat, 5:rgba1010102 "
1461       "(default)] \n"
1462       "          It should be noted that not all combinations of output color format and output \n"
1463       "          transfer function are supported. \n"
1464       "          srgb output color transfer shall be paired with rgba8888 only. \n"
1465       "          hlg, pq shall be paired with rgba1010102. \n"
1466       "          linear shall be paired with rgbahalffloat. \n");
1467   fprintf(stderr,
1468           "    -u    enable gles acceleration, optional. [0:disable (default), 1:enable]. \n");
1469   fprintf(stderr, "\n## common options : \n");
1470   fprintf(stderr,
1471           "    -z    output filename, optional. \n"
1472           "          in encoding mode, default output filename 'out.jpeg'. \n"
1473           "          in decoding mode, default output filename 'outrgb.raw'. \n");
1474   fprintf(
1475       stderr,
1476       "    -f    gainmap metadata config file. \n"
1477       "          in encoding mode, resource from which gainmap metadata is read, required for "
1478       "encoding scenario 4. \n"
1479       "          in decoding mode, resource to which gainmap metadata is written, optional. \n");
1480   fprintf(stderr, "\n## examples of usage :\n");
1481   fprintf(stderr, "\n## encode scenario 0 :\n");
1482   fprintf(stderr,
1483           "    ultrahdr_app -m 0 -p cosmat_1920x1080_p010.yuv -w 1920 -h 1080 -q 97 -a 0\n");
1484   fprintf(stderr,
1485           "    ultrahdr_app -m 0 -p cosmat_1920x1080_rgba1010102.raw -w 1920 -h 1080 -q 97 -a 5\n");
1486   fprintf(
1487       stderr,
1488       "    ultrahdr_app -m 0 -p cosmat_1920x1080_p010.yuv -w 1920 -h 1080 -q 97 -C 1 -t 2 -a 0\n");
1489   fprintf(stderr,
1490           "    ultrahdr_app -m 0 -p cosmat_1920x1080_rgba1010102.raw -w 1920 -h 1080 -q 97 -C 1 "
1491           "-t 2 -a 5\n");
1492   fprintf(stderr, "\n## encode scenario 1 :\n");
1493   fprintf(stderr,
1494           "    ultrahdr_app -m 0 -p cosmat_1920x1080_p010.yuv -y cosmat_1920x1080_420.yuv -w 1920 "
1495           "-h 1080 -q 97 -a 0 -b 1\n");
1496   fprintf(stderr,
1497           "    ultrahdr_app -m 0 -p cosmat_1920x1080_rgba1010102.raw "
1498           "-y cosmat_1920x1080_rgba8888.raw -w 1920 -h 1080 -q 97 -a 5 -b 3\n");
1499   fprintf(stderr,
1500           "    ultrahdr_app -m 0 -p cosmat_1920x1080_p010.yuv -y cosmat_1920x1080_420.yuv -w 1920 "
1501           "-h 1080 -q 97 -C 2 -c 1 -t 1 -a 0 -b 1\n");
1502   fprintf(stderr,
1503           "    ultrahdr_app -m 0 -p cosmat_1920x1080_rgba1010102.raw "
1504           "-y cosmat_1920x1080_rgba8888.raw -w 1920 -h 1080 -q 97 -C 2 -c 1 -t 1 -a 5 -b 3\n");
1505   fprintf(stderr,
1506           "    ultrahdr_app -m 0 -p cosmat_1920x1080_p010.yuv -y cosmat_1920x1080_420.yuv -w 1920 "
1507           "-h 1080 -q 97 -C 2 -c 1 -t 1 -e 1 -a 0 -b 1\n");
1508   fprintf(stderr, "\n## encode scenario 2 :\n");
1509   fprintf(stderr,
1510           "    ultrahdr_app -m 0 -p cosmat_1920x1080_p010.yuv -y cosmat_1920x1080_420.yuv -i "
1511           "cosmat_1920x1080_420_8bit.jpg -w 1920 -h 1080 -t 1 -o 3 -O 3 -e 1 -a 0 -b 1\n");
1512   fprintf(stderr,
1513           "    ultrahdr_app -m 0 -p cosmat_1920x1080_rgba1010102.raw -y cosmat_1920x1080_420.yuv "
1514           "-i cosmat_1920x1080_420_8bit.jpg -w 1920 -h 1080 -t 1 -o 3 -O 3 -e 1 -a 5 -b 1\n");
1515   fprintf(stderr, "\n## encode scenario 3 :\n");
1516   fprintf(stderr,
1517           "    ultrahdr_app -m 0 -p cosmat_1920x1080_p010.yuv -i cosmat_1920x1080_420_8bit.jpg -w "
1518           "1920 -h 1080 -t 1 -o 1 -O 5 -e 1 -a 0\n");
1519   fprintf(stderr,
1520           "    ultrahdr_app -m 0 -p cosmat_1920x1080_rgba1010102.raw "
1521           "-i cosmat_1920x1080_420_8bit.jpg -w 1920 -h 1080 -t 1 -o 1 -O 5 -e 1 -a 5\n");
1522   fprintf(stderr, "\n## encode scenario 4 :\n");
1523   fprintf(stderr,
1524           "    ultrahdr_app -m 0 -i cosmat_1920x1080_420_8bit.jpg -g cosmat_1920x1080_420_8bit.jpg "
1525           "-f metadata.cfg\n");
1526   fprintf(stderr, "\n## encode at high quality :\n");
1527   fprintf(stderr,
1528           "    ultrahdr_app -m 0 -p hdr_intent.raw -y sdr_intent.raw -w 640 -h 480 -c <select> -C "
1529           "<select> -t <select> -s 1 -M 1 -Q 98 -q 98 -D 1\n");
1530 
1531   fprintf(stderr, "\n## decode api :\n");
1532   fprintf(stderr, "    ultrahdr_app -m 1 -j cosmat_1920x1080_hdr.jpg \n");
1533   fprintf(stderr, "    ultrahdr_app -m 1 -j cosmat_1920x1080_hdr.jpg -o 3 -O 3\n");
1534   fprintf(stderr, "    ultrahdr_app -m 1 -j cosmat_1920x1080_hdr.jpg -o 1 -O 5\n");
1535   fprintf(stderr, "\n");
1536 }
1537 
main(int argc,char * argv[])1538 int main(int argc, char* argv[]) {
1539   char opt_string[] = "p:y:i:g:f:w:h:C:c:t:q:o:O:m:j:e:a:b:z:R:s:M:Q:G:x:u:D:k:K:L:";
1540   char *hdr_intent_raw_file = nullptr, *sdr_intent_raw_file = nullptr, *uhdr_file = nullptr,
1541        *sdr_intent_compressed_file = nullptr, *gainmap_compressed_file = nullptr,
1542        *gainmap_metadata_cfg_file = nullptr, *output_file = nullptr, *exif_file = nullptr;
1543   int width = 0, height = 0;
1544   uhdr_color_gamut_t hdr_cg = UHDR_CG_DISPLAY_P3;
1545   uhdr_color_gamut_t sdr_cg = UHDR_CG_BT_709;
1546   uhdr_img_fmt_t hdr_cf = UHDR_IMG_FMT_32bppRGBA1010102;
1547   uhdr_img_fmt_t sdr_cf = UHDR_IMG_FMT_32bppRGBA8888;
1548   uhdr_color_transfer_t hdr_tf = UHDR_CT_HLG;
1549   int quality = 95;
1550   uhdr_color_transfer_t out_tf = UHDR_CT_HLG;
1551   uhdr_img_fmt_t out_cf = UHDR_IMG_FMT_32bppRGBA1010102;
1552   int mode = -1;
1553   int gainmap_scale_factor = 1;
1554   bool use_multi_channel_gainmap = true;
1555   bool use_full_range_color_hdr = false;
1556   int gainmap_compression_quality = 95;
1557   int compute_psnr = 0;
1558   float gamma = 1.0f;
1559   bool enable_gles = false;
1560   uhdr_enc_preset_t enc_preset = UHDR_USAGE_BEST_QUALITY;
1561   float min_content_boost = FLT_MIN;
1562   float max_content_boost = FLT_MAX;
1563   float target_disp_peak_brightness = -1.0f;
1564   int ch;
1565   while ((ch = getopt_s(argc, argv, opt_string)) != -1) {
1566     switch (ch) {
1567       case 'a':
1568         hdr_cf = static_cast<uhdr_img_fmt_t>(atoi(optarg_s));
1569         break;
1570       case 'b':
1571         sdr_cf = static_cast<uhdr_img_fmt_t>(atoi(optarg_s));
1572         break;
1573       case 'p':
1574         hdr_intent_raw_file = optarg_s;
1575         break;
1576       case 'y':
1577         sdr_intent_raw_file = optarg_s;
1578         break;
1579       case 'i':
1580         sdr_intent_compressed_file = optarg_s;
1581         break;
1582       case 'g':
1583         gainmap_compressed_file = optarg_s;
1584         break;
1585       case 'f':
1586         gainmap_metadata_cfg_file = optarg_s;
1587         break;
1588       case 'w':
1589         width = atoi(optarg_s);
1590         break;
1591       case 'h':
1592         height = atoi(optarg_s);
1593         break;
1594       case 'C':
1595         hdr_cg = static_cast<uhdr_color_gamut_t>(atoi(optarg_s));
1596         break;
1597       case 'c':
1598         sdr_cg = static_cast<uhdr_color_gamut_t>(atoi(optarg_s));
1599         break;
1600       case 't':
1601         hdr_tf = static_cast<uhdr_color_transfer_t>(atoi(optarg_s));
1602         break;
1603       case 'q':
1604         quality = atoi(optarg_s);
1605         break;
1606       case 'O':
1607         out_cf = static_cast<uhdr_img_fmt_t>(atoi(optarg_s));
1608         break;
1609       case 'o':
1610         out_tf = static_cast<uhdr_color_transfer_t>(atoi(optarg_s));
1611         break;
1612       case 'm':
1613         mode = atoi(optarg_s);
1614         break;
1615       case 'R':
1616         use_full_range_color_hdr = atoi(optarg_s) == 1 ? true : false;
1617         break;
1618       // TODO
1619       /*case 'r':
1620         use_full_range_color_sdr = atoi(optarg_s) == 1 ? true : false;
1621         break;*/
1622       case 's':
1623         gainmap_scale_factor = atoi(optarg_s);
1624         break;
1625       case 'M':
1626         use_multi_channel_gainmap = atoi(optarg_s) == 1 ? true : false;
1627         break;
1628       case 'Q':
1629         gainmap_compression_quality = atoi(optarg_s);
1630         break;
1631       case 'G':
1632         gamma = (float)atof(optarg_s);
1633         break;
1634       case 'j':
1635         uhdr_file = optarg_s;
1636         break;
1637       case 'e':
1638         compute_psnr = atoi(optarg_s);
1639         break;
1640       case 'z':
1641         output_file = optarg_s;
1642         break;
1643       case 'x':
1644         exif_file = optarg_s;
1645         break;
1646       case 'u':
1647         enable_gles = atoi(optarg_s) == 1 ? true : false;
1648         break;
1649       case 'D':
1650         enc_preset = static_cast<uhdr_enc_preset_t>(atoi(optarg_s));
1651         break;
1652       case 'k':
1653         min_content_boost = (float)atof(optarg_s);
1654         break;
1655       case 'K':
1656         max_content_boost = (float)atof(optarg_s);
1657         break;
1658       case 'L':
1659         target_disp_peak_brightness = (float)atof(optarg_s);
1660         break;
1661       default:
1662         usage(argv[0]);
1663         return -1;
1664     }
1665   }
1666   if (mode == 0) {
1667     if (width <= 0 && gainmap_metadata_cfg_file == nullptr) {
1668       std::cerr << "did not receive valid image width for encoding. width :  " << width
1669                 << std::endl;
1670       return -1;
1671     }
1672     if (height <= 0 && gainmap_metadata_cfg_file == nullptr) {
1673       std::cerr << "did not receive valid image height for encoding. height :  " << height
1674                 << std::endl;
1675       return -1;
1676     }
1677     if (hdr_intent_raw_file == nullptr &&
1678         (sdr_intent_compressed_file == nullptr || gainmap_compressed_file == nullptr ||
1679          gainmap_metadata_cfg_file == nullptr)) {
1680       std::cerr << "did not receive raw resources for encoding." << std::endl;
1681       return -1;
1682     }
1683     UltraHdrAppInput appInput(
1684         hdr_intent_raw_file, sdr_intent_raw_file, sdr_intent_compressed_file,
1685         gainmap_compressed_file, gainmap_metadata_cfg_file, exif_file,
1686         output_file ? output_file : "out.jpeg", width, height, hdr_cf, sdr_cf, hdr_cg, sdr_cg,
1687         hdr_tf, quality, out_tf, out_cf, use_full_range_color_hdr, gainmap_scale_factor,
1688         gainmap_compression_quality, use_multi_channel_gainmap, gamma, enable_gles, enc_preset,
1689         min_content_boost, max_content_boost, target_disp_peak_brightness);
1690     if (!appInput.encode()) return -1;
1691     if (compute_psnr == 1) {
1692       if (!appInput.decode()) return -1;
1693       if (out_cf == UHDR_IMG_FMT_32bppRGBA8888 && sdr_intent_raw_file != nullptr) {
1694         if (sdr_cf == UHDR_IMG_FMT_12bppYCbCr420) {
1695           appInput.convertYuv420ToRGBImage();
1696         }
1697         appInput.computeRGBSdrPSNR();
1698         if (sdr_cf == UHDR_IMG_FMT_12bppYCbCr420) {
1699           appInput.convertRgba8888ToYUV444Image();
1700           appInput.computeYUVSdrPSNR();
1701         }
1702       } else if (out_cf == UHDR_IMG_FMT_32bppRGBA1010102 && hdr_intent_raw_file != nullptr &&
1703                  hdr_cf != UHDR_IMG_FMT_64bppRGBAHalfFloat) {
1704         if (hdr_cf == UHDR_IMG_FMT_24bppYCbCrP010) {
1705           appInput.convertP010ToRGBImage();
1706         }
1707         appInput.computeRGBHdrPSNR();
1708         if (hdr_cf == UHDR_IMG_FMT_24bppYCbCrP010) {
1709           appInput.convertRgba1010102ToYUV444Image();
1710           appInput.computeYUVHdrPSNR();
1711         }
1712       } else {
1713         std::cerr << "failed to compute psnr " << std::endl;
1714       }
1715     }
1716   } else if (mode == 1) {
1717     if (uhdr_file == nullptr) {
1718       std::cerr << "did not receive resources for decoding " << std::endl;
1719       return -1;
1720     }
1721     UltraHdrAppInput appInput(gainmap_metadata_cfg_file, uhdr_file,
1722                               output_file ? output_file : "outrgb.raw", out_tf, out_cf,
1723                               enable_gles);
1724     if (!appInput.decode()) return -1;
1725   } else {
1726     if (argc > 1) std::cerr << "did not receive valid mode of operation " << mode << std::endl;
1727     usage(argv[0]);
1728     return -1;
1729   }
1730 
1731   return 0;
1732 }
1733