text: /*
   * Copyright (c) 2010-2016 Richard Geldreich, Jr. and Binomial LLC
   * Copyright (c) 2020 FrozenStorm Interactive, Yoann Potinet
   *
   * This software is provided 'as-is', without any express or implied
   * warranty.  In no event will the authors be held liable for any damages
   * arising from the use of this software.
   *
   * Permission is granted to anyone to use this software for any purpose,
   * including commercial applications, and to alter it and redistribute it
   * freely, subject to the following restrictions:
   *
   * 1. The origin of this software must not be misrepresented; you must not
   *    claim that you wrote the original software. If you use this software
   *    in a product, an acknowledgment in the product documentation or credits
   *    is required.
   *
   * 2. Altered source versions must be plainly marked as such, and must not be
   *    misrepresented as being the original software.
   *
   * 3. This notice may not be removed or altered from any source distribution.
   */
  
  #pragma once
  
  #include "crn_image.h"
  #include "crn_data_stream_serializer.h"
  #include "crn_export.h"
  
  namespace crnlib
  {
      enum pixel_format;
  
      namespace image_utils
      {
          enum read_flags_t
          {
              cReadFlagForceSTB = 1,
  
              cReadFlagsAllFlags = 1
          };
  
          CRN_EXPORT bool read_from_stream_stb(data_stream_serializer& serializer, image_u8& img);
          CRN_EXPORT bool read_from_stream_jpgd(data_stream_serializer& serializer, image_u8& img);
          CRN_EXPORT bool read_from_stream(image_u8& dest, data_stream_serializer& serializer, uint read_flags = 0);
          CRN_EXPORT bool read_from_file(image_u8& dest, const char* pFilename, uint read_flags = 0);
  
          // Reads texture from memory, results returned stb_image.c style.
          // *pActual_comps is set to 1, 3, or 4. req_comps must range from 1-4.
          CRN_EXPORT uint8* read_from_memory(const uint8* pImage, int nSize, int* pWidth, int* pHeight, int* pActualComps, int req_comps, const char* pFilename);
  
          enum
          {
              cWriteFlagIgnoreAlpha = 0x00000001,
              cWriteFlagGrayscale = 0x00000002,
  
              cWriteFlagJPEGH1V1 = 0x00010000,
              cWriteFlagJPEGH2V1 = 0x00020000,
              cWriteFlagJPEGH2V2 = 0x00040000,
              cWriteFlagJPEGTwoPass = 0x00080000,
              cWriteFlagJPEGNoChromaDiscrim = 0x00100000,
              cWriteFlagJPEGQualityLevelMask = 0xFF000000,
              cWriteFlagJPEGQualityLevelShift = 24,
          };
  
          const int cLumaComponentIndex = -1;
  
          inline uint create_jpeg_write_flags(uint base_flags, uint quality_level)
          {
              CRNLIB_ASSERT(quality_level <= 100);
              return base_flags | ((quality_level << cWriteFlagJPEGQualityLevelShift) & cWriteFlagJPEGQualityLevelMask);
          }
  
          CRN_EXPORT bool write_to_file(const char* pFilename, const image_u8& img, uint write_flags = 0, int grayscale_comp_index = cLumaComponentIndex);
  
          CRN_EXPORT bool has_alpha(const image_u8& img);
          CRN_EXPORT bool is_normal_map(const image_u8& img, const char* pFilename = nullptr);
          CRN_EXPORT void renorm_normal_map(image_u8& img);
  
          struct resample_params
          {
              resample_params() :
                  m_dst_width(0),
                  m_dst_height(0),
                  m_pFilter("lanczos4"),
                  m_filter_scale(1.0f),
                  m_srgb(true),
                  m_wrapping(false),
                  m_first_comp(0),
                  m_num_comps(4),
                  m_source_gamma(2.2f), // 1.75f
                  m_multithreaded(true)
              {
              }
  
              uint m_dst_width;
              uint m_dst_height;
              const char* m_pFilter;
              float m_filter_scale;
              bool m_srgb;
              bool m_wrapping;
              uint m_first_comp;
              uint m_num_comps;
              float m_source_gamma;
              bool m_multithreaded;
          };
  
          CRN_EXPORT bool resample_single_thread(const image_u8& src, image_u8& dst, const resample_params& params);
          CRN_EXPORT bool resample_multithreaded(const image_u8& src, image_u8& dst, const resample_params& params);
          CRN_EXPORT bool resample(const image_u8& src, image_u8& dst, const resample_params& params);
  
          CRN_EXPORT bool compute_delta(image_u8& dest, image_u8& a, image_u8& b, uint scale = 2);
  
          class CRN_EXPORT error_metrics
          {
          public:
              error_metrics()
              {
                  utils::zero_this(this);
              }
  
              void print(const char* pName) const;
  
              // If num_channels==0, luma error is computed.
              // If pHist != nullptr, it must point to a 256 entry array.
              bool compute(const image_u8& a, const image_u8& b, uint first_channel, uint num_channels, bool average_component_error = true);
  
              uint mMax;
              double mMean;
              double mMeanSquared;
              double mRootMeanSquared;
              double mPeakSNR;
  
              inline bool operator==(const error_metrics& other) const
              {
                  return mPeakSNR == other.mPeakSNR;
              }
  
              inline bool operator<(const error_metrics& other) const
              {
                  return mPeakSNR < other.mPeakSNR;
              }
  
              inline bool operator>(const error_metrics& other) const
              {
                  return mPeakSNR > other.mPeakSNR;
              }
          };
  
          CRN_EXPORT void print_image_metrics(const image_u8& src_img, const image_u8& dst_img);
  
          CRN_EXPORT double compute_block_ssim(uint n, const uint8* pX, const uint8* pY);
          CRN_EXPORT double compute_ssim(const image_u8& a, const image_u8& b, int channel_index);
          CRN_EXPORT void print_ssim(const image_u8& src_img, const image_u8& dst_img);
  
          enum conversion_type
          {
              cConversion_Invalid = -1,
  
              cConversion_To_CCxY,
              cConversion_From_CCxY,
  
              cConversion_To_xGxR,
              cConversion_From_xGxR,
  
              cConversion_To_xGBR,
              cConversion_From_xGBR,
  
              cConversion_To_AGBR,
              cConversion_From_AGBR,
  
              cConversion_XY_to_XYZ,
  
              cConversion_Y_To_A,
  
              cConversion_A_To_RGBA,
              cConversion_Y_To_RGB,
  
              cConversion_To_Y,
  
              cConversionTotal
          };
  
          CRN_EXPORT void convert_image(image_u8& img, conversion_type conv_type);
  
          template<typename image_type>
          inline uint8* pack_image(const image_type& img, const pixel_packer& packer, uint& n)
          {
              n = 0;
  
              if (!packer.is_valid())
              {
                  return nullptr;
              }
  
              const uint width = img.get_width(), height = img.get_height();
              uint dst_pixel_stride = packer.get_pixel_stride();
              uint dst_pitch = width * dst_pixel_stride;
  
              n = dst_pitch * height;
  
              uint8* pImage = static_cast<uint8*>(crnlib_malloc(n));
  
              uint8* pDst = pImage;
              for (uint y = 0; y < height; y++)
              {
                  const typename image_type::color_t* pSrc = img.get_scanline(y);
                  for (uint x = 0; x < width; x++)
                  {
                      pDst = (uint8*)packer.pack(*pSrc++, pDst);
                  }
              }
  
              return pImage;
          }
  
          CRN_EXPORT image_utils::conversion_type get_conversion_type(bool cooking, pixel_format fmt);
  
          CRN_EXPORT image_utils::conversion_type get_image_conversion_type_from_crn_format(crn_format fmt);
  
          CRN_EXPORT double compute_std_dev(uint n, const color_quad_u8* pPixels, uint first_channel, uint num_channels);
  
          CRN_EXPORT uint8* read_image_from_memory(const uint8* pImage, int nSize, int* pWidth, int* pHeight, int* pActualComps, int req_comps, const char* pFilename);
      } // namespace image_utils
  } // namespace crnlib
  