Team Fortress 2 Source Code as on 22/4/2020
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.

919 lines
27 KiB

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