/*
 * Copyright 2022 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
 * https://www.apache.org/licenses/LICENSE-2.0> or the MIT license
 * <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your
 * option. This file may not be copied, modified, or distributed
 * except according to those terms.
 */

#ifndef ULTRAHDR_JPEGR_H
#define ULTRAHDR_JPEGR_H

#include <array>
#include <cfloat>

#include "ultrahdr_api.h"
#include "ultrahdr/ultrahdr.h"
#include "ultrahdr/ultrahdrcommon.h"
#include "ultrahdr/jpegdecoderhelper.h"
#include "ultrahdr/jpegencoderhelper.h"

namespace ultrahdr {

// Default configurations
// gainmap image downscale factor
static const int kMapDimensionScaleFactorDefault = 1;
static const int kMapDimensionScaleFactorAndroidDefault = 4;

// JPEG compress quality (0 ~ 100) for base image
static const int kBaseCompressQualityDefault = 95;

// JPEG compress quality (0 ~ 100) for gain map
static const int kMapCompressQualityDefault = 95;
static const int kMapCompressQualityAndroidDefault = 85;

// Gain map calculation
static const bool kUseMultiChannelGainMapDefault = true;
static const bool kUseMultiChannelGainMapAndroidDefault = false;

// encoding preset
static const uhdr_enc_preset_t kEncSpeedPresetDefault = UHDR_USAGE_BEST_QUALITY;
static const uhdr_enc_preset_t kEncSpeedPresetAndroidDefault = UHDR_USAGE_REALTIME;

// Default gamma value for gain map
static const float kGainMapGammaDefault = 1.0f;

// The current JPEGR version that we encode to
static const char* const kJpegrVersion = "1.0";

/*
 * Holds information of jpeg image
 */
struct jpeg_info_struct {
  std::vector<uint8_t> imgData = std::vector<uint8_t>(0);
  std::vector<uint8_t> iccData = std::vector<uint8_t>(0);
  std::vector<uint8_t> exifData = std::vector<uint8_t>(0);
  std::vector<uint8_t> xmpData = std::vector<uint8_t>(0);
  std::vector<uint8_t> isoData = std::vector<uint8_t>(0);
  unsigned int width;
  unsigned int height;
  unsigned int numComponents;
};

/*
 * Holds information of jpegr image
 */
struct jpegr_info_struct {
  unsigned int width;   // copy of primary image width (for easier access)
  unsigned int height;  // copy of primary image height (for easier access)
  jpeg_info_struct* primaryImgInfo = nullptr;
  jpeg_info_struct* gainmapImgInfo = nullptr;
};

typedef struct jpeg_info_struct* j_info_ptr;
typedef struct jpegr_info_struct* jr_info_ptr;

class JpegR {
 public:
  JpegR(void* uhdrGLESCtxt = nullptr,
        int mapDimensionScaleFactor = kMapDimensionScaleFactorAndroidDefault,
        int mapCompressQuality = kMapCompressQualityAndroidDefault,
        bool useMultiChannelGainMap = kUseMultiChannelGainMapAndroidDefault,
        float gamma = kGainMapGammaDefault,
        uhdr_enc_preset_t preset = kEncSpeedPresetAndroidDefault, float minContentBoost = FLT_MIN,
        float maxContentBoost = FLT_MAX, float targetDispPeakBrightness = -1.0f);

  /*!\brief Encode API-0.
   *
   * Create ultrahdr jpeg image from raw hdr intent.
   *
   * Experimental only.
   *
   * Input hdr image is tonemapped to sdr image. A gainmap coefficient is computed between hdr and
   * sdr intent. sdr intent and gain map coefficient are compressed using jpeg encoding. compressed
   * gainmap is appended at the end of compressed sdr image.
   *
   * \param[in]       hdr_intent        hdr intent raw input image descriptor
   * \param[in, out]  dest              output image descriptor to store compressed ultrahdr image
   * \param[in]       quality           quality factor for sdr intent jpeg compression
   * \param[in]       exif              optional exif metadata that needs to be inserted in
   *                                    compressed output
   *
   * \return uhdr_error_info_t #UHDR_CODEC_OK if operation succeeds, uhdr_codec_err_t otherwise.
   */
  uhdr_error_info_t encodeJPEGR(uhdr_raw_image_t* hdr_intent, uhdr_compressed_image_t* dest,
                                int quality, uhdr_mem_block_t* exif);

  /*!\brief Encode API-1.
   *
   * Create ultrahdr jpeg image from raw hdr intent and raw sdr intent.
   *
   * A gainmap coefficient is computed between hdr and sdr intent. sdr intent and gain map
   * coefficient are compressed using jpeg encoding. compressed gainmap is appended at the end of
   * compressed sdr image.
   * NOTE: Color transfer of sdr intent is expected to be sRGB.
   *
   * \param[in]       hdr_intent        hdr intent raw input image descriptor
   * \param[in]       sdr_intent        sdr intent raw input image descriptor
   * \param[in, out]  dest              output image descriptor to store compressed ultrahdr image
   * \param[in]       quality           quality factor for sdr intent jpeg compression
   * \param[in]       exif              optional exif metadata that needs to be inserted in
   *                                    compressed output
   *
   * \return uhdr_error_info_t #UHDR_CODEC_OK if operation succeeds, uhdr_codec_err_t otherwise.
   */
  uhdr_error_info_t 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);

  /*!\brief Encode API-2.
   *
   * Create ultrahdr jpeg image from raw hdr intent, raw sdr intent and compressed sdr intent.
   *
   * A gainmap coefficient is computed between hdr and sdr intent. gain map coefficient is
   * compressed using jpeg encoding. compressed gainmap is appended at the end of compressed sdr
   * intent. ICC profile is added if one isn't present in the sdr intent JPEG image.
   * NOTE: Color transfer of sdr intent is expected to be sRGB.
   * NOTE: sdr intent raw and compressed inputs are expected to be related via compress/decompress
   * operations.
   *
   * \param[in]       hdr_intent               hdr intent raw input image descriptor
   * \param[in]       sdr_intent               sdr intent raw input image descriptor
   * \param[in]       sdr_intent_compressed    sdr intent compressed input image descriptor
   * \param[in, out]  dest                     output image descriptor to store compressed ultrahdr
   *                                           image
   *
   * \return uhdr_error_info_t #UHDR_CODEC_OK if operation succeeds, uhdr_codec_err_t otherwise.
   */
  uhdr_error_info_t 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);

  /*!\brief Encode API-3.
   *
   * Create ultrahdr jpeg image from raw hdr intent and compressed sdr intent.
   *
   * The sdr intent is decoded and a gainmap coefficient is computed between hdr and sdr intent.
   * gain map coefficient is compressed using jpeg encoding. compressed gainmap is appended at the
   * end of compressed sdr image. ICC profile is added if one isn't present in the sdr intent JPEG
   * image.
   * NOTE: Color transfer of sdr intent is expected to be sRGB.
   *
   * \param[in]       hdr_intent               hdr intent raw input image descriptor
   * \param[in]       sdr_intent_compressed    sdr intent compressed input image descriptor
   * \param[in, out]  dest                     output image descriptor to store compressed ultrahdr
   *                                           image
   *
   * \return uhdr_error_info_t #UHDR_CODEC_OK if operation succeeds, uhdr_codec_err_t otherwise.
   */
  uhdr_error_info_t encodeJPEGR(uhdr_raw_image_t* hdr_intent,
                                uhdr_compressed_image_t* sdr_intent_compressed,
                                uhdr_compressed_image_t* dest);

  /*!\brief Encode API-4.
   *
   * Create ultrahdr jpeg image from compressed sdr image and compressed gainmap image
   *
   * compressed gainmap image is added at the end of compressed sdr image. ICC profile is added if
   * one isn't present in the sdr intent compressed image.
   *
   * \param[in]       base_img_compressed      sdr intent compressed input image descriptor
   * \param[in]       gainmap_img_compressed   gainmap compressed image descriptor
   * \param[in]       metadata                 gainmap metadata descriptor
   * \param[in, out]  dest                     output image descriptor to store compressed ultrahdr
   *                                           image
   *
   * \return uhdr_error_info_t #UHDR_CODEC_OK if operation succeeds, uhdr_codec_err_t otherwise.
   */
  uhdr_error_info_t 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);

  /*!\brief Decode API.
   *
   * Decompress ultrahdr jpeg image.
   *
   * NOTE: This method requires that the ultrahdr input image contains an ICC profile with primaries
   * that match those of a color gamut that this library is aware of; Bt.709, Display-P3, or
   * Bt.2100. It also assumes the base image color transfer characteristics are sRGB.
   *
   * \param[in]       uhdr_compressed_img      compressed ultrahdr image descriptor
   * \param[in, out]  dest                     output image descriptor to store decoded output
   * \param[in]       max_display_boost        (optional) the maximum available boost supported by a
   *                                           display, the value must be greater than or equal
   *                                           to 1.0
   * \param[in]       output_ct                (optional) output color transfer
   * \param[in]       output_format            (optional) output pixel format
   * \param[in, out]  gainmap_img              (optional) output image descriptor to store decoded
   *                                           gainmap image
   * \param[in, out]  gainmap_metadata         (optional) descriptor to store gainmap metadata
   *
   * \return uhdr_error_info_t #UHDR_CODEC_OK if operation succeeds, uhdr_codec_err_t otherwise.
   *
   * NOTE: This method only supports single gain map metadata values for fields that allow
   * multi-channel metadata values.
   *
   * NOTE: Not all combinations of output color transfer and output pixel format are supported.
   * Refer below table for supported combinations.
   *         ----------------------------------------------------------------------
   *         |           color transfer	       |          color format            |
   *         ----------------------------------------------------------------------
   *         |                 SDR             |          32bppRGBA8888           |
   *         ----------------------------------------------------------------------
   *         |             HDR_LINEAR          |          64bppRGBAHalfFloat      |
   *         ----------------------------------------------------------------------
   *         |               HDR_PQ            |          32bppRGBA1010102        |
   *         ----------------------------------------------------------------------
   *         |               HDR_HLG           |          32bppRGBA1010102        |
   *         ----------------------------------------------------------------------
   */
  uhdr_error_info_t decodeJPEGR(uhdr_compressed_image_t* uhdr_compressed_img,
                                uhdr_raw_image_t* dest, float max_display_boost = FLT_MAX,
                                uhdr_color_transfer_t output_ct = UHDR_CT_LINEAR,
                                uhdr_img_fmt_t output_format = UHDR_IMG_FMT_64bppRGBAHalfFloat,
                                uhdr_raw_image_t* gainmap_img = nullptr,
                                uhdr_gainmap_metadata_t* gainmap_metadata = nullptr);

  /*!\brief This function parses the bitstream and returns information that is useful for actual
   * decoding. This does not decode the image. That is handled by decodeJPEGR
   *
   * \param[in]       uhdr_compressed_img      compressed ultrahdr image descriptor
   * \param[in, out]  uhdr_image_info          image info descriptor
   *
   * \return uhdr_error_info_t #UHDR_CODEC_OK if operation succeeds, uhdr_codec_err_t otherwise.
   */
  uhdr_error_info_t getJPEGRInfo(uhdr_compressed_image_t* uhdr_compressed_img,
                                 jr_info_ptr uhdr_image_info);

  /*!\brief set gain map dimension scale factor
   * NOTE: Applicable only in encoding scenario
   *
   * \param[in]       mapDimensionScaleFactor      scale factor
   *
   * \return none
   */
  void setMapDimensionScaleFactor(int mapDimensionScaleFactor) {
    this->mMapDimensionScaleFactor = mapDimensionScaleFactor;
  }

  /*!\brief get gain map dimension scale factor
   * NOTE: Applicable only in encoding scenario
   *
   * \return mapDimensionScaleFactor
   */
  int getMapDimensionScaleFactor() { return this->mMapDimensionScaleFactor; }

  /*!\brief set gain map compression quality factor
   * NOTE: Applicable only in encoding scenario
   *
   * \param[in]       mapCompressQuality      quality factor for gain map image compression
   *
   * \return none
   */
  void setMapCompressQuality(int mapCompressQuality) {
    this->mMapCompressQuality = mapCompressQuality;
  }

  /*!\brief get gain map quality factor
   * NOTE: Applicable only in encoding scenario
   *
   * \return quality factor
   */
  int getMapCompressQuality() { return this->mMapCompressQuality; }

  /*!\brief set gain map gamma
   * NOTE: Applicable only in encoding scenario
   *
   * \param[in]       gamma      gamma parameter that is used for gain map calculation
   *
   * \return none
   */
  void setGainMapGamma(float gamma) { this->mGamma = gamma; }

  /*!\brief get gain map gamma
   * NOTE: Applicable only in encoding scenario
   *
   * \return gamma parameter
   */
  float getGainMapGamma() { return this->mGamma; }

  /*!\brief enable / disable multi channel gain map
   * NOTE: Applicable only in encoding scenario
   *
   * \param[in]       useMultiChannelGainMap      enable / disable multi channel gain map
   *
   * \return none
   */
  void setUseMultiChannelGainMap(bool useMultiChannelGainMap) {
    this->mUseMultiChannelGainMap = useMultiChannelGainMap;
  }

  /*!\brief check if multi channel gain map is enabled
   * NOTE: Applicable only in encoding scenario
   *
   * \return true if multi channel gain map is enabled, false otherwise
   */
  bool isUsingMultiChannelGainMap() { return this->mUseMultiChannelGainMap; }

  /*!\brief set gain map min and max content boost
   * NOTE: Applicable only in encoding scenario
   *
   * \param[in]       minBoost      gain map min content boost
   * \param[in]       maxBoost      gain map max content boost
   *
   * \return none
   */
  void setGainMapMinMaxContentBoost(float minBoost, float maxBoost) {
    this->mMinContentBoost = minBoost;
    this->mMaxContentBoost = maxBoost;
  }

  /*!\brief get gain map min max content boost
   * NOTE: Applicable only in encoding scenario
   *
   * \param[out]       minBoost      gain map min content boost
   * \param[out]       maxBoost      gain map max content boost
   *
   * \return none
   */
  void getGainMapMinMaxContentBoost(float& minBoost, float& maxBoost) {
    minBoost = this->mMinContentBoost;
    maxBoost = this->mMaxContentBoost;
  }

  /* \brief Alias of Encode API-0.
   *
   * \deprecated This function is deprecated. Use its alias
   */
  status_t encodeJPEGR(jr_uncompressed_ptr p010_image_ptr, ultrahdr_transfer_function hdr_tf,
                       jr_compressed_ptr dest, int quality, jr_exif_ptr exif);

  /* \brief Alias of Encode API-1.
   *
   * \deprecated This function is deprecated. Use its actual
   */
  status_t 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);

  /* \brief Alias of Encode API-2.
   *
   * \deprecated This function is deprecated. Use its actual
   */
  status_t 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);

  /* \brief Alias of Encode API-3.
   *
   * \deprecated This function is deprecated. Use its actual
   */
  status_t encodeJPEGR(jr_uncompressed_ptr p010_image_ptr, jr_compressed_ptr yuv420jpg_image_ptr,
                       ultrahdr_transfer_function hdr_tf, jr_compressed_ptr dest);

  /* \brief Alias of Encode API-4.
   *
   * \deprecated This function is deprecated. Use its actual
   */
  status_t encodeJPEGR(jr_compressed_ptr yuv420jpg_image_ptr,
                       jr_compressed_ptr gainmapjpg_image_ptr, ultrahdr_metadata_ptr metadata,
                       jr_compressed_ptr dest);

  /* \brief Alias of Decode API
   *
   * \deprecated This function is deprecated. Use its actual
   */
  status_t decodeJPEGR(jr_compressed_ptr jpegr_image_ptr, jr_uncompressed_ptr dest,
                       float max_display_boost = FLT_MAX, jr_exif_ptr exif = nullptr,
                       ultrahdr_output_format output_format = ULTRAHDR_OUTPUT_HDR_LINEAR,
                       jr_uncompressed_ptr gainmap_image_ptr = nullptr,
                       ultrahdr_metadata_ptr metadata = nullptr);

  /* \brief Alias of getJPEGRInfo
   *
   * \deprecated This function is deprecated. Use its actual
   */
  status_t getJPEGRInfo(jr_compressed_ptr jpegr_image_ptr, jr_info_ptr jpegr_image_info_ptr);

  /*!\brief This function receives iso block and / or xmp + exif block and parses gainmap metadata
   * and fill the output descriptor. If both iso block and xmp +exif block are available, then iso
   * block is preferred over xmp. Exif can be useful with xmp for Apple gain maps:
   * https://developer.apple.com/documentation/appkit/applying-apple-hdr-effect-to-your-photos
   *
   * \param[in]       iso_data                  iso memory block
   * \param[in]       iso_size                  iso block size
   * \param[in]       xmp_data                  xmp memory block
   * \param[in]       xmp_size                  xmp block size
   * \param[in]       exif_data                 exif memory block
   * \param[in]       exif_size                 exif block size
   * \param[in, out]  gainmap_metadata          gainmap metadata descriptor
   *
   * \return uhdr_error_info_t #UHDR_CODEC_OK if operation succeeds, uhdr_codec_err_t otherwise.
   */
  uhdr_error_info_t parseGainMapMetadata(uint8_t* iso_data, size_t iso_size, uint8_t* xmp_data,
                                         size_t xmp_size, uint8_t* exif_data, int exif_size,
                                         uhdr_gainmap_metadata_ext_t* uhdr_metadata);

  /*!\brief This method is used to tone map a hdr image
   *
   * \param[in]            hdr_intent      hdr image descriptor
   * \param[in, out]       sdr_intent      sdr image descriptor
   *
   * \return uhdr_error_info_t #UHDR_CODEC_OK if operation succeeds, uhdr_codec_err_t otherwise.
   */
  uhdr_error_info_t toneMap(uhdr_raw_image_t* hdr_intent, uhdr_raw_image_t* sdr_intent);

  /*!\brief This method takes hdr intent and sdr intent and computes gainmap coefficient.
   *
   * This method is called in the encoding pipeline. It takes uncompressed 8-bit and 10-bit yuv
   * images as input and calculates gainmap.
   *
   * NOTE: The input images must be the same resolution.
   * NOTE: The SDR input is assumed to use the sRGB transfer function.
   *
   * \param[in]       sdr_intent               sdr intent raw input image descriptor
   * \param[in]       hdr_intent               hdr intent raw input image descriptor
   * \param[in, out]  gainmap_metadata         gainmap metadata descriptor
   * \param[in, out]  gainmap_img              gainmap image descriptor
   * \param[in]       sdr_is_601               (optional) if sdr_is_601 is true, then use BT.601
   *                                           gamut to represent sdr intent regardless of the value
   *                                           present in the sdr intent image descriptor
   * \param[in]       use_luminance            (optional) used for single channel gainmap. If
   *                                           use_luminance is true, gainmap calculation is based
   *                                           on the pixel's luminance which is a weighted
   *                                           combination of r, g, b channels; otherwise, gainmap
   *                                           calculation is based of the maximun value of r, g, b
   *                                           channels.
   *
   * \return uhdr_error_info_t #UHDR_CODEC_OK if operation succeeds, uhdr_codec_err_t otherwise.
   */
  uhdr_error_info_t 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 = false, bool use_luminance = true);

 protected:
  /*!\brief This method takes sdr intent, gainmap image and gainmap metadata and computes hdr
   * intent. This method is called in the decoding pipeline. The output hdr intent image will have
   * same color gamut as sdr intent.
   *
   * NOTE: The SDR input is assumed to use the sRGB transfer function.
   *
   * \param[in]       sdr_intent               sdr intent raw input image descriptor
   * \param[in]       gainmap_img              gainmap image descriptor
   * \param[in]       gainmap_metadata         gainmap metadata descriptor
   * \param[in]       output_ct                output color transfer
   * \param[in]       output_format            output pixel format
   * \param[in]       max_display_boost        the maximum available boost supported by a
   *                                           display, the value must be greater than or equal
   *                                           to 1.0
   * \param[in, out]  dest                     output image descriptor to store output
   *
   * \return uhdr_error_info_t #UHDR_CODEC_OK if operation succeeds, uhdr_codec_err_t otherwise.
   */
  uhdr_error_info_t 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);

 private:
  /*!\brief compress gainmap image
   *
   * \param[in]       gainmap_img              gainmap image descriptor
   * \param[in]       jpeg_enc_obj             jpeg encoder object handle
   *
   * \return uhdr_error_info_t #UHDR_CODEC_OK if operation succeeds, uhdr_codec_err_t otherwise.
   */
  uhdr_error_info_t compressGainMap(uhdr_raw_image_t* gainmap_img, JpegEncoderHelper* jpeg_enc_obj);

  /*!\brief This method is called to separate base image and gain map image from compressed
   * ultrahdr image
   *
   * \param[in]            jpegr_image               compressed ultrahdr image descriptor
   * \param[in, out]       primary_image             sdr image descriptor
   * \param[in, out]       gainmap_image             gainmap image descriptor
   *
   * \return uhdr_error_info_t #UHDR_CODEC_OK if operation succeeds, uhdr_codec_err_t otherwise.
   */
  uhdr_error_info_t extractPrimaryImageAndGainMap(uhdr_compressed_image_t* jpegr_image,
                                                  uhdr_compressed_image_t* primary_image,
                                                  uhdr_compressed_image_t* gainmap_image);

  /*!\brief This function parses the bitstream and returns metadata that is useful for actual
   * decoding. This does not decode the image. That is handled by decompressImage().
   *
   * \param[in]            jpeg_image      compressed jpeg image descriptor
   * \param[in, out]       image_info      image info descriptor
   * \param[in, out]       img_width       (optional) image width
   * \param[in, out]       img_height      (optional) image height
   *
   * \return uhdr_error_info_t #UHDR_CODEC_OK if operation succeeds, uhdr_codec_err_t otherwise.
   */
  uhdr_error_info_t parseJpegInfo(uhdr_compressed_image_t* jpeg_image, j_info_ptr image_info,
                                  unsigned int* img_width = nullptr,
                                  unsigned int* img_height = nullptr);

  /*!\brief This method takes compressed sdr intent, compressed gainmap coefficient, gainmap
   * metadata and creates a ultrahdr image. This is done by first generating XMP packet from gainmap
   * metadata, then appending in the order,
   *    SOI, APP2 (Exif is present), APP2 (XMP), base image, gain map image.
   *
   * NOTE: In the final output, EXIF package will appear if ONLY ONE of the following conditions is
   * fulfilled:
   * (1) EXIF package is available from outside input. I.e. pExif != nullptr.
   * (2) Compressed sdr intent has EXIF.
   * If both conditions are fulfilled, this method will return error indicating that it is unable to
   * choose which exif to be placed in the bitstream.
   *
   * \param[in]       sdr_intent_compressed    sdr intent image descriptor
   * \param[in]       gainmap_compressed       gainmap intent input image descriptor
   * \param[in]       pExif                    exif block to be placed in the bitstream
   * \param[in]       pIcc                     pointer to icc segment that needs to be added to the
   *                                           compressed image
   * \param[in]       icc_size                 size of icc segment
   * \param[in]       metadata                 gainmap metadata descriptor
   * \param[in, out]  dest                     output image descriptor to store compressed ultrahdr
   *                                           image
   *
   * \return uhdr_error_info_t #UHDR_CODEC_OK if operation succeeds, uhdr_codec_err_t otherwise.
   */
  uhdr_error_info_t 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);

  /*!\brief This method is used to convert a raw image from one gamut space to another gamut space
   * in-place.
   *
   * \param[in, out]  image              raw image descriptor
   * \param[in]       src_encoding       input gamut space
   * \param[in]       dst_encoding       destination gamut space
   *
   * \return uhdr_error_info_t #UHDR_CODEC_OK if operation succeeds, uhdr_codec_err_t otherwise.
   */
  uhdr_error_info_t convertYuv(uhdr_raw_image_t* image, uhdr_color_gamut_t src_encoding,
                               uhdr_color_gamut_t dst_encoding);

  /*
   * This method will check the validity of the input arguments.
   *
   * @param p010_image_ptr uncompressed HDR image in P010 color format
   * @param yuv420_image_ptr pointer to uncompressed SDR image struct. HDR image is expected to
   *                         be in 420p color format
   * @param hdr_tf transfer function of the HDR image
   * @param dest destination of the compressed JPEGR image. Please note that {@code maxLength}
   *             represents the maximum available size of the desitination buffer, and it must be
   *             set before calling this method. If the encoded JPEGR size exceeds
   *             {@code maxLength}, this method will return {@code ERROR_JPEGR_BUFFER_TOO_SMALL}.
   * @return NO_ERROR if the input args are valid, error code is not valid.
   */
  status_t areInputArgumentsValid(jr_uncompressed_ptr p010_image_ptr,
                                  jr_uncompressed_ptr yuv420_image_ptr,
                                  ultrahdr_transfer_function hdr_tf, jr_compressed_ptr dest_ptr);

  /*
   * This method will check the validity of the input arguments.
   *
   * @param p010_image_ptr uncompressed HDR image in P010 color format
   * @param yuv420_image_ptr pointer to uncompressed SDR image struct. HDR image is expected to
   *                         be in 420p color format
   * @param hdr_tf transfer function of the HDR image
   * @param dest destination of the compressed JPEGR image. Please note that {@code maxLength}
   *             represents the maximum available size of the destination buffer, and it must be
   *             set before calling this method. If the encoded JPEGR size exceeds
   *             {@code maxLength}, this method will return {@code ERROR_JPEGR_BUFFER_TOO_SMALL}.
   * @param quality target quality of the JPEG encoding, must be in range of 0-100 where 100 is
   *                the highest quality
   * @return NO_ERROR if the input args are valid, error code is not valid.
   */
  status_t areInputArgumentsValid(jr_uncompressed_ptr p010_image_ptr,
                                  jr_uncompressed_ptr yuv420_image_ptr,
                                  ultrahdr_transfer_function hdr_tf, jr_compressed_ptr dest,
                                  int quality);

  // Configurations
  void* mUhdrGLESCtxt;              // opengl es context
  int mMapDimensionScaleFactor;     // gain map scale factor
  int mMapCompressQuality;          // gain map quality factor
  bool mUseMultiChannelGainMap;     // enable multichannel gain map
  float mGamma;                     // gain map gamma parameter
  uhdr_enc_preset_t mEncPreset;     // encoding speed preset
  float mMinContentBoost;           // min content boost recommendation
  float mMaxContentBoost;           // max content boost recommendation
  float mTargetDispPeakBrightness;  // target display max luminance in nits
};

/*
 * Holds tonemapping results of a pixel
 */
struct GlobalTonemapOutputs {
  std::array<float, 3> rgb_out;
  float y_hdr;
  float y_sdr;
};

/*!\brief Applies a global tone mapping, based on Chrome's HLG/PQ rendering implemented at
 *  https://source.chromium.org/chromium/chromium/src/+/main:ui/gfx/color_transform.cc;l=1197-1252;drc=ac505aff1d29ec3bfcf317cb77d5e196a3664e92
 *
 * \param[in]       rgb_in              hdr intent pixel in array format.
 * \param[in]       headroom            ratio between hdr and sdr peak luminances. Must be greater
 *                                      than 1. If the input is normalized, then this is used to
 *                                      stretch it linearly from [0.0..1.0] to [0.0..headroom]
 * \param[in]       is_normalized       marker to differentiate, if the input is normalized.
 *
 * \return tonemapped pixel in the normalized range [0.0..1.0]
 */
GlobalTonemapOutputs globalTonemap(const std::array<float, 3>& rgb_in, float headroom,
                                   bool is_normalized);

}  // namespace ultrahdr

#endif  // ULTRAHDR_JPEGR_H
