BlitSurface.cpp 9.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356
  1. // Jolt Physics Library (https://github.com/jrouwe/JoltPhysics)
  2. // SPDX-FileCopyrightText: 2021 Jorrit Rouwe
  3. // SPDX-License-Identifier: MIT
  4. #include <TestFramework.h>
  5. #include <Image/BlitSurface.h>
  6. #include <Image/Surface.h>
  7. #include <Jolt/Core/Color.h>
  8. #include <Jolt/Core/Profiler.h>
  9. //////////////////////////////////////////////////////////////////////////////////////////
  10. // BlitSettings
  11. //////////////////////////////////////////////////////////////////////////////////////////
  12. const BlitSettings BlitSettings::sDefault;
  13. BlitSettings::BlitSettings() :
  14. mConvertRGBToAlpha(false),
  15. mConvertAlphaToRGB(false),
  16. mConvertToGrayScale(false),
  17. mInvertAlpha(false),
  18. mColorKeyAlpha(false),
  19. mColorKeyStart(240, 0, 240),
  20. mColorKeyEnd(255, 15, 255)
  21. {
  22. }
  23. bool BlitSettings::operator == (const BlitSettings &inRHS) const
  24. {
  25. return mConvertRGBToAlpha == inRHS.mConvertRGBToAlpha
  26. && mConvertAlphaToRGB == inRHS.mConvertAlphaToRGB
  27. && mConvertToGrayScale == inRHS.mConvertToGrayScale
  28. && mInvertAlpha == inRHS.mInvertAlpha
  29. && mColorKeyAlpha == inRHS.mColorKeyAlpha
  30. && mColorKeyStart == inRHS.mColorKeyStart
  31. && mColorKeyEnd == inRHS.mColorKeyEnd
  32. && mZoomSettings == inRHS.mZoomSettings;
  33. }
  34. //////////////////////////////////////////////////////////////////////////////////////////
  35. // Converting from one format to another
  36. //////////////////////////////////////////////////////////////////////////////////////////
  37. // The macro COL(s) converts color s to another color given the mapping tables
  38. #define CMP(s, c) map[256 * c + ((s & src_mask[c]) >> src_shift[c])]
  39. #define COL(s) (CMP(s, 0) + CMP(s, 1) + CMP(s, 2) + CMP(s, 3))
  40. static void sComputeTranslationTable(const FormatDescription & inSrcDesc, const FormatDescription & inDstDesc, uint32 *outMask, uint32 *outShift, uint32 *outMap)
  41. {
  42. JPH_PROFILE("sComputeTranslationTable");
  43. // Compute translation tables for each color component
  44. uint32 written_mask = 0;
  45. for (int c = 0; c < 4; ++c)
  46. {
  47. outMask[c] = inSrcDesc.GetComponentMask(c);
  48. outShift[c] = CountTrailingZeros(outMask[c]);
  49. uint32 src_shifted_mask = outMask[c] >> outShift[c];
  50. uint32 dst_mask = inDstDesc.GetComponentMask(c);
  51. uint32 dst_shift = CountTrailingZeros(dst_mask);
  52. uint32 dst_shifted_mask = dst_mask >> dst_shift;
  53. if ((written_mask & dst_mask) != 0)
  54. {
  55. dst_mask = 0;
  56. dst_shift = 0;
  57. dst_shifted_mask = 0;
  58. }
  59. else
  60. written_mask |= dst_mask;
  61. float scale = float(dst_shifted_mask) / src_shifted_mask;
  62. uint32 entry = 0;
  63. if (src_shifted_mask != 0)
  64. for (; entry <= src_shifted_mask; ++entry)
  65. outMap[256 * c + entry] = uint32(round(scale * entry)) << dst_shift;
  66. for (; entry < 256; ++entry)
  67. outMap[256 * c + entry] = dst_mask;
  68. }
  69. }
  70. static bool sConvertImageDifferentTypes(RefConst<Surface> inSrc, Ref<Surface> ioDst)
  71. {
  72. JPH_PROFILE("sConvertImageDifferentTypes");
  73. // Get image properties
  74. int sbpp = inSrc->GetBytesPerPixel();
  75. int dbpp = ioDst->GetBytesPerPixel();
  76. int width = inSrc->GetWidth();
  77. int height = inSrc->GetHeight();
  78. JPH_ASSERT(width == ioDst->GetWidth());
  79. JPH_ASSERT(height == ioDst->GetHeight());
  80. // Compute conversion map
  81. uint32 src_mask[4];
  82. uint32 src_shift[4];
  83. uint32 map[4 * 256];
  84. sComputeTranslationTable(inSrc->GetFormatDescription(), ioDst->GetFormatDescription(), src_mask, src_shift, map);
  85. inSrc->Lock(ESurfaceLockMode::Read);
  86. ioDst->Lock(ESurfaceLockMode::Write);
  87. // Convert the image
  88. for (int y = 0; y < height; ++y)
  89. {
  90. const uint8 *s = inSrc->GetScanLine(y);
  91. const uint8 *s_end = inSrc->GetScanLine(y) + width * sbpp;
  92. uint8 *d = ioDst->GetScanLine(y);
  93. while (s < s_end)
  94. {
  95. uint32 src = 0;
  96. memcpy(&src, s, sbpp);
  97. uint32 dst = COL(src);
  98. memcpy(d, &dst, dbpp);
  99. s += sbpp;
  100. d += dbpp;
  101. }
  102. }
  103. inSrc->UnLock();
  104. ioDst->UnLock();
  105. return true;
  106. }
  107. static bool sConvertImageSameTypes(RefConst<Surface> inSrc, Ref<Surface> ioDst)
  108. {
  109. JPH_PROFILE("sConvertImageSameTypes");
  110. // Get image properties
  111. int dbpp = ioDst->GetBytesPerPixel();
  112. int width = inSrc->GetWidth();
  113. int height = inSrc->GetHeight();
  114. JPH_ASSERT(inSrc->GetFormat() == ioDst->GetFormat());
  115. JPH_ASSERT(dbpp == inSrc->GetBytesPerPixel());
  116. JPH_ASSERT(width == ioDst->GetWidth());
  117. JPH_ASSERT(height == ioDst->GetHeight());
  118. inSrc->Lock(ESurfaceLockMode::Read);
  119. ioDst->Lock(ESurfaceLockMode::Write);
  120. // Copy the image line by line to compensate for stride
  121. for (int y = 0; y < height; ++y)
  122. memcpy(ioDst->GetScanLine(y), inSrc->GetScanLine(y), width * dbpp);
  123. inSrc->UnLock();
  124. ioDst->UnLock();
  125. return true;
  126. }
  127. static bool sConvertImage(RefConst<Surface> inSrc, Ref<Surface> ioDst)
  128. {
  129. JPH_PROFILE("sConvertImage");
  130. if (inSrc->GetFormat() == ioDst->GetFormat())
  131. return sConvertImageSameTypes(inSrc, ioDst);
  132. else
  133. return sConvertImageDifferentTypes(inSrc, ioDst);
  134. }
  135. //////////////////////////////////////////////////////////////////////////////////////////
  136. // Special color conversions
  137. //////////////////////////////////////////////////////////////////////////////////////////
  138. static void sConvertRGBToAlpha(Ref<Surface> ioSurface)
  139. {
  140. JPH_PROFILE("sConvertRGBToAlpha");
  141. // Check surface format
  142. JPH_ASSERT(ioSurface->GetFormat() == ESurfaceFormat::A8R8G8B8);
  143. // Get dimensions of image
  144. int width = ioSurface->GetWidth();
  145. int height = ioSurface->GetHeight();
  146. // Convert RGB values to alpha values
  147. for (int y = 0; y < height; ++y)
  148. {
  149. Color *c = (Color *)ioSurface->GetScanLine(y);
  150. Color *c_end = (Color *)(ioSurface->GetScanLine(y) + width * sizeof(Color));
  151. while (c < c_end)
  152. {
  153. c->a = c->GetIntensity();
  154. ++c;
  155. }
  156. }
  157. }
  158. static void sConvertAlphaToRGB(Ref<Surface> ioSurface)
  159. {
  160. JPH_PROFILE("sConvertAlphaToRGB");
  161. // Check surface format
  162. JPH_ASSERT(ioSurface->GetFormat() == ESurfaceFormat::A8R8G8B8);
  163. // Get dimensions of image
  164. int width = ioSurface->GetWidth();
  165. int height = ioSurface->GetHeight();
  166. // Convert alpha values to RGB values
  167. for (int y = 0; y < height; ++y)
  168. {
  169. Color *c = (Color *)ioSurface->GetScanLine(y);
  170. Color *c_end = (Color *)(ioSurface->GetScanLine(y) + width * sizeof(Color));
  171. while (c < c_end)
  172. {
  173. c->r = c->g = c->b = c->a;
  174. ++c;
  175. }
  176. }
  177. }
  178. static void sConvertToGrayScale(Ref<Surface> ioSurface)
  179. {
  180. JPH_PROFILE("sConvertToGrayScale");
  181. // Check surface format
  182. JPH_ASSERT(ioSurface->GetFormat() == ESurfaceFormat::A8R8G8B8);
  183. // Get dimensions of image
  184. int width = ioSurface->GetWidth();
  185. int height = ioSurface->GetHeight();
  186. // Convert RGB values to grayscale values
  187. for (int y = 0; y < height; ++y)
  188. {
  189. Color *c = (Color *)ioSurface->GetScanLine(y);
  190. Color *c_end = (Color *)(ioSurface->GetScanLine(y) + width * sizeof(Color));
  191. while (c < c_end)
  192. {
  193. uint8 intensity = c->GetIntensity();
  194. c->r = intensity;
  195. c->g = intensity;
  196. c->b = intensity;
  197. ++c;
  198. }
  199. }
  200. }
  201. static void sInvertAlpha(Ref<Surface> ioSurface)
  202. {
  203. JPH_PROFILE("sInvertAlpha");
  204. // Check surface format
  205. JPH_ASSERT(ioSurface->GetFormat() == ESurfaceFormat::A8R8G8B8);
  206. // Get dimensions of image
  207. int width = ioSurface->GetWidth();
  208. int height = ioSurface->GetHeight();
  209. // Invert all alpha values
  210. for (int y = 0; y < height; ++y)
  211. {
  212. Color *c = (Color *)ioSurface->GetScanLine(y);
  213. Color *c_end = (Color *)(ioSurface->GetScanLine(y) + width * sizeof(Color));
  214. while (c < c_end)
  215. {
  216. c->a = uint8(255 - c->a);
  217. ++c;
  218. }
  219. }
  220. }
  221. static void sColorKeyAlpha(Ref<Surface> ioSurface, ColorArg inStart, ColorArg inEnd)
  222. {
  223. JPH_PROFILE("sColorKeyAlpha");
  224. // Check surface format
  225. JPH_ASSERT(ioSurface->GetFormat() == ESurfaceFormat::A8R8G8B8);
  226. // Get dimensions of image
  227. int width = ioSurface->GetWidth();
  228. int height = ioSurface->GetHeight();
  229. // Set alpha values
  230. for (int y = 0; y < height; ++y)
  231. {
  232. Color *c = (Color *)ioSurface->GetScanLine(y);
  233. Color *c_end = (Color *)(ioSurface->GetScanLine(y) + width * sizeof(Color));
  234. while (c < c_end)
  235. {
  236. if (c->r >= inStart.r && c->r <= inEnd.r && c->g >= inStart.g && c->g <= inEnd.g && c->b >= inStart.b && c->b <= inEnd.b)
  237. c->a = 0;
  238. else
  239. c->a = 255;
  240. ++c;
  241. }
  242. }
  243. }
  244. //////////////////////////////////////////////////////////////////////////////////////////
  245. // BlitSurface
  246. //////////////////////////////////////////////////////////////////////////////////////////
  247. bool BlitSurface(RefConst<Surface> inSrc, Ref<Surface> ioDst, const BlitSettings &inBlitSettings)
  248. {
  249. JPH_PROFILE("BlitSurface");
  250. // Do extra conversion options
  251. RefConst<Surface> src = inSrc;
  252. if (inBlitSettings.mConvertRGBToAlpha || inBlitSettings.mConvertAlphaToRGB || inBlitSettings.mConvertToGrayScale || inBlitSettings.mInvertAlpha || inBlitSettings.mColorKeyAlpha)
  253. {
  254. // Do them on A8R8G8B8 format so the conversion routines are simple
  255. Ref<Surface> tmp = new SoftwareSurface(inSrc->GetWidth(), inSrc->GetHeight(), ESurfaceFormat::A8R8G8B8);
  256. sConvertImage(inSrc, tmp);
  257. src = tmp;
  258. // Perform all optional conversions
  259. tmp->Lock(ESurfaceLockMode::ReadWrite);
  260. if (inBlitSettings.mConvertRGBToAlpha)
  261. sConvertRGBToAlpha(tmp);
  262. if (inBlitSettings.mConvertAlphaToRGB)
  263. sConvertAlphaToRGB(tmp);
  264. if (inBlitSettings.mConvertToGrayScale)
  265. sConvertToGrayScale(tmp);
  266. if (inBlitSettings.mInvertAlpha)
  267. sInvertAlpha(tmp);
  268. if (inBlitSettings.mColorKeyAlpha)
  269. sColorKeyAlpha(tmp, inBlitSettings.mColorKeyStart, inBlitSettings.mColorKeyEnd);
  270. tmp->UnLock();
  271. }
  272. if (src->GetWidth() != ioDst->GetWidth() || src->GetHeight() != ioDst->GetHeight())
  273. {
  274. // Zoom the image if the destination size is not equal to the source size
  275. if (!ZoomImage(src, ioDst, inBlitSettings.mZoomSettings))
  276. return false;
  277. }
  278. else
  279. {
  280. // Convert the image if the sizes are equal
  281. if (!sConvertImage(src, ioDst))
  282. return false;
  283. }
  284. return true;
  285. }