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.

241 lines
6.3 KiB

  1. //========= Copyright Valve Corporation, All rights reserved. ============//
  2. //
  3. // Purpose: Font effects that operate on linear rgba data
  4. //
  5. //=====================================================================================//
  6. #include "tier0/platform.h"
  7. #include <tier0/dbg.h>
  8. #include <math.h>
  9. #include "FontEffects.h"
  10. // NOTE: This has to be the last file included!
  11. #include "tier0/memdbgon.h"
  12. //-----------------------------------------------------------------------------
  13. // Purpose: Adds center line to font
  14. //-----------------------------------------------------------------------------
  15. void ApplyRotaryEffectToTexture( int rgbaWide, int rgbaTall, unsigned char *rgba, bool bRotary )
  16. {
  17. if ( !bRotary )
  18. return;
  19. int y = rgbaTall * 0.5;
  20. unsigned char *line = &rgba[(y * rgbaWide) * 4];
  21. // Draw a line down middle
  22. for (int x = 0; x < rgbaWide; x++, line+=4)
  23. {
  24. line[0] = 127;
  25. line[1] = 127;
  26. line[2] = 127;
  27. line[3] = 255;
  28. }
  29. }
  30. //-----------------------------------------------------------------------------
  31. // Purpose: adds scanlines to the texture
  32. //-----------------------------------------------------------------------------
  33. void ApplyScanlineEffectToTexture( int rgbaWide, int rgbaTall, unsigned char *rgba, int iScanLines )
  34. {
  35. if ( iScanLines < 2 )
  36. return;
  37. float scale;
  38. scale = 0.7f;
  39. // darken all the areas except the scanlines
  40. for (int y = 0; y < rgbaTall; y++)
  41. {
  42. // skip the scan lines
  43. if (y % iScanLines == 0)
  44. continue;
  45. unsigned char *pBits = &rgba[(y * rgbaWide) * 4];
  46. // darken the other lines
  47. for (int x = 0; x < rgbaWide; x++, pBits += 4)
  48. {
  49. pBits[0] *= scale;
  50. pBits[1] *= scale;
  51. pBits[2] *= scale;
  52. }
  53. }
  54. }
  55. //-----------------------------------------------------------------------------
  56. // Purpose: adds a dropshadow the the font texture
  57. //-----------------------------------------------------------------------------
  58. void ApplyDropShadowToTexture( int rgbaWide, int rgbaTall, unsigned char *rgba, int iDropShadowOffset )
  59. {
  60. if ( !iDropShadowOffset )
  61. return;
  62. // walk the original image from the bottom up
  63. // shifting it down and right, and turning it black (the dropshadow)
  64. for (int y = rgbaTall - 1; y >= iDropShadowOffset; y--)
  65. {
  66. for (int x = rgbaWide - 1; x >= iDropShadowOffset; x--)
  67. {
  68. unsigned char *dest = &rgba[(x + (y * rgbaWide)) * 4];
  69. if (dest[3] == 0)
  70. {
  71. // there is nothing in this spot, copy in the dropshadow
  72. unsigned char *src = &rgba[(x - iDropShadowOffset + ((y - iDropShadowOffset) * rgbaWide)) * 4];
  73. dest[0] = 0;
  74. dest[1] = 0;
  75. dest[2] = 0;
  76. dest[3] = src[3];
  77. }
  78. }
  79. }
  80. }
  81. //-----------------------------------------------------------------------------
  82. // Purpose: adds an outline to the font texture
  83. //-----------------------------------------------------------------------------
  84. void ApplyOutlineToTexture( int rgbaWide, int rgbaTall, unsigned char *rgba, int iOutlineSize )
  85. {
  86. if ( !iOutlineSize )
  87. return;
  88. int x, y;
  89. for( y = 0; y < rgbaTall; y++ )
  90. {
  91. for( x = 0; x < rgbaWide; x++ )
  92. {
  93. unsigned char *src = &rgba[(x + (y * rgbaWide)) * 4];
  94. if( src[3] == 0 )
  95. {
  96. // We have a valid font texel. Make all the alpha == 0 neighbors black.
  97. int shadowX, shadowY;
  98. for( shadowX = -(int)iOutlineSize; shadowX <= (int)iOutlineSize; shadowX++ )
  99. {
  100. for( shadowY = -(int)iOutlineSize; shadowY <= (int)iOutlineSize; shadowY++ )
  101. {
  102. if( shadowX == 0 && shadowY == 0 )
  103. {
  104. continue;
  105. }
  106. int testX, testY;
  107. testX = shadowX + x;
  108. testY = shadowY + y;
  109. if( testX < 0 || testX >= rgbaWide ||
  110. testY < 0 || testY >= rgbaTall )
  111. {
  112. continue;
  113. }
  114. unsigned char *test = &rgba[(testX + (testY * rgbaWide)) * 4];
  115. if( test[0] != 0 && test[1] != 0 && test[2] != 0 && test[3] != 0 )
  116. {
  117. src[0] = 0;
  118. src[1] = 0;
  119. src[2] = 0;
  120. src[3] = 255;
  121. }
  122. }
  123. }
  124. }
  125. }
  126. }
  127. }
  128. namespace
  129. {
  130. unsigned char CalculatePixelBlur(const unsigned char* src, int nStride, const float* distribution, int nValues)
  131. {
  132. float accum = 0.0;
  133. for ( int n = 0; n != nValues; ++n )
  134. {
  135. accum += distribution[n]*static_cast<float>(src[n*nStride]);
  136. }
  137. return static_cast<unsigned char>(accum);
  138. }
  139. }
  140. //-----------------------------------------------------------------------------
  141. // Purpose: blurs the texture
  142. //-----------------------------------------------------------------------------
  143. void ApplyGaussianBlurToTexture( int rgbaWide, int rgbaTall, unsigned char *rgba, int nBlur )
  144. {
  145. if ( !nBlur )
  146. return;
  147. // generate the gaussian field
  148. float *pGaussianDistribution = (float*) stackalloc( (nBlur*2+1) * sizeof(float) );
  149. double sigma = 0.683 * nBlur;
  150. for (int x = 0; x <= (nBlur * 2); x++)
  151. {
  152. int val = x - nBlur;
  153. pGaussianDistribution[x] = (float)( 1.0f / sqrt(2 * 3.14 * sigma * sigma)) * pow(2.7, -1 * (val * val) / (2 * sigma * sigma));
  154. }
  155. // alloc a new buffer
  156. unsigned char *src = (unsigned char *) stackalloc( rgbaWide * rgbaTall * 4);
  157. // copy in
  158. memcpy(src, rgba, rgbaWide * rgbaTall * 4);
  159. //make an initial horizontal pass
  160. for ( int x = 0; x < rgbaWide; x++ )
  161. {
  162. const float* dist = pGaussianDistribution;
  163. int nValues = nBlur*2 + 1;
  164. int nOffset = 0;
  165. if ( x < nBlur )
  166. {
  167. nOffset += nBlur - x;
  168. dist += nOffset;
  169. nValues -= nOffset;
  170. }
  171. if ( x >= rgbaWide - nBlur )
  172. {
  173. nValues = rgbaWide - (x - nOffset);
  174. }
  175. for ( int y = 0; y < rgbaTall; y++ )
  176. {
  177. const unsigned char* read_from = src + (y*rgbaWide + x + nOffset - nBlur)*4 + 3;
  178. unsigned char* dst = rgba + (y*rgbaWide + x)*4;
  179. unsigned char alpha = CalculatePixelBlur(read_from, 4, dist, nValues);
  180. dst[0] = dst[1] = dst[2] = alpha > 0 ? 255 : 0;
  181. dst[3] = alpha;
  182. }
  183. }
  184. // refresh the source buffer for a second vertical pass
  185. memcpy(src, rgba, rgbaWide * rgbaTall * 4);
  186. for ( int y = 0; y < rgbaTall; y++ )
  187. {
  188. const float* dist = pGaussianDistribution;
  189. int nValues = nBlur*2 + 1;
  190. int nOffset = 0;
  191. if ( y < nBlur )
  192. {
  193. nOffset += nBlur - y;
  194. dist += nOffset;
  195. nValues -= nOffset;
  196. }
  197. if ( y >= rgbaTall - nBlur )
  198. {
  199. nValues = rgbaTall - (y - nOffset);
  200. }
  201. for ( int x = 0; x < rgbaWide; x++ )
  202. {
  203. const unsigned char* read_from = src + ((y + nOffset - nBlur)*rgbaWide + x)*4 + 3;
  204. unsigned char* dst = rgba + (y*rgbaWide + x)*4;
  205. unsigned char alpha = CalculatePixelBlur(read_from, 4*rgbaWide, dist, nValues);
  206. dst[0] = dst[1] = dst[2] = alpha > 0 ? 255 : 0;
  207. dst[3] = alpha;
  208. }
  209. }
  210. }