3
0

Gamma.cpp 7.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243
  1. /*
  2. * Copyright (c) Contributors to the Open 3D Engine Project.
  3. * For complete copyright and license terms please see the LICENSE at the root of this distribution.
  4. *
  5. * SPDX-License-Identifier: Apache-2.0 OR MIT
  6. *
  7. */
  8. #include <Atom/ImageProcessing/ImageObject.h>
  9. #include <Processing/ImageToProcess.h>
  10. #include <Processing/PixelFormatInfo.h>
  11. #include <Processing/ImageFlags.h>
  12. #include <Atom/ImageProcessing/PixelFormats.h>
  13. #include <AzCore/Math/Color.h>
  14. #include <Converters/FIR-Weights.h>
  15. #include <Converters/PixelOperation.h>
  16. namespace ImageProcessingAtom
  17. {
  18. ///////////////////////////////////////////////////////////////////////////////////
  19. // Lookup table for a function 'float fn(float x)'.
  20. // Computed function values are stored in the table for x in [0.0; 1.0].
  21. //
  22. // If passed x is less than xMin (xMin must be >= 0) or greater than 1.0,
  23. // then the original function is called.
  24. // Otherwise, a value from the table (linearly interpolated)
  25. // is returned.
  26. template <int TABLE_SIZE>
  27. class FunctionLookupTable
  28. {
  29. public:
  30. FunctionLookupTable(float(*fn)(float x), float xMin, float maxAllowedDifference)
  31. : m_fn(fn)
  32. , m_xMin(xMin)
  33. , m_fMaxDiff(maxAllowedDifference)
  34. {
  35. }
  36. void Initialize() const
  37. {
  38. m_initialized = true;
  39. AZ_Assert(m_xMin >= 0.0f, "wrong initial data for m_xMin");
  40. for (int i = 0; i <= TABLE_SIZE; ++i)
  41. {
  42. const float x = i / (float)TABLE_SIZE;
  43. const float y = (*m_fn)(x);
  44. m_table[i] = y;
  45. }
  46. }
  47. inline float compute(float x) const
  48. {
  49. if (x < m_xMin || x > 1)
  50. {
  51. return m_fn(x);
  52. }
  53. const float f = x * TABLE_SIZE;
  54. const int i = int(f);
  55. if (!m_initialized)
  56. {
  57. Initialize();
  58. }
  59. if (i >= TABLE_SIZE)
  60. {
  61. return m_table[TABLE_SIZE];
  62. }
  63. const float alpha = f - i;
  64. return (1 - alpha) * m_table[i] + alpha * m_table[i + 1];
  65. }
  66. public:
  67. bool Test(const float maxDifferenceAllowed) const
  68. {
  69. if (int(-0.99f) != 0 ||
  70. int(+0.00f) != 0 ||
  71. int(+0.01f) != 0 ||
  72. int(+0.99f) != 0 ||
  73. int(+1.00f) != 1 ||
  74. int(+1.01f) != 1 ||
  75. int(+1.99f) != 1 ||
  76. int(+2.00f) != 2 ||
  77. int(+2.01f) != 2)
  78. {
  79. return false;
  80. }
  81. if (m_xMin < 0)
  82. {
  83. return false;
  84. }
  85. const int n = 1000000;
  86. for (int i = 0; i <= n; ++i)
  87. {
  88. const float x = 1.1f * (i / (float)n);
  89. const float resOriginal = m_fn(x);
  90. const float resTable = compute(x);
  91. const float difference = resOriginal - resTable;
  92. if (fabs(difference) > maxDifferenceAllowed)
  93. {
  94. return false;
  95. }
  96. }
  97. return true;
  98. }
  99. private:
  100. float(* m_fn)(float x);
  101. float m_xMin;
  102. mutable float m_table[TABLE_SIZE + 1];
  103. mutable bool m_initialized = false;
  104. float m_fMaxDiff = 0.0f;
  105. };
  106. static FunctionLookupTable<1024> s_lutGammaToLinear(AZ::Color::ConvertSrgbGammaToLinear, 0.04045f, 0.00001f);
  107. static FunctionLookupTable<1024> s_lutLinearToGamma(AZ::Color::ConvertSrgbLinearToGamma, 0.05f, 0.00001f);
  108. ///////////////////////////////////////////////////////////////////////////////////
  109. ///////////////////////////////////////////////////////////////////////////////////
  110. bool ImageToProcess::GammaToLinearRGBA32F(bool bDeGamma)
  111. {
  112. // return immediately if there is no need to de-gamma image and the source is in the desired format
  113. EPixelFormat srcFmt = m_img->GetPixelFormat();
  114. if (!bDeGamma && (srcFmt == ePixelFormat_R32G32B32A32F))
  115. {
  116. return true;
  117. }
  118. //convert to 32F first
  119. if (!CPixelFormats::GetInstance().IsPixelFormatUncompressed(srcFmt))
  120. {
  121. ConvertFormat(ePixelFormat_R32G32B32A32F);
  122. srcFmt = ePixelFormat_R32G32B32A32F;
  123. }
  124. IImageObjectPtr srcImage = m_img;
  125. EPixelFormat dstFmt = ePixelFormat_R32G32B32A32F;
  126. IImageObjectPtr dstImage(m_img->AllocateImage(dstFmt));
  127. //create pixel operation function for src and dst images
  128. IPixelOperationPtr srcOp = CreatePixelOperation(srcFmt);
  129. IPixelOperationPtr dstOp = CreatePixelOperation(dstFmt);
  130. //get count of bytes per pixel for both src and dst images
  131. uint32 srcPixelBytes = CPixelFormats::GetInstance().GetPixelFormatInfo(srcFmt)->bitsPerBlock / 8;
  132. uint32 dstPixelBytes = CPixelFormats::GetInstance().GetPixelFormatInfo(dstFmt)->bitsPerBlock / 8;
  133. const uint32 dwMips = dstImage->GetMipCount();
  134. float r, g, b, a;
  135. for (uint32 dwMip = 0; dwMip < dwMips; ++dwMip)
  136. {
  137. uint8* srcPixelBuf;
  138. uint32 srcPitch;
  139. srcImage->GetImagePointer(dwMip, srcPixelBuf, srcPitch);
  140. uint8* dstPixelBuf;
  141. uint32 dstPitch;
  142. dstImage->GetImagePointer(dwMip, dstPixelBuf, dstPitch);
  143. const uint32 pixelCount = srcImage->GetPixelCount(dwMip);
  144. for (uint32 i = 0; i < pixelCount; ++i, srcPixelBuf += srcPixelBytes, dstPixelBuf += dstPixelBytes)
  145. {
  146. srcOp->GetRGBA(srcPixelBuf, r, g, b, a);
  147. if (bDeGamma)
  148. {
  149. r = s_lutGammaToLinear.compute(r);
  150. g = s_lutGammaToLinear.compute(g);
  151. b = s_lutGammaToLinear.compute(b);
  152. }
  153. dstOp->SetRGBA(dstPixelBuf, r, g, b, a);
  154. }
  155. }
  156. m_img = dstImage;
  157. if (bDeGamma)
  158. {
  159. m_img->RemoveImageFlags(EIF_SRGBRead);
  160. }
  161. return true;
  162. }
  163. void ImageToProcess::LinearToGamma()
  164. {
  165. if (Get()->HasImageFlags(EIF_SRGBRead))
  166. {
  167. AZ_Assert(false, "%s: input image is already SRGB", __FUNCTION__);
  168. return;
  169. }
  170. if (!CPixelFormats::GetInstance().IsPixelFormatUncompressed(m_img->GetPixelFormat()))
  171. {
  172. AZ_Assert(false, "This is not common user case with compressed format input. But it may continue");
  173. ConvertFormat(ePixelFormat_R32G32B32A32F);
  174. }
  175. EPixelFormat srcFmt = m_img->GetPixelFormat();
  176. IImageObjectPtr srcImage = m_img;
  177. IImageObjectPtr dstImage(m_img->AllocateImage(srcFmt));
  178. //create pixel operation function
  179. IPixelOperationPtr pixelOp = CreatePixelOperation(srcFmt);
  180. //get count of bytes per pixel for both src and dst images
  181. uint32 pixelBytes = CPixelFormats::GetInstance().GetPixelFormatInfo(srcFmt)->bitsPerBlock / 8;
  182. const uint32 dwMips = srcImage->GetMipCount();
  183. float r, g, b, a;
  184. for (uint32 dwMip = 0; dwMip < dwMips; ++dwMip)
  185. {
  186. uint8* srcPixelBuf;
  187. uint32 srcPitch;
  188. srcImage->GetImagePointer(dwMip, srcPixelBuf, srcPitch);
  189. uint8* dstPixelBuf;
  190. uint32 dstPitch;
  191. dstImage->GetImagePointer(dwMip, dstPixelBuf, dstPitch);
  192. const uint32 pixelCount = srcImage->GetPixelCount(dwMip);
  193. for (uint32 i = 0; i < pixelCount; ++i, srcPixelBuf += pixelBytes, dstPixelBuf += pixelBytes)
  194. {
  195. pixelOp->GetRGBA(srcPixelBuf, r, g, b, a);
  196. r = s_lutLinearToGamma.compute(r);
  197. g = s_lutLinearToGamma.compute(g);
  198. b = s_lutLinearToGamma.compute(b);
  199. pixelOp->SetRGBA(dstPixelBuf, r, g, b, a);
  200. }
  201. }
  202. m_img = dstImage;
  203. Get()->AddImageFlags(EIF_SRGBRead);
  204. }
  205. } // namespace ImageProcessingAtom