Counter Strike : Global Offensive Source Code
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

902 lines
27 KiB

  1. //======= Copyright � 1996-2005, Valve Corporation, All rights reserved. ======//
  2. //
  3. // Purpose:
  4. //
  5. //=============================================================================//
  6. #include "nvtc.h"
  7. #include "bitmap/imageformat.h"
  8. #include "basetypes.h"
  9. #include "tier0/dbg.h"
  10. #ifndef _PS3
  11. #include <malloc.h>
  12. #include <memory.h>
  13. #else
  14. #include <stdlib.h>
  15. #endif
  16. #include "mathlib/mathlib.h"
  17. #include "mathlib/vector.h"
  18. #include "tier1/utlmemory.h"
  19. #include "tier1/strtools.h"
  20. #include "mathlib/compressed_vector.h"
  21. // Should be last include
  22. #include "tier0/memdbgon.h"
  23. namespace ImageLoader
  24. {
  25. //-----------------------------------------------------------------------------
  26. // Gamma correction
  27. //-----------------------------------------------------------------------------
  28. static void ConstructFloatGammaTable( float* pTable, float srcGamma, float dstGamma )
  29. {
  30. for( int i = 0; i < 256; i++ )
  31. {
  32. pTable[i] = 255.0 * pow( (float)i / 255.0f, srcGamma / dstGamma );
  33. }
  34. }
  35. void ConstructGammaTable( unsigned char* pTable, float srcGamma, float dstGamma )
  36. {
  37. int v;
  38. for( int i = 0; i < 256; i++ )
  39. {
  40. double f;
  41. f = 255.0 * pow( (float)i / 255.0f, srcGamma / dstGamma );
  42. v = ( int )(f + 0.5f);
  43. if( v < 0 )
  44. {
  45. v = 0;
  46. }
  47. else if( v > 255 )
  48. {
  49. v = 255;
  50. }
  51. pTable[i] = ( unsigned char )v;
  52. }
  53. }
  54. void GammaCorrectRGBA8888( unsigned char *pSrc, unsigned char* pDst, int width, int height, int depth,
  55. unsigned char* pGammaTable )
  56. {
  57. for (int h = 0; h < depth; ++h )
  58. {
  59. for (int i = 0; i < height; ++i )
  60. {
  61. for (int j = 0; j < width; ++j )
  62. {
  63. int idx = (h * width * height + i * width + j) * 4;
  64. // don't gamma correct alpha
  65. pDst[idx] = pGammaTable[pSrc[idx]];
  66. pDst[idx+1] = pGammaTable[pSrc[idx+1]];
  67. pDst[idx+2] = pGammaTable[pSrc[idx+2]];
  68. }
  69. }
  70. }
  71. }
  72. void GammaCorrectRGBA8888( unsigned char *src, unsigned char* dst, int width, int height, int depth,
  73. float srcGamma, float dstGamma )
  74. {
  75. if (srcGamma == dstGamma)
  76. {
  77. if (src != dst)
  78. {
  79. memcpy( dst, src, GetMemRequired( width, height, depth, IMAGE_FORMAT_RGBA8888, false ) );
  80. }
  81. return;
  82. }
  83. static unsigned char gamma[256];
  84. static float lastSrcGamma = -1;
  85. static float lastDstGamma = -1;
  86. if (lastSrcGamma != srcGamma || lastDstGamma != dstGamma)
  87. {
  88. ConstructGammaTable( gamma, srcGamma, dstGamma );
  89. lastSrcGamma = srcGamma;
  90. lastDstGamma = dstGamma;
  91. }
  92. GammaCorrectRGBA8888( src, dst, width, height, depth, gamma );
  93. }
  94. //-----------------------------------------------------------------------------
  95. // Generate a NICE filter kernel
  96. //-----------------------------------------------------------------------------
  97. static void GenerateNiceFilter( float wratio, float hratio, float dratio, int kernelDiameter, float* pKernel, float *pInvKernel )
  98. {
  99. // Compute a kernel. This is a NICE filter
  100. // sinc pi*x * a box from -3 to 3 * sinc ( pi * x/3)
  101. // where x is the pixel # in the destination (shrunken) image.
  102. // only problem here is that the NICE filter has a very large kernel
  103. // (7x7 x wratio x hratio x dratio)
  104. int kernelWidth, kernelHeight, kernelDepth;
  105. float sx, dx, sy, dy, sz, dz;
  106. float flInvFactor = 1.0f;
  107. kernelWidth = kernelHeight = kernelDepth = 1;
  108. sx = dx = sy = dy = sz = dz = 0.0f;
  109. if ( wratio > 1.0f )
  110. {
  111. kernelWidth = ( int )( kernelDiameter * wratio );
  112. dx = 1.0f / (float)wratio;
  113. sx = -((float)kernelDiameter - dx) * 0.5f;
  114. flInvFactor *= wratio;
  115. }
  116. if ( hratio > 1.0f )
  117. {
  118. kernelHeight = ( int )( kernelDiameter * hratio );
  119. dy = 1.0f / (float)hratio;
  120. sy = -((float)kernelDiameter - dy) * 0.5f;
  121. flInvFactor *= hratio;
  122. }
  123. if ( dratio > 1.0f )
  124. {
  125. kernelDepth = ( int )( kernelDiameter * dratio );
  126. dz = 1.0f / (float)dratio;
  127. sz = -((float)kernelDiameter - dz) * 0.5f;
  128. flInvFactor *= dratio;
  129. }
  130. float z = sz;
  131. int h, i, j;
  132. float total = 0.0f;
  133. for ( h = 0; h < kernelDepth; ++h )
  134. {
  135. float y = sy;
  136. for ( i = 0; i < kernelHeight; ++i )
  137. {
  138. float x = sx;
  139. for ( j = 0; j < kernelWidth; ++j )
  140. {
  141. int nKernelIndex = kernelWidth * ( i + h * kernelHeight ) + j;
  142. float d = sqrt( x * x + y * y + z * z );
  143. if (d > kernelDiameter * 0.5f)
  144. {
  145. pKernel[nKernelIndex] = 0.0f;
  146. }
  147. else
  148. {
  149. float t = M_PI * d;
  150. if ( t != 0 )
  151. {
  152. float sinc = sin( t ) / t;
  153. float sinc3 = 3.0f * sin( t / 3.0f ) / t;
  154. pKernel[nKernelIndex] = sinc * sinc3;
  155. }
  156. else
  157. {
  158. pKernel[nKernelIndex] = 1.0f;
  159. }
  160. total += pKernel[nKernelIndex];
  161. }
  162. x += dx;
  163. }
  164. y += dy;
  165. }
  166. z += dz;
  167. }
  168. // normalize
  169. float flInvTotal = (total != 0.0f) ? 1.0f / total : 1.0f;
  170. for ( h = 0; h < kernelDepth; ++h )
  171. {
  172. for ( i = 0; i < kernelHeight; ++i )
  173. {
  174. int nPixel = kernelWidth * ( h * kernelHeight + i );
  175. for ( j = 0; j < kernelWidth; ++j )
  176. {
  177. pKernel[nPixel + j] *= flInvTotal;
  178. pInvKernel[nPixel + j] = flInvFactor * pKernel[nPixel + j];
  179. }
  180. }
  181. }
  182. }
  183. //-----------------------------------------------------------------------------
  184. // Resample an image
  185. //-----------------------------------------------------------------------------
  186. static inline unsigned char Clamp( float x )
  187. {
  188. int idx = (int)(x + 0.5f);
  189. if (idx < 0) idx = 0;
  190. else if (idx > 255) idx = 255;
  191. return idx;
  192. }
  193. inline bool IsPowerOfTwo( int x )
  194. {
  195. return (x & ( x - 1 )) == 0;
  196. }
  197. enum KernelType_t
  198. {
  199. KERNEL_DEFAULT = 0,
  200. KERNEL_NORMALMAP,
  201. KERNEL_ALPHATEST,
  202. };
  203. typedef void (*ApplyKernelFunc_t)( const KernelInfo_t &kernel, const ResampleInfo_t &info, int wratio, int hratio, int dratio, float* gammaToLinear, float *pAlphaResult );
  204. //-----------------------------------------------------------------------------
  205. // Apply Kernel to an image
  206. //-----------------------------------------------------------------------------
  207. template< int type, bool bNiceFilter >
  208. class CKernelWrapper
  209. {
  210. public:
  211. static inline int ActualX( int x, const ResampleInfo_t &info )
  212. {
  213. if ( info.m_nFlags & RESAMPLE_CLAMPS )
  214. return clamp( x, 0, info.m_nSrcWidth - 1 );
  215. // This works since info.m_nSrcWidth is a power of two.
  216. // Even for negative #s!
  217. return x & (info.m_nSrcWidth - 1);
  218. }
  219. static inline int ActualY( int y, const ResampleInfo_t &info )
  220. {
  221. if ( info.m_nFlags & RESAMPLE_CLAMPT )
  222. return clamp( y, 0, info.m_nSrcHeight - 1 );
  223. // This works since info.m_nSrcHeight is a power of two.
  224. // Even for negative #s!
  225. return y & (info.m_nSrcHeight - 1);
  226. }
  227. static inline int ActualZ( int z, const ResampleInfo_t &info )
  228. {
  229. if ( info.m_nFlags & RESAMPLE_CLAMPU )
  230. return clamp( z, 0, info.m_nSrcDepth - 1 );
  231. // This works since info.m_nSrcDepth is a power of two.
  232. // Even for negative #s!
  233. return z & (info.m_nSrcDepth - 1);
  234. }
  235. static void ComputeAveragedColor( const KernelInfo_t &kernel, const ResampleInfo_t &info,
  236. int startX, int startY, int startZ, float *gammaToLinear, float *total )
  237. {
  238. total[0] = total[1] = total[2] = total[3] = 0.0f;
  239. for ( int j = 0, srcZ = startZ; j < kernel.m_nDepth; ++j, ++srcZ )
  240. {
  241. int sz = ActualZ( srcZ, info );
  242. sz *= info.m_nSrcWidth * info.m_nSrcHeight;
  243. for ( int k = 0, srcY = startY; k < kernel.m_nHeight; ++k, ++srcY )
  244. {
  245. int sy = ActualY( srcY, info );
  246. sy *= info.m_nSrcWidth;
  247. int kernelIdx;
  248. if ( bNiceFilter )
  249. {
  250. kernelIdx = kernel.m_nWidth * ( k + j * kernel.m_nHeight );
  251. }
  252. else
  253. {
  254. kernelIdx = 0;
  255. }
  256. for ( int l = 0, srcX = startX; l < kernel.m_nWidth; ++l, ++srcX, ++kernelIdx )
  257. {
  258. int sx = ActualX( srcX, info );
  259. int srcPixel = (sz + sy + sx) << 2;
  260. float flKernelFactor;
  261. if ( bNiceFilter )
  262. {
  263. flKernelFactor = kernel.m_pKernel[kernelIdx];
  264. if ( flKernelFactor == 0.0f )
  265. continue;
  266. }
  267. else
  268. {
  269. flKernelFactor = kernel.m_pKernel[0];
  270. }
  271. if ( type == KERNEL_NORMALMAP )
  272. {
  273. total[0] += flKernelFactor * info.m_pSrc[srcPixel + 0];
  274. total[1] += flKernelFactor * info.m_pSrc[srcPixel + 1];
  275. total[2] += flKernelFactor * info.m_pSrc[srcPixel + 2];
  276. total[3] += flKernelFactor * info.m_pSrc[srcPixel + 3];
  277. }
  278. else if ( type == KERNEL_ALPHATEST )
  279. {
  280. total[0] += flKernelFactor * gammaToLinear[ info.m_pSrc[srcPixel + 0] ];
  281. total[1] += flKernelFactor * gammaToLinear[ info.m_pSrc[srcPixel + 1] ];
  282. total[2] += flKernelFactor * gammaToLinear[ info.m_pSrc[srcPixel + 2] ];
  283. if ( info.m_pSrc[srcPixel + 3] > 192 )
  284. {
  285. total[3] += flKernelFactor * 255.0f;
  286. }
  287. }
  288. else
  289. {
  290. total[0] += flKernelFactor * gammaToLinear[ info.m_pSrc[srcPixel + 0] ];
  291. total[1] += flKernelFactor * gammaToLinear[ info.m_pSrc[srcPixel + 1] ];
  292. total[2] += flKernelFactor * gammaToLinear[ info.m_pSrc[srcPixel + 2] ];
  293. total[3] += flKernelFactor * info.m_pSrc[srcPixel + 3];
  294. }
  295. }
  296. }
  297. }
  298. }
  299. static void AddAlphaToAlphaResult( const KernelInfo_t &kernel, const ResampleInfo_t &info,
  300. int startX, int startY, int startZ, float flAlpha, float *pAlphaResult )
  301. {
  302. for ( int j = 0, srcZ = startZ; j < kernel.m_nDepth; ++j, ++srcZ )
  303. {
  304. int sz = ActualZ( srcZ, info );
  305. sz *= info.m_nSrcWidth * info.m_nSrcHeight;
  306. for ( int k = 0, srcY = startY; k < kernel.m_nHeight; ++k, ++srcY )
  307. {
  308. int sy = ActualY( srcY, info );
  309. sy *= info.m_nSrcWidth;
  310. int kernelIdx;
  311. if ( bNiceFilter )
  312. {
  313. kernelIdx = k * kernel.m_nWidth + j * kernel.m_nWidth * kernel.m_nHeight;
  314. }
  315. else
  316. {
  317. kernelIdx = 0;
  318. }
  319. for ( int l = 0, srcX = startX; l < kernel.m_nWidth; ++l, ++srcX, ++kernelIdx )
  320. {
  321. int sx = ActualX( srcX, info );
  322. int srcPixel = sz + sy + sx;
  323. float flKernelFactor;
  324. if ( bNiceFilter )
  325. {
  326. flKernelFactor = kernel.m_pInvKernel[kernelIdx];
  327. if ( flKernelFactor == 0.0f )
  328. continue;
  329. }
  330. else
  331. {
  332. flKernelFactor = kernel.m_pInvKernel[0];
  333. }
  334. pAlphaResult[srcPixel] += flKernelFactor * flAlpha;
  335. }
  336. }
  337. }
  338. }
  339. static void AdjustAlphaChannel( const KernelInfo_t &kernel, const ResampleInfo_t &info,
  340. int wratio, int hratio, int dratio, float *pAlphaResult )
  341. {
  342. // Find the delta between the alpha + source image
  343. int i, k;
  344. for ( k = 0; k < info.m_nSrcDepth; ++k )
  345. {
  346. for ( i = 0; i < info.m_nSrcHeight; ++i )
  347. {
  348. int dstPixel = i * info.m_nSrcWidth + k * info.m_nSrcWidth * info.m_nSrcHeight;
  349. for ( int j = 0; j < info.m_nSrcWidth; ++j, ++dstPixel )
  350. {
  351. pAlphaResult[dstPixel] = fabs( pAlphaResult[dstPixel] - info.m_pSrc[dstPixel * 4 + 3] );
  352. }
  353. }
  354. }
  355. // Apply the kernel to the image
  356. int nInitialZ = (dratio >> 1) - ((dratio * kernel.m_nDiameter) >> 1);
  357. int nInitialY = (hratio >> 1) - ((hratio * kernel.m_nDiameter) >> 1);
  358. int nInitialX = (wratio >> 1) - ((wratio * kernel.m_nDiameter) >> 1);
  359. float flAlphaThreshhold = (info.m_flAlphaHiFreqThreshhold >= 0 ) ? 255.0f * info.m_flAlphaHiFreqThreshhold : 255.0f * 0.4f;
  360. float flInvFactor = (dratio == 0) ? 1.0f / (hratio * wratio) : 1.0f / (hratio * wratio * dratio);
  361. for ( int h = 0; h < info.m_nDestDepth; ++h )
  362. {
  363. int startZ = dratio * h + nInitialZ;
  364. for ( i = 0; i < info.m_nDestHeight; ++i )
  365. {
  366. int startY = hratio * i + nInitialY;
  367. int dstPixel = ( info.m_nDestWidth * (i + h * info.m_nDestHeight) ) << 2;
  368. for ( int j = 0; j < info.m_nDestWidth; ++j, dstPixel += 4 )
  369. {
  370. if ( info.m_pDest[ dstPixel + 3 ] == 255 )
  371. continue;
  372. int startX = wratio * j + nInitialX;
  373. float flAlphaDelta = 0.0f;
  374. for ( int m = 0, srcZ = startZ; m < dratio; ++m, ++srcZ )
  375. {
  376. int sz = ActualZ( srcZ, info );
  377. sz *= info.m_nSrcWidth * info.m_nSrcHeight;
  378. for ( int k = 0, srcY = startY; k < hratio; ++k, ++srcY )
  379. {
  380. int sy = ActualY( srcY, info );
  381. sy *= info.m_nSrcWidth;
  382. for ( int l = 0, srcX = startX; l < wratio; ++l, ++srcX )
  383. {
  384. // HACK: This temp variable fixes an internal compiler error in vs2005
  385. int temp = srcX;
  386. int sx = ActualX( temp, info );
  387. int srcPixel = sz + sy + sx;
  388. flAlphaDelta += pAlphaResult[srcPixel];
  389. }
  390. }
  391. }
  392. flAlphaDelta *= flInvFactor;
  393. if ( flAlphaDelta > flAlphaThreshhold )
  394. {
  395. info.m_pDest[ dstPixel + 3 ] = 255;
  396. }
  397. }
  398. }
  399. }
  400. }
  401. static void ApplyKernel( const KernelInfo_t &kernel, const ResampleInfo_t &info, int wratio, int hratio, int dratio, float* gammaToLinear, float *pAlphaResult )
  402. {
  403. float invDstGamma = 1.0f / info.m_flDestGamma;
  404. // Apply the kernel to the image
  405. int nInitialZ = (dratio >> 1) - ((dratio * kernel.m_nDiameter) >> 1);
  406. int nInitialY = (hratio >> 1) - ((hratio * kernel.m_nDiameter) >> 1);
  407. int nInitialX = (wratio >> 1) - ((wratio * kernel.m_nDiameter) >> 1);
  408. float flAlphaThreshhold = (info.m_flAlphaThreshhold >= 0 ) ? 255.0f * info.m_flAlphaThreshhold : 255.0f * 0.4f;
  409. for ( int k = 0; k < info.m_nDestDepth; ++k )
  410. {
  411. int startZ = dratio * k + nInitialZ;
  412. for ( int i = 0; i < info.m_nDestHeight; ++i )
  413. {
  414. int startY = hratio * i + nInitialY;
  415. int dstPixel = (i * info.m_nDestWidth + k * info.m_nDestWidth * info.m_nDestHeight) << 2;
  416. for ( int j = 0; j < info.m_nDestWidth; ++j, dstPixel += 4 )
  417. {
  418. int startX = wratio * j + nInitialX;
  419. float total[4];
  420. ComputeAveragedColor( kernel, info, startX, startY, startZ, gammaToLinear, total );
  421. // NOTE: Can't use a table here, we lose too many bits
  422. if( type == KERNEL_NORMALMAP )
  423. {
  424. for ( int ch = 0; ch < 4; ++ ch )
  425. info.m_pDest[ dstPixel + ch ] = Clamp( info.m_flColorGoal[ch] + ( info.m_flColorScale[ch] * ( total[ch] - info.m_flColorGoal[ch] ) ) );
  426. }
  427. else if ( type == KERNEL_ALPHATEST )
  428. {
  429. // If there's more than 40% coverage, then keep the pixel (renormalize the color based on coverage)
  430. float flAlpha = ( total[3] >= flAlphaThreshhold ) ? 255 : 0;
  431. for ( int ch = 0; ch < 3; ++ ch )
  432. info.m_pDest[ dstPixel + ch ] = Clamp( 255.0f * pow( ( info.m_flColorGoal[ch] + ( info.m_flColorScale[ch] * ( ( total[ch] > 0 ? total[ch] : 0 ) - info.m_flColorGoal[ch] ) ) ) / 255.0f, invDstGamma ) );
  433. info.m_pDest[ dstPixel + 3 ] = Clamp( flAlpha );
  434. AddAlphaToAlphaResult( kernel, info, startX, startY, startZ, flAlpha, pAlphaResult );
  435. }
  436. else
  437. {
  438. for ( int ch = 0; ch < 3; ++ ch )
  439. info.m_pDest[ dstPixel + ch ] = Clamp( 255.0f * pow( ( info.m_flColorGoal[ch] + ( info.m_flColorScale[ch] * ( ( total[ch] > 0 ? total[ch] : 0 ) - info.m_flColorGoal[ch] ) ) ) / 255.0f, invDstGamma ) );
  440. info.m_pDest[ dstPixel + 3 ] = Clamp( info.m_flColorGoal[3] + ( info.m_flColorScale[3] * ( total[3] - info.m_flColorGoal[3] ) ) );
  441. }
  442. }
  443. }
  444. if ( type == KERNEL_ALPHATEST )
  445. {
  446. AdjustAlphaChannel( kernel, info, wratio, hratio, dratio, pAlphaResult );
  447. }
  448. }
  449. }
  450. };
  451. typedef CKernelWrapper< KERNEL_DEFAULT, false > ApplyKernelDefault_t;
  452. typedef CKernelWrapper< KERNEL_NORMALMAP, false > ApplyKernelNormalmap_t;
  453. typedef CKernelWrapper< KERNEL_ALPHATEST, false > ApplyKernelAlphatest_t;
  454. typedef CKernelWrapper< KERNEL_DEFAULT, true > ApplyKernelDefaultNice_t;
  455. typedef CKernelWrapper< KERNEL_NORMALMAP, true > ApplyKernelNormalmapNice_t;
  456. typedef CKernelWrapper< KERNEL_ALPHATEST, true > ApplyKernelAlphatestNice_t;
  457. static ApplyKernelFunc_t g_KernelFunc[] =
  458. {
  459. ApplyKernelDefault_t::ApplyKernel,
  460. ApplyKernelNormalmap_t::ApplyKernel,
  461. ApplyKernelAlphatest_t::ApplyKernel,
  462. };
  463. static ApplyKernelFunc_t g_KernelFuncNice[] =
  464. {
  465. ApplyKernelDefaultNice_t::ApplyKernel,
  466. ApplyKernelNormalmapNice_t::ApplyKernel,
  467. ApplyKernelAlphatestNice_t::ApplyKernel,
  468. };
  469. void ComputeNiceFilterKernel( float wratio, float hratio, float dratio, KernelInfo_t *pKernel )
  470. {
  471. // Kernel size is measured in dst pixels
  472. pKernel->m_nDiameter = 6;
  473. // Compute a nice kernel...
  474. pKernel->m_nWidth = ( wratio > 1 ) ? ( int )( pKernel->m_nDiameter * wratio ) : 1;
  475. pKernel->m_nHeight = ( hratio > 1 ) ? ( int )( pKernel->m_nDiameter * hratio ) : 1;
  476. pKernel->m_nDepth = ( dratio > 1 ) ? ( int )( pKernel->m_nDiameter * dratio ) : 1;
  477. // Cache the filter (2d kernels only)....
  478. int power = -1;
  479. if ( (wratio == hratio) && (dratio <= 1) && ( IsPowerOfTwo( pKernel->m_nWidth ) ) && ( IsPowerOfTwo( pKernel->m_nHeight ) ) )
  480. {
  481. power = 0;
  482. int tempWidth = ( int )wratio;
  483. while (tempWidth > 1)
  484. {
  485. ++power;
  486. tempWidth >>= 1;
  487. }
  488. // Don't cache anything bigger than 512x512
  489. if (power >= 10)
  490. {
  491. power = -1;
  492. }
  493. }
  494. static float* s_pKernelCache[10] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
  495. static float* s_pInvKernelCache[10] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
  496. if (power >= 0)
  497. {
  498. if (!s_pKernelCache[power])
  499. {
  500. s_pKernelCache[power] = new float[pKernel->m_nWidth * pKernel->m_nHeight];
  501. s_pInvKernelCache[power] = new float[pKernel->m_nWidth * pKernel->m_nHeight];
  502. GenerateNiceFilter( wratio, hratio, dratio, pKernel->m_nDiameter, s_pKernelCache[power], s_pInvKernelCache[power] );
  503. }
  504. pKernel->m_pKernel = s_pKernelCache[power];
  505. pKernel->m_pInvKernel = s_pInvKernelCache[power];
  506. }
  507. else
  508. {
  509. // Don't cache non-square kernels, or 3d kernels
  510. float *pTempMemory = new float[pKernel->m_nWidth * pKernel->m_nHeight * pKernel->m_nDepth];
  511. float *pTempInvMemory = new float[pKernel->m_nWidth * pKernel->m_nHeight * pKernel->m_nDepth];
  512. GenerateNiceFilter( wratio, hratio, dratio, pKernel->m_nDiameter, pTempMemory, pTempInvMemory );
  513. pKernel->m_pKernel = pTempMemory;
  514. pKernel->m_pInvKernel = pTempInvMemory;
  515. }
  516. }
  517. void CleanupNiceFilterKernel( KernelInfo_t *pKernel )
  518. {
  519. if ( ( pKernel->m_nWidth != pKernel->m_nHeight ) || ( pKernel->m_nDepth > 1 ) || ( pKernel->m_nWidth > 512 ) ||
  520. ( !IsPowerOfTwo( pKernel->m_nWidth ) ) || ( !IsPowerOfTwo( pKernel->m_nHeight ) ) )
  521. {
  522. delete[] pKernel->m_pKernel;
  523. delete[] pKernel->m_pInvKernel;
  524. }
  525. }
  526. bool ResampleRGBA8888( const ResampleInfo_t& info )
  527. {
  528. // No resampling needed, just gamma correction
  529. if ( info.m_nSrcWidth == info.m_nDestWidth && info.m_nSrcHeight == info.m_nDestHeight && info.m_nSrcDepth == info.m_nDestDepth )
  530. {
  531. // Here, we need to gamma convert the source image..
  532. GammaCorrectRGBA8888( info.m_pSrc, info.m_pDest, info.m_nSrcWidth, info.m_nSrcHeight, info.m_nSrcDepth, info.m_flSrcGamma, info.m_flDestGamma );
  533. return true;
  534. }
  535. // fixme: has to be power of two for now.
  536. if( !IsPowerOfTwo(info.m_nSrcWidth) || !IsPowerOfTwo(info.m_nSrcHeight) || !IsPowerOfTwo(info.m_nSrcDepth) ||
  537. !IsPowerOfTwo(info.m_nDestWidth) || !IsPowerOfTwo(info.m_nDestHeight) || !IsPowerOfTwo(info.m_nDestDepth) )
  538. {
  539. return false;
  540. }
  541. // fixme: can only downsample for now.
  542. if( (info.m_nSrcWidth < info.m_nDestWidth) || (info.m_nSrcHeight < info.m_nDestHeight) || (info.m_nSrcDepth < info.m_nDestDepth) )
  543. {
  544. return false;
  545. }
  546. // Compute gamma tables...
  547. static float gammaToLinear[256];
  548. static float lastSrcGamma = -1;
  549. if (lastSrcGamma != info.m_flSrcGamma)
  550. {
  551. ConstructFloatGammaTable( gammaToLinear, info.m_flSrcGamma, 1.0f );
  552. lastSrcGamma = info.m_flSrcGamma;
  553. }
  554. int wratio = info.m_nSrcWidth / info.m_nDestWidth;
  555. int hratio = info.m_nSrcHeight / info.m_nDestHeight;
  556. int dratio = (info.m_nSrcDepth != info.m_nDestDepth) ? info.m_nSrcDepth / info.m_nDestDepth : 0;
  557. KernelInfo_t kernel;
  558. memset( &kernel, 0, sizeof( KernelInfo_t ) );
  559. float pKernelMem[1];
  560. float pInvKernelMem[1];
  561. if ( info.m_nFlags & RESAMPLE_NICE_FILTER )
  562. {
  563. ComputeNiceFilterKernel( wratio, hratio, dratio, &kernel );
  564. }
  565. else
  566. {
  567. // Compute a kernel...
  568. kernel.m_nWidth = wratio;
  569. kernel.m_nHeight = hratio;
  570. kernel.m_nDepth = dratio ? dratio : 1;
  571. kernel.m_nDiameter = 1;
  572. // Simple implementation of a box filter that doesn't block the stack!
  573. pKernelMem[0] = 1.0f / (float)(kernel.m_nWidth * kernel.m_nHeight * kernel.m_nDepth);
  574. pInvKernelMem[0] = 1.0f;
  575. kernel.m_pKernel = pKernelMem;
  576. kernel.m_pInvKernel = pInvKernelMem;
  577. }
  578. float *pAlphaResult = NULL;
  579. KernelType_t type;
  580. if ( info.m_nFlags & RESAMPLE_NORMALMAP )
  581. {
  582. type = KERNEL_NORMALMAP;
  583. }
  584. else if ( info.m_nFlags & RESAMPLE_ALPHATEST )
  585. {
  586. int nSize = info.m_nSrcHeight * info.m_nSrcWidth * info.m_nSrcDepth * sizeof(float);
  587. pAlphaResult = (float*)malloc( nSize );
  588. memset( pAlphaResult, 0, nSize );
  589. type = KERNEL_ALPHATEST;
  590. }
  591. else
  592. {
  593. type = KERNEL_DEFAULT;
  594. }
  595. if ( info.m_nFlags & RESAMPLE_NICE_FILTER )
  596. {
  597. g_KernelFuncNice[type]( kernel, info, wratio, hratio, dratio, gammaToLinear, pAlphaResult );
  598. CleanupNiceFilterKernel( &kernel );
  599. }
  600. else
  601. {
  602. g_KernelFunc[type]( kernel, info, wratio, hratio, dratio, gammaToLinear, pAlphaResult );
  603. }
  604. if ( pAlphaResult )
  605. {
  606. free( pAlphaResult );
  607. }
  608. return true;
  609. }
  610. bool ResampleRGBA16161616( const ResampleInfo_t& info )
  611. {
  612. // HDRFIXME: This is some lame shit right here. (We need to get NICE working, etc, etc.)
  613. // Make sure everything is power of two.
  614. Assert( ( info.m_nSrcWidth & ( info.m_nSrcWidth - 1 ) ) == 0 );
  615. Assert( ( info.m_nSrcHeight & ( info.m_nSrcHeight - 1 ) ) == 0 );
  616. Assert( ( info.m_nDestWidth & ( info.m_nDestWidth - 1 ) ) == 0 );
  617. Assert( ( info.m_nDestHeight & ( info.m_nDestHeight - 1 ) ) == 0 );
  618. // Make sure that we aren't upscsaling the image. . .we do`n't support that very well.
  619. Assert( info.m_nSrcWidth >= info.m_nDestWidth );
  620. Assert( info.m_nSrcHeight >= info.m_nDestHeight );
  621. int nSampleWidth = info.m_nSrcWidth / info.m_nDestWidth;
  622. int nSampleHeight = info.m_nSrcHeight / info.m_nDestHeight;
  623. unsigned short *pSrc = ( unsigned short * )info.m_pSrc;
  624. unsigned short *pDst = ( unsigned short * )info.m_pDest;
  625. int x, y;
  626. for( y = 0; y < info.m_nDestHeight; y++ )
  627. {
  628. for( x = 0; x < info.m_nDestWidth; x++ )
  629. {
  630. int accum[4];
  631. accum[0] = accum[1] = accum[2] = accum[3] = 0;
  632. int nSampleY;
  633. for( nSampleY = 0; nSampleY < nSampleHeight; nSampleY++ )
  634. {
  635. int nSampleX;
  636. for( nSampleX = 0; nSampleX < nSampleWidth; nSampleX++ )
  637. {
  638. accum[0] += ( int )pSrc[((x*nSampleWidth+nSampleX)+(y*nSampleHeight+nSampleY)*info.m_nSrcWidth)*4+0];
  639. accum[1] += ( int )pSrc[((x*nSampleWidth+nSampleX)+(y*nSampleHeight+nSampleY)*info.m_nSrcWidth)*4+1];
  640. accum[2] += ( int )pSrc[((x*nSampleWidth+nSampleX)+(y*nSampleHeight+nSampleY)*info.m_nSrcWidth)*4+2];
  641. accum[3] += ( int )pSrc[((x*nSampleWidth+nSampleX)+(y*nSampleHeight+nSampleY)*info.m_nSrcWidth)*4+3];
  642. }
  643. }
  644. int i;
  645. for( i = 0; i < 4; i++ )
  646. {
  647. accum[i] /= ( nSampleWidth * nSampleHeight );
  648. accum[i] = MAX( accum[i], 0 );
  649. accum[i] = MIN( accum[i], 65535 );
  650. pDst[(x+y*info.m_nDestWidth)*4+i] = ( unsigned short )accum[i];
  651. }
  652. }
  653. }
  654. return true;
  655. }
  656. bool ResampleRGB323232F( const ResampleInfo_t& info )
  657. {
  658. // HDRFIXME: This is some lame shit right here. (We need to get NICE working, etc, etc.)
  659. // Make sure everything is power of two.
  660. Assert( ( info.m_nSrcWidth & ( info.m_nSrcWidth - 1 ) ) == 0 );
  661. Assert( ( info.m_nSrcHeight & ( info.m_nSrcHeight - 1 ) ) == 0 );
  662. Assert( ( info.m_nDestWidth & ( info.m_nDestWidth - 1 ) ) == 0 );
  663. Assert( ( info.m_nDestHeight & ( info.m_nDestHeight - 1 ) ) == 0 );
  664. // Make sure that we aren't upscaling the image. . .we do`n't support that very well.
  665. Assert( info.m_nSrcWidth >= info.m_nDestWidth );
  666. Assert( info.m_nSrcHeight >= info.m_nDestHeight );
  667. int nSampleWidth = info.m_nSrcWidth / info.m_nDestWidth;
  668. int nSampleHeight = info.m_nSrcHeight / info.m_nDestHeight;
  669. float *pSrc = ( float * )info.m_pSrc;
  670. float *pDst = ( float * )info.m_pDest;
  671. for( int y = 0; y < info.m_nDestHeight; y++ )
  672. {
  673. for( int x = 0; x < info.m_nDestWidth; x++ )
  674. {
  675. float accum[4];
  676. accum[0] = accum[1] = accum[2] = accum[3] = 0;
  677. for( int nSampleY = 0; nSampleY < nSampleHeight; nSampleY++ )
  678. {
  679. for( int nSampleX = 0; nSampleX < nSampleWidth; nSampleX++ )
  680. {
  681. accum[0] += pSrc[((x*nSampleWidth+nSampleX)+(y*nSampleHeight+nSampleY)*info.m_nSrcWidth)*3+0];
  682. accum[1] += pSrc[((x*nSampleWidth+nSampleX)+(y*nSampleHeight+nSampleY)*info.m_nSrcWidth)*3+1];
  683. accum[2] += pSrc[((x*nSampleWidth+nSampleX)+(y*nSampleHeight+nSampleY)*info.m_nSrcWidth)*3+2];
  684. }
  685. }
  686. for( int i = 0; i < 3; i++ )
  687. {
  688. accum[i] /= ( nSampleWidth * nSampleHeight );
  689. pDst[(x+y*info.m_nDestWidth)*3+i] = accum[i];
  690. }
  691. }
  692. }
  693. return true;
  694. }
  695. bool ResampleRGBA32323232F( const ResampleInfo_t& info )
  696. {
  697. // HDRFIXME: This is some lame shit right here. (We need to get NICE working, etc, etc.)
  698. // Make sure everything is power of two.
  699. Assert( ( info.m_nSrcWidth & ( info.m_nSrcWidth - 1 ) ) == 0 );
  700. Assert( ( info.m_nSrcHeight & ( info.m_nSrcHeight - 1 ) ) == 0 );
  701. Assert( ( info.m_nDestWidth & ( info.m_nDestWidth - 1 ) ) == 0 );
  702. Assert( ( info.m_nDestHeight & ( info.m_nDestHeight - 1 ) ) == 0 );
  703. // Make sure that we aren't upscaling the image. . .we don't support that very well.
  704. Assert( info.m_nSrcWidth >= info.m_nDestWidth );
  705. Assert( info.m_nSrcHeight >= info.m_nDestHeight );
  706. int nSampleWidth = info.m_nSrcWidth / info.m_nDestWidth;
  707. int nSampleHeight = info.m_nSrcHeight / info.m_nDestHeight;
  708. float *pSrc = ( float * )info.m_pSrc;
  709. float *pDst = ( float * )info.m_pDest;
  710. for( int y = 0; y < info.m_nDestHeight; y++ )
  711. {
  712. for( int x = 0; x < info.m_nDestWidth; x++ )
  713. {
  714. float accum[4] = { 0.0f, 0.0f, 0.0f, 0.0f };
  715. for( int nSampleY = 0; nSampleY < nSampleHeight; nSampleY++ )
  716. {
  717. for( int nSampleX = 0; nSampleX < nSampleWidth; nSampleX++ )
  718. {
  719. accum[0] += pSrc[((x*nSampleWidth+nSampleX)+(y*nSampleHeight+nSampleY)*info.m_nSrcWidth)*4+0];
  720. accum[1] += pSrc[((x*nSampleWidth+nSampleX)+(y*nSampleHeight+nSampleY)*info.m_nSrcWidth)*4+1];
  721. accum[2] += pSrc[((x*nSampleWidth+nSampleX)+(y*nSampleHeight+nSampleY)*info.m_nSrcWidth)*4+2];
  722. accum[3] += pSrc[((x*nSampleWidth+nSampleX)+(y*nSampleHeight+nSampleY)*info.m_nSrcWidth)*4+3];
  723. }
  724. }
  725. for( int i = 0; i < 4; i++ )
  726. {
  727. accum[i] /= ( nSampleWidth * nSampleHeight );
  728. pDst[(x+y*info.m_nDestWidth)*4+i] = accum[i];
  729. }
  730. }
  731. }
  732. return true;
  733. }
  734. //-----------------------------------------------------------------------------
  735. // Generates mipmap levels
  736. //-----------------------------------------------------------------------------
  737. void GenerateMipmapLevels( unsigned char* pSrc, unsigned char* pDst, int width,
  738. int height, int depth, ImageFormat imageFormat, float srcGamma, float dstGamma, int numLevels )
  739. {
  740. int dstWidth = width;
  741. int dstHeight = height;
  742. int dstDepth = depth;
  743. // temporary storage for the mipmaps
  744. int tempMem = GetMemRequired( dstWidth, dstHeight, dstDepth, IMAGE_FORMAT_RGBA8888, false );
  745. CUtlMemory<unsigned char> tmpImage;
  746. tmpImage.EnsureCapacity( tempMem );
  747. while( true )
  748. {
  749. // This generates a mipmap in RGBA8888, linear space
  750. ResampleInfo_t info;
  751. info.m_pSrc = pSrc;
  752. info.m_pDest = tmpImage.Base();
  753. info.m_nSrcWidth = width;
  754. info.m_nSrcHeight = height;
  755. info.m_nSrcDepth = depth;
  756. info.m_nDestWidth = dstWidth;
  757. info.m_nDestHeight = dstHeight;
  758. info.m_nDestDepth = dstDepth;
  759. info.m_flSrcGamma = srcGamma;
  760. info.m_flDestGamma = dstGamma;
  761. ResampleRGBA8888( info );
  762. // each mipmap level needs to be color converted separately
  763. ConvertImageFormat( tmpImage.Base(), IMAGE_FORMAT_RGBA8888,
  764. pDst, imageFormat, dstWidth, dstHeight, 0, 0 );
  765. if (numLevels == 0)
  766. {
  767. // We're done after we've made the 1x1 mip level
  768. if (dstWidth == 1 && dstHeight == 1 && dstDepth == 1)
  769. return;
  770. }
  771. else
  772. {
  773. if (--numLevels <= 0)
  774. return;
  775. }
  776. // Figure out where the next level goes
  777. int memRequired = ImageLoader::GetMemRequired( dstWidth, dstHeight, dstDepth, imageFormat, false);
  778. pDst += memRequired;
  779. // shrink by a factor of 2, but clamp at 1 pixel (non-square textures)
  780. dstWidth = dstWidth > 1 ? dstWidth >> 1 : 1;
  781. dstHeight = dstHeight > 1 ? dstHeight >> 1 : 1;
  782. dstDepth = dstDepth > 1 ? dstDepth >> 1 : 1;
  783. }
  784. }
  785. } // ImageLoader namespace ends