ezEngine  Milestone 7
ImageConversionMixin.h
1 #pragma once
2 
3 #include <CoreUtils/Image/Image.h>
4 #include <CoreUtils/Image/ImageConversion.h>
5 
7 template<typename Impl>
9 {
11  virtual ezResult DoConvert(const ezImage& source, ezImage& target, ezImageFormat::Enum targetFormat) const override
12  {
14  ezImageFormat::GetBitsPerPixel(source.GetImageFormat()) == Impl::s_uiSourceBpp &&
15  ezImageFormat::GetBitsPerPixel(targetFormat) == Impl::s_uiTargetBpp,
16  "Image format pixel size not supported by this conversion routine");
17 
18  static_cast<ezImageHeader&>(target) = source;
19 
20  target.SetImageFormat(targetFormat);
21 
22  target.AllocateImageData();
23 
24  for (ezUInt32 uiArrayIndex = 0; uiArrayIndex < source.GetNumArrayIndices(); uiArrayIndex++)
25  {
26  for (ezUInt32 uiFace = 0; uiFace < source.GetNumFaces(); uiFace++)
27  {
28  for (ezUInt32 uiMipLevel = 0; uiMipLevel < source.GetNumMipLevels(); uiMipLevel++)
29  {
30  Impl::ConvertSubImage(source, target, uiFace, uiMipLevel, uiArrayIndex);
31  }
32  }
33  }
34 
35  return EZ_SUCCESS;
36  }
37 };
38 
40 template<typename Impl>
42 {
43  static void ConvertSubImage(const ezImage& source, ezImage& target, ezUInt32 uiFace, ezUInt32 uiMipLevel, ezUInt32 uiArrayIndex)
44  {
45  const ezUInt32 uiWidth = source.GetWidth(uiMipLevel);
46  const ezUInt32 uiHeight = source.GetHeight(uiMipLevel);
47 
48  const ezUInt32 uiBlockSize = 4;
49 
50  const ezUInt32 uiNumBlocksX = source.GetNumBlocksX(uiMipLevel);
51  const ezUInt32 uiNumBlocksY = source.GetNumBlocksY(uiMipLevel);
52 
53  // If the row pitch is a multiple of the pixel size, we can transform a whole slice at once
54  // instead of converting row-wise.
55  const ezImageFormat::Enum sourceFormat = source.GetImageFormat();
56  const ezImageFormat::Enum targetFormat = target.GetImageFormat();
57 
58  const ezUInt32 uiSourceBytesPerPixel = Impl::s_uiSourceBpp / 8;
59  const ezUInt32 uiTargetBytesPerPixel = Impl::s_uiTargetBpp / 8;
60 
61  const ezUInt32 uiTargetRowPitch = target.GetRowPitch(uiMipLevel);
62 
63  const ezUInt32 uiSourceDepthPitch = source.GetDepthPitch(uiMipLevel);
64  const ezUInt32 uiTargetDepthPitch = target.GetDepthPitch(uiMipLevel);
65 
66  ezUInt32 uiBytesPerBlock = uiBlockSize * uiBlockSize * uiSourceBytesPerPixel;
67 
68  for (ezUInt32 uiSlice = 0; uiSlice < source.GetDepth(uiMipLevel); uiSlice++)
69  {
70  for (ezUInt32 uiBlockY = 0; uiBlockY < uiNumBlocksY; uiBlockY++)
71  {
72  for (ezUInt32 uiBlockX = 0; uiBlockX < uiNumBlocksX; uiBlockX++)
73  {
74  const ezUInt8* pSource = source.GetBlockPointer<ezUInt8>(uiMipLevel, uiFace, uiArrayIndex, uiBlockX, uiBlockY, uiSlice);
75 
76  // Decompress into a temp memory block so we don't have to explicitly handle the case where the image is not a multiple of the block size
77  ezUInt8 tempBuffer[uiBlockSize * uiBlockSize * uiTargetBytesPerPixel];
78 
79  Impl::DecompressBlock(reinterpret_cast<const typename Impl::SourceType*>(pSource), reinterpret_cast<typename Impl::TargetType*>(tempBuffer));
80 
81  ezUInt8* pTarget = target.GetPixelPointer<ezUInt8>(
82  uiMipLevel, uiFace, uiArrayIndex,
83  uiBlockX * uiBlockSize, uiBlockY * uiBlockSize, uiSlice);
84 
85  // Copy into actual target, clamping to image dimensions
86  ezUInt32 uiCopyWidth = ezMath::Min(uiBlockSize, uiWidth - uiBlockX * uiBlockSize);
87  ezUInt32 uiCopyHeight= ezMath::Min(uiBlockSize, uiHeight - uiBlockY * uiBlockSize);
88  for (ezUInt32 uiRow = 0; uiRow < uiCopyHeight; uiRow++)
89  {
90  memcpy(pTarget, &tempBuffer[uiRow * uiBlockSize * uiTargetBytesPerPixel], uiCopyWidth * uiTargetBytesPerPixel);
91  pTarget += uiTargetRowPitch;
92  }
93  }
94  }
95  }
96  }
97 };
98 
100 template<typename Impl>
102 {
103  static void ConvertSubImage(const ezImage& source, ezImage& target, ezUInt32 uiFace, ezUInt32 uiMipLevel, ezUInt32 uiArrayIndex)
104  {
105  const ezUInt32 uiWidth = source.GetWidth(uiMipLevel);
106  const ezUInt32 uiHeight = source.GetHeight(uiMipLevel);
107 
108  // If the row pitch is a multiple of the pixel size, we can transform a whole slice at once
109  // instead of converting row-wise.
110  const ezImageFormat::Enum sourceFormat = source.GetImageFormat();
111  const ezImageFormat::Enum targetFormat = target.GetImageFormat();
112 
113  const ezUInt32 uiSourceBytesPerPixel = Impl::s_uiSourceBpp / 8;
114  const ezUInt32 uiTargetBytesPerPixel = Impl::s_uiTargetBpp / 8;
115 
116  const ezUInt32 uiSourceRowPitch = source.GetRowPitch(uiMipLevel);
117  const ezUInt32 uiTargetRowPitch = target.GetRowPitch(uiMipLevel);
118 
119  const ezUInt32 uiSourceDepthPitch = source.GetDepthPitch(uiMipLevel);
120  const ezUInt32 uiTargetDepthPitch = target.GetDepthPitch(uiMipLevel);
121 
122  const bool bConvertRowWise =
123  (uiSourceRowPitch % uiSourceBytesPerPixel != 0) ||
124  (uiTargetRowPitch % uiTargetBytesPerPixel != 0);
125 
126  for (ezUInt32 uiSlice = 0; uiSlice < source.GetDepth(uiMipLevel); uiSlice++)
127  {
128  const ezUInt8* pSource = source.GetPixelPointer<ezUInt8>(uiMipLevel, uiFace, uiArrayIndex, 0, 0, uiSlice);
129  ezUInt8* pTarget = target.GetPixelPointer<ezUInt8>(uiMipLevel, uiFace, uiArrayIndex, 0, 0, uiSlice);
130 
131  if (bConvertRowWise)
132  {
133  for (ezUInt32 uiRow = 0; uiRow < uiHeight; uiRow++)
134  {
135  ConvertBatch(pSource, pTarget, uiWidth);
136  pSource += uiSourceRowPitch;
137  pTarget += uiTargetRowPitch;
138  }
139  }
140  else
141  {
142  ConvertBatch(pSource, pTarget, uiSourceRowPitch / uiSourceBytesPerPixel * uiHeight);
143  }
144  }
145  }
146 
147  static void ConvertBatch(const ezUInt8* pSource, ezUInt8* pTarget, const ezUInt32 uiElements)
148  {
149  const ezUInt8* pSource16 = ezMemoryUtils::Align(pSource, 16);
150  const ezUInt8* pTarget16 = ezMemoryUtils::Align(pTarget, 16);
151 
152  const ezUInt32 uiSourceBytesPerPixel = Impl::s_uiSourceBpp / 8;
153  const ezUInt32 uiTargetBytesPerPixel = Impl::s_uiTargetBpp / 8;
154 
155  const ezUInt32 uiLeadInSource = static_cast<ezUInt32>(pSource - pSource16);
156  const ezUInt32 uiLeadInTarget = static_cast<ezUInt32>(pTarget - pTarget16);
157 
158  // Check if we can reach a point where source and target are both aligned
159  const bool bAlignmentMatches =
160  (uiLeadInSource % uiSourceBytesPerPixel == 0) &&
161  (uiLeadInTarget % uiTargetBytesPerPixel == 0) &&
162  (uiLeadInSource / uiSourceBytesPerPixel == uiLeadInTarget / uiTargetBytesPerPixel);
163 
164  if (bAlignmentMatches)
165  {
166  // Convert element-wise until we reach alignment
167  const ezUInt32 uiLeadInElements = uiLeadInSource / uiSourceBytesPerPixel;
168  for (ezUInt32 uiElement = 0; uiElement < uiLeadInElements; uiElement++)
169  {
170  Impl::ConvertSingle(
171  reinterpret_cast<const typename Impl::SourceTypeSingle*>(pSource),
172  reinterpret_cast<typename Impl::TargetTypeSingle*>(pTarget));
173  pSource += uiSourceBytesPerPixel;
174  pTarget += uiTargetBytesPerPixel;
175  }
176 
177  // Convert multiple elements for as long as possible
178  const ezUInt32 uiMiddleElements =
179  ((uiElements - uiLeadInElements) / Impl::s_uiMultiConversionSize) * Impl::s_uiMultiConversionSize;
180  for (ezUInt32 uiElement = 0; uiElement < uiMiddleElements; uiElement += Impl::s_uiMultiConversionSize)
181  {
182  Impl::ConvertMultiple(
183  reinterpret_cast<const typename Impl::SourceTypeMultiple*>(pSource),
184  reinterpret_cast<typename Impl::TargetTypeMultiple*>(pTarget));
185  pSource += Impl::s_uiMultiConversionSize * uiSourceBytesPerPixel;
186  pTarget += Impl::s_uiMultiConversionSize * uiTargetBytesPerPixel;
187  }
188 
189  EZ_ASSERT_DEV(uiLeadInElements + uiMiddleElements <= uiElements, "This will result in a memory access violation due to a variable underflow.");
190 
191  // Convert element-wise until the end
192  const ezUInt32 uiLeadOutElements = uiElements - (uiLeadInElements + uiMiddleElements);
193 
194  for (ezUInt32 uiElement = 0; uiElement < uiLeadOutElements; uiElement++)
195  {
196  Impl::ConvertSingle(
197  reinterpret_cast<const typename Impl::SourceTypeSingle*>(pSource),
198  reinterpret_cast<typename Impl::TargetTypeSingle*>(pTarget));
199  pSource += uiSourceBytesPerPixel;
200  pTarget += uiTargetBytesPerPixel;
201  }
202  }
203  else
204  {
205  // Slow path: convert each element by itself
206  for (ezUInt32 uiElement = 0; uiElement < uiElements; uiElement++)
207  {
208  Impl::ConvertSingle(
209  reinterpret_cast<const typename Impl::SourceTypeSingle*>(pSource),
210  reinterpret_cast<typename Impl::TargetTypeSingle*>(pTarget));
211  pSource += uiSourceBytesPerPixel;
212  pTarget += uiTargetBytesPerPixel;
213  }
214  }
215  }
216 };
217