text: /*
  * All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
  * its licensors.
  *
  * For complete copyright and license terms please see the LICENSE at the root of this
  * distribution (the "License"). All use of this software is governed by the License,
  * or, if provided, by the license below or the license accompanying this file. Do not
  * remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  *
  */
  
  #include <ImageProcessing_precompiled.h>
  
  #include <ImageProcessing/ImageObject.h>
  #include <Processing/PixelFormatInfo.h>
  #include <Processing/ImageFlags.h>
  #include <Converters/PixelOperation.h>
  #include <Compressors/ETC2.h>
  
  #include <EtcConfig.h>
  #include <Etc.h>
  #include <EtcImage.h>
  #include <EtcColorFloatRGBA.h>
  
  namespace ImageProcessing
  {
      //limited to 1 thread because AP requires so. We may change to n when AP allocate n thread to a job in the furture
      static const int  MAX_COMP_JOBS = 1;
      static const int MIN_COMP_JOBS = 1;
      static const float  ETC_LOW_EFFORT_LEVEL = 25.0f;
      static const float  ETC_MED_EFFORT_LEVEL = 40.0f;
      static const float  ETC_HIGH_EFFORT_LEVEL = 80.0f;
      
      //Grab the Etc2Comp specific pixel format enum
      static Etc::Image::Format FindEtc2PixelFormat(EPixelFormat fmt)
      {
          switch (fmt)
          {
          case ePixelFormat_EAC_RG11:
              return Etc::Image::Format::RG11;
          case ePixelFormat_EAC_R11:
              return Etc::Image::Format::R11;
          case ePixelFormat_ETC2:
              return Etc::Image::Format::RGB8;
          case ePixelFormat_ETC2a:
              return Etc::Image::Format::RGBA8;
          default:
              return Etc::Image::Format::FORMATS;
          }
      }
  
      //Get the errmetric required for the compression
      static Etc::ErrorMetric FindErrMetric(Etc::Image::Format fmt)
      {
          switch (fmt)
          {
          case Etc::Image::Format::RG11:
              return Etc::ErrorMetric::NORMALXYZ;
          case Etc::Image::Format::R11:
              return Etc::ErrorMetric::NUMERIC;
          case Etc::Image::Format::RGB8:
              return Etc::ErrorMetric::RGBX;
          case  Etc::Image::Format::RGBA8:
              return Etc::ErrorMetric::RGBA;
          default:
              return Etc::ErrorMetric::ERROR_METRICS;
          }
  
      }
  
      //Convert to sRGB format
      static Etc::Image::Format FindGammaEtc2PixelFormat(Etc::Image::Format fmt)
      {
          switch (fmt)
          {
          case Etc::Image::Format::RGB8:
              return Etc::Image::Format::SRGB8;
          case Etc::Image::Format::RGBA8:
              return Etc::Image::Format::SRGBA8;
          case Etc::Image::Format::RGB8A1:
              return Etc::Image::Format::SRGB8A1;
          default:
              return Etc::Image::Format::FORMATS;
          }
      }
  
      bool ETC2Compressor::IsCompressedPixelFormatSupported(EPixelFormat fmt)
      {
          return (FindEtc2PixelFormat(fmt) != Etc::Image::Format::FORMATS);
      }
  
      bool ETC2Compressor::IsUncompressedPixelFormatSupported(EPixelFormat fmt)
      {
          //for uncompress format
          if (fmt == ePixelFormat_R8G8B8A8)
          {
              return true;
          }
          return false;
      }
     
      EPixelFormat ETC2Compressor::GetSuggestedUncompressedFormat(EPixelFormat compressedfmt, EPixelFormat uncompressedfmt)
      {
          return ePixelFormat_R8G8B8A8;
      }
      
      bool ETC2Compressor::DoesSupportDecompress(EPixelFormat fmtDst)
      {
          return false;
      }
      
      IImageObjectPtr ETC2Compressor::CompressImage(IImageObjectPtr srcImage, EPixelFormat fmtDst, 
              const CompressOption *compressOption)
      {
          const size_t srcPixelSize = 4;
  
          //validate input
          EPixelFormat fmtSrc = srcImage->GetPixelFormat();
          
          //src format need to be uncompressed and dst format need to compressed.
          if (!IsUncompressedPixelFormatSupported(fmtSrc) || !IsCompressedPixelFormatSupported(fmtDst))
          {
              return nullptr;
          }
          
          IImageObjectPtr dstImage(srcImage->AllocateImage(fmtDst));
  
          //determinate compression quality
          ICompressor::EQuality quality = ICompressor::eQuality_Normal;
          //get setting from compression option
          if (compressOption)
          {
              quality = compressOption->compressQuality;
          }
  
          float qualityEffort = 0.0f;
          switch (quality)
          {
              case eQuality_Preview:
              case eQuality_Fast:
              {
                  qualityEffort = ETC_LOW_EFFORT_LEVEL;
                  break;
              }
              case eQuality_Normal:
              {
                  qualityEffort = ETC_MED_EFFORT_LEVEL;
                  break;
              }
              default:
              {
                  qualityEffort = ETC_HIGH_EFFORT_LEVEL;
              }
          }
  
          Etc::Image::Format dstEtc2Format = FindEtc2PixelFormat(fmtDst);
          if (srcImage->GetImageFlags() & EIF_SRGBRead)
          {
              dstEtc2Format = FindGammaEtc2PixelFormat(dstEtc2Format);
          }
  
          //use to read pixel data from src image
          IPixelOperationPtr pixelOp = CreatePixelOperation(fmtSrc);
          //get count of bytes per pixel for images
          AZ::u32 pixelBytes = CPixelFormats::GetInstance().GetPixelFormatInfo(fmtSrc)->bitsPerBlock / 8;
  
          const AZ::u32 mipCount = dstImage->GetMipCount();
          for (AZ::u32 mip = 0; mip < mipCount; ++mip)
          {
              const AZ::u32 width = srcImage->GetWidth(mip);
              const AZ::u32 height = srcImage->GetHeight(mip);
  
              // Prepare source data
              AZ::u8* srcMem;
              AZ::u32 srcPitch;
              srcImage->GetImagePointer(mip, srcMem, srcPitch);
              const AZ::u32 pixelCount = srcImage->GetPixelCount(mip);
              
              Etc::ColorFloatRGBA* rgbaPixels = new Etc::ColorFloatRGBA[pixelCount];
              Etc::ColorFloatRGBA* rgbaPixelPtr = rgbaPixels;
              float r, g, b, a;
              for (AZ::u32 pixelIdx = 0; pixelIdx < pixelCount; pixelIdx++, srcMem += pixelBytes, rgbaPixelPtr++)
              {
                  pixelOp->GetRGBA(srcMem, r, g, b, a);
                  rgbaPixelPtr->fA = a;
                  rgbaPixelPtr->fR = r;
                  rgbaPixelPtr->fG = g;
                  rgbaPixelPtr->fB = b;
              }
  
              //Call into etc2Comp lib to compress. https://medium.com/@duhroach/building-a-blazing-fast-etc2-compressor-307f3e9aad99
              Etc::ErrorMetric errMetric = FindErrMetric(dstEtc2Format);
              unsigned char* paucEncodingBits;
              unsigned int uiEncodingBitsBytes;
              unsigned int uiExtendedWidth;
              unsigned int uiExtendedHeight;
              int iEncodingTime_ms;
  
              Etc::Encode(reinterpret_cast<float*>(rgbaPixels),
                  width, height,
                  dstEtc2Format,
                  errMetric,
                  qualityEffort,
                  MIN_COMP_JOBS,
                  MAX_COMP_JOBS,
                  &paucEncodingBits, &uiEncodingBitsBytes,
                  &uiExtendedWidth, &uiExtendedHeight,
                  &iEncodingTime_ms);
  
              AZ::u8* dstMem;
              AZ::u32 dstPitch;
              dstImage->GetImagePointer(mip, dstMem, dstPitch);
  
              memcpy(dstMem, paucEncodingBits, uiEncodingBitsBytes);
              delete[] rgbaPixels;
          }
  
          return dstImage;
      }
  
      IImageObjectPtr ETC2Compressor::DecompressImage(IImageObjectPtr srcImage, EPixelFormat fmtDst)
      {
          //etc2Comp doesn't support decompression
          //Since PVRTexLib support ETC formats too. It may take over the decompression.
          return nullptr;
      }
  
  } // namespace ImageProcessing
  