Image_process.cpp 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493
  1. /*
  2. ===========================================================================
  3. Doom 3 BFG Edition GPL Source Code
  4. Copyright (C) 1993-2012 id Software LLC, a ZeniMax Media company.
  5. This file is part of the Doom 3 BFG Edition GPL Source Code ("Doom 3 BFG Edition Source Code").
  6. Doom 3 BFG Edition Source Code is free software: you can redistribute it and/or modify
  7. it under the terms of the GNU General Public License as published by
  8. the Free Software Foundation, either version 3 of the License, or
  9. (at your option) any later version.
  10. Doom 3 BFG Edition Source Code is distributed in the hope that it will be useful,
  11. but WITHOUT ANY WARRANTY; without even the implied warranty of
  12. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  13. GNU General Public License for more details.
  14. You should have received a copy of the GNU General Public License
  15. along with Doom 3 BFG Edition Source Code. If not, see <http://www.gnu.org/licenses/>.
  16. In addition, the Doom 3 BFG Edition Source Code is also subject to certain additional terms. You should have received a copy of these additional terms immediately following the terms and conditions of the GNU General Public License which accompanied the Doom 3 BFG Edition Source Code. If not, please request a copy in writing from id Software at the address below.
  17. If you have questions concerning this license or the applicable additional terms, you may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA.
  18. ===========================================================================
  19. */
  20. #pragma hdrstop
  21. #include "../idlib/precompiled.h"
  22. #include "tr_local.h"
  23. /*
  24. ================
  25. R_ResampleTexture
  26. Used to resample images in a more general than quartering fashion.
  27. This will only have filter coverage if the resampled size
  28. is greater than half the original size.
  29. If a larger shrinking is needed, use the mipmap function
  30. after resampling to the next lower power of two.
  31. ================
  32. */
  33. #define MAX_DIMENSION 4096
  34. byte *R_ResampleTexture( const byte *in, int inwidth, int inheight,
  35. int outwidth, int outheight ) {
  36. int i, j;
  37. const byte *inrow, *inrow2;
  38. unsigned int frac, fracstep;
  39. unsigned int p1[MAX_DIMENSION], p2[MAX_DIMENSION];
  40. const byte *pix1, *pix2, *pix3, *pix4;
  41. byte *out, *out_p;
  42. if ( outwidth > MAX_DIMENSION ) {
  43. outwidth = MAX_DIMENSION;
  44. }
  45. if ( outheight > MAX_DIMENSION ) {
  46. outheight = MAX_DIMENSION;
  47. }
  48. out = (byte *)R_StaticAlloc( outwidth * outheight * 4, TAG_IMAGE );
  49. out_p = out;
  50. fracstep = inwidth*0x10000/outwidth;
  51. frac = fracstep>>2;
  52. for ( i=0 ; i<outwidth ; i++ ) {
  53. p1[i] = 4*(frac>>16);
  54. frac += fracstep;
  55. }
  56. frac = 3*(fracstep>>2);
  57. for ( i=0 ; i<outwidth ; i++ ) {
  58. p2[i] = 4*(frac>>16);
  59. frac += fracstep;
  60. }
  61. for (i=0 ; i<outheight ; i++, out_p += outwidth*4 ) {
  62. inrow = in + 4 * inwidth * (int)( ( i + 0.25f ) * inheight / outheight );
  63. inrow2 = in + 4 * inwidth * (int)( ( i + 0.75f ) * inheight / outheight );
  64. frac = fracstep >> 1;
  65. for (j=0 ; j<outwidth ; j++) {
  66. pix1 = inrow + p1[j];
  67. pix2 = inrow + p2[j];
  68. pix3 = inrow2 + p1[j];
  69. pix4 = inrow2 + p2[j];
  70. out_p[j*4+0] = (pix1[0] + pix2[0] + pix3[0] + pix4[0])>>2;
  71. out_p[j*4+1] = (pix1[1] + pix2[1] + pix3[1] + pix4[1])>>2;
  72. out_p[j*4+2] = (pix1[2] + pix2[2] + pix3[2] + pix4[2])>>2;
  73. out_p[j*4+3] = (pix1[3] + pix2[3] + pix3[3] + pix4[3])>>2;
  74. }
  75. }
  76. return out;
  77. }
  78. /*
  79. ================
  80. R_Dropsample
  81. Used to resample images in a more general than quartering fashion.
  82. Normal maps and such should not be bilerped.
  83. ================
  84. */
  85. byte *R_Dropsample( const byte *in, int inwidth, int inheight,
  86. int outwidth, int outheight ) {
  87. int i, j, k;
  88. const byte *inrow;
  89. const byte *pix1;
  90. byte *out, *out_p;
  91. out = (byte *)R_StaticAlloc( outwidth * outheight * 4, TAG_IMAGE );
  92. out_p = out;
  93. for (i=0 ; i<outheight ; i++, out_p += outwidth*4 ) {
  94. inrow = in + 4*inwidth*(int)((i+0.25)*inheight/outheight);
  95. for (j=0 ; j<outwidth ; j++) {
  96. k = j * inwidth / outwidth;
  97. pix1 = inrow + k * 4;
  98. out_p[j*4+0] = pix1[0];
  99. out_p[j*4+1] = pix1[1];
  100. out_p[j*4+2] = pix1[2];
  101. out_p[j*4+3] = pix1[3];
  102. }
  103. }
  104. return out;
  105. }
  106. /*
  107. ================
  108. R_SetAlphaNormalDivergence
  109. If any of the angles inside the cone would directly reflect to the light, there will be
  110. a specular highlight. The intensity of the highlight is inversely proportional to the
  111. area of the spread.
  112. Light source area is important for the base size.
  113. area subtended in light is the divergence times the distance
  114. Shininess value is subtracted from the divergence
  115. Sets the alpha channel to the greatest divergence dot product of the surrounding texels.
  116. 1.0 = flat, 0.0 = turns a 90 degree angle
  117. Lower values give less shiny specular
  118. With mip maps, the lowest samnpled value will be retained
  119. Should we rewrite the normal as the centered average?
  120. ================
  121. */
  122. void R_SetAlphaNormalDivergence( byte *in, int width, int height ) {
  123. for ( int y = 0 ; y < height ; y++ ) {
  124. for ( int x = 0 ; x < width ; x++ ) {
  125. // the divergence is the smallest dot product of any of the eight surrounding texels
  126. byte *pic_p = in + ( y * width + x ) * 4;
  127. idVec3 center;
  128. center[0] = ( pic_p[0] - 128 ) / 127;
  129. center[1] = ( pic_p[1] - 128 ) / 127;
  130. center[2] = ( pic_p[2] - 128 ) / 127;
  131. center.Normalize();
  132. float maxDiverge = 1.0;
  133. // FIXME: this assumes wrap mode, but should handle clamp modes and border colors
  134. for ( int yy = -1 ; yy <= 1 ; yy++ ) {
  135. for ( int xx = -1 ; xx <= 1 ; xx++ ) {
  136. if ( yy == 0 && xx == 0 ) {
  137. continue;
  138. }
  139. byte *corner_p = in + ( ((y+yy)&(height-1)) * width + ((x+xx)&width-1) ) * 4;
  140. idVec3 corner;
  141. corner[0] = ( corner_p[0] - 128 ) / 127;
  142. corner[1] = ( corner_p[1] - 128 ) / 127;
  143. corner[2] = ( corner_p[2] - 128 ) / 127;
  144. corner.Normalize();
  145. float diverge = corner * center;
  146. if ( diverge < maxDiverge ) {
  147. maxDiverge = diverge;
  148. }
  149. }
  150. }
  151. // we can get a diverge < 0 in some extreme cases
  152. if ( maxDiverge < 0 ) {
  153. maxDiverge = 0;
  154. }
  155. pic_p[3] = maxDiverge * 255;
  156. }
  157. }
  158. }
  159. /*
  160. ================
  161. R_MipMapWithAlphaSpecularity
  162. Returns a new copy of the texture, quartered in size and filtered.
  163. The alpha channel is taken to be the minimum of the dots of all surrounding normals.
  164. ================
  165. */
  166. #define MIP_MIN(a,b) (a<b?a:b)
  167. byte *R_MipMapWithAlphaSpecularity( const byte *in, int width, int height ) {
  168. int i, j, c, x, y, sx, sy;
  169. const byte *in_p;
  170. byte *out, *out_p;
  171. int row;
  172. int newWidth, newHeight;
  173. float *fbuf, *fbuf_p;
  174. if ( width < 1 || height < 1 || ( width + height == 2 ) ) {
  175. common->FatalError( "R_MipMapWithAlphaMin called with size %i,%i", width, height );
  176. }
  177. // convert the incoming texture to centered floating point
  178. c = width * height;
  179. fbuf = (float *)_alloca( c * 4 * sizeof( *fbuf ) );
  180. in_p = in;
  181. fbuf_p = fbuf;
  182. for ( i = 0 ; i < c ; i++, in_p+=4, fbuf_p += 4 ) {
  183. fbuf_p[0] = ( in_p[0] / 255.0 ) * 2.0 - 1.0; // convert to a normal
  184. fbuf_p[1] = ( in_p[1] / 255.0 ) * 2.0 - 1.0;
  185. fbuf_p[2] = ( in_p[2] / 255.0 ) * 2.0 - 1.0;
  186. fbuf_p[3] = ( in_p[3] / 255.0 ); // filtered divegence / specularity
  187. }
  188. row = width * 4;
  189. newWidth = width >> 1;
  190. newHeight = height >> 1;
  191. if ( !newWidth ) {
  192. newWidth = 1;
  193. }
  194. if ( !newHeight ) {
  195. newHeight = 1;
  196. }
  197. out = (byte *)R_StaticAlloc( newWidth * newHeight * 4, TAG_IMAGE );
  198. out_p = out;
  199. in_p = in;
  200. for ( i=0 ; i<newHeight ; i++ ) {
  201. for ( j=0 ; j<newWidth ; j++, out_p+=4 ) {
  202. idVec3 total;
  203. float totalSpec;
  204. total.Zero();
  205. totalSpec = 0;
  206. // find the average normal
  207. for ( x = -1 ; x <= 1 ; x++ ) {
  208. sx = ( j * 2 + x ) & (width-1);
  209. for ( y = -1 ; y <= 1 ; y++ ) {
  210. sy = ( i * 2 + y ) & (height-1);
  211. fbuf_p = fbuf + ( sy * width + sx ) * 4;
  212. total[0] += fbuf_p[0];
  213. total[1] += fbuf_p[1];
  214. total[2] += fbuf_p[2];
  215. totalSpec += fbuf_p[3];
  216. }
  217. }
  218. total.Normalize();
  219. totalSpec /= 9.0;
  220. // find the maximum divergence
  221. for ( x = -1 ; x <= 1 ; x++ ) {
  222. for ( y = -1 ; y <= 1 ; y++ ) {
  223. }
  224. }
  225. // store the average normal and divergence
  226. }
  227. }
  228. return out;
  229. }
  230. float mip_gammaTable[256] = {
  231. 0.000000f, 0.000005f, 0.000023f, 0.000057f, 0.000107f, 0.000175f, 0.000262f, 0.000367f, 0.000493f, 0.000638f, 0.000805f, 0.000992f, 0.001202f, 0.001433f, 0.001687f, 0.001963f,
  232. 0.002263f, 0.002586f, 0.002932f, 0.003303f, 0.003697f, 0.004116f, 0.004560f, 0.005028f, 0.005522f, 0.006041f, 0.006585f, 0.007155f, 0.007751f, 0.008373f, 0.009021f, 0.009696f,
  233. 0.010398f, 0.011126f, 0.011881f, 0.012664f, 0.013473f, 0.014311f, 0.015175f, 0.016068f, 0.016988f, 0.017936f, 0.018913f, 0.019918f, 0.020951f, 0.022013f, 0.023104f, 0.024223f,
  234. 0.025371f, 0.026549f, 0.027755f, 0.028991f, 0.030257f, 0.031551f, 0.032876f, 0.034230f, 0.035614f, 0.037029f, 0.038473f, 0.039947f, 0.041452f, 0.042987f, 0.044553f, 0.046149f,
  235. 0.047776f, 0.049433f, 0.051122f, 0.052842f, 0.054592f, 0.056374f, 0.058187f, 0.060032f, 0.061907f, 0.063815f, 0.065754f, 0.067725f, 0.069727f, 0.071761f, 0.073828f, 0.075926f,
  236. 0.078057f, 0.080219f, 0.082414f, 0.084642f, 0.086901f, 0.089194f, 0.091518f, 0.093876f, 0.096266f, 0.098689f, 0.101145f, 0.103634f, 0.106156f, 0.108711f, 0.111299f, 0.113921f,
  237. 0.116576f, 0.119264f, 0.121986f, 0.124741f, 0.127530f, 0.130352f, 0.133209f, 0.136099f, 0.139022f, 0.141980f, 0.144972f, 0.147998f, 0.151058f, 0.154152f, 0.157281f, 0.160444f,
  238. 0.163641f, 0.166872f, 0.170138f, 0.173439f, 0.176774f, 0.180144f, 0.183549f, 0.186989f, 0.190463f, 0.193972f, 0.197516f, 0.201096f, 0.204710f, 0.208360f, 0.212044f, 0.215764f,
  239. 0.219520f, 0.223310f, 0.227137f, 0.230998f, 0.234895f, 0.238828f, 0.242796f, 0.246800f, 0.250840f, 0.254916f, 0.259027f, 0.263175f, 0.267358f, 0.271577f, 0.275833f, 0.280124f,
  240. 0.284452f, 0.288816f, 0.293216f, 0.297653f, 0.302126f, 0.306635f, 0.311181f, 0.315763f, 0.320382f, 0.325037f, 0.329729f, 0.334458f, 0.339223f, 0.344026f, 0.348865f, 0.353741f,
  241. 0.358654f, 0.363604f, 0.368591f, 0.373615f, 0.378676f, 0.383775f, 0.388910f, 0.394083f, 0.399293f, 0.404541f, 0.409826f, 0.415148f, 0.420508f, 0.425905f, 0.431340f, 0.436813f,
  242. 0.442323f, 0.447871f, 0.453456f, 0.459080f, 0.464741f, 0.470440f, 0.476177f, 0.481952f, 0.487765f, 0.493616f, 0.499505f, 0.505432f, 0.511398f, 0.517401f, 0.523443f, 0.529523f,
  243. 0.535642f, 0.541798f, 0.547994f, 0.554227f, 0.560499f, 0.566810f, 0.573159f, 0.579547f, 0.585973f, 0.592438f, 0.598942f, 0.605484f, 0.612066f, 0.618686f, 0.625345f, 0.632043f,
  244. 0.638779f, 0.645555f, 0.652370f, 0.659224f, 0.666117f, 0.673049f, 0.680020f, 0.687031f, 0.694081f, 0.701169f, 0.708298f, 0.715465f, 0.722672f, 0.729919f, 0.737205f, 0.744530f,
  245. 0.751895f, 0.759300f, 0.766744f, 0.774227f, 0.781751f, 0.789314f, 0.796917f, 0.804559f, 0.812241f, 0.819964f, 0.827726f, 0.835528f, 0.843370f, 0.851252f, 0.859174f, 0.867136f,
  246. 0.875138f, 0.883180f, 0.891262f, 0.899384f, 0.907547f, 0.915750f, 0.923993f, 0.932277f, 0.940601f, 0.948965f, 0.957370f, 0.965815f, 0.974300f, 0.982826f, 0.991393f, 1.000000f
  247. };
  248. /*
  249. ================
  250. R_MipMapGamma
  251. Returns a new copy of the texture, quartered in size with gamma correction.
  252. ================
  253. */
  254. byte * R_MipMapWithGamma( const byte *in, int width, int height ) {
  255. int i, j;
  256. const byte *in_p;
  257. byte *out, *out_p;
  258. int row;
  259. int newWidth, newHeight;
  260. if ( width < 1 || height < 1 || ( width + height == 2 ) ) {
  261. return NULL;
  262. }
  263. row = width * 4;
  264. newWidth = width >> 1;
  265. newHeight = height >> 1;
  266. if ( !newWidth ) {
  267. newWidth = 1;
  268. }
  269. if ( !newHeight ) {
  270. newHeight = 1;
  271. }
  272. out = (byte *)R_StaticAlloc( newWidth * newHeight * 4, TAG_IMAGE );
  273. out_p = out;
  274. in_p = in;
  275. width >>= 1;
  276. height >>= 1;
  277. if ( width == 0 || height == 0 ) {
  278. width += height; // get largest
  279. for (i=0 ; i<width ; i++, out_p+=4, in_p+=8 ) {
  280. out_p[0] = idMath::Ftob( 255.0f * idMath::Pow( 0.5f * ( mip_gammaTable[in_p[0]] + mip_gammaTable[in_p[4]] ), 1.0f / 2.2f ) );
  281. out_p[1] = idMath::Ftob( 255.0f * idMath::Pow( 0.5f * ( mip_gammaTable[in_p[1]] + mip_gammaTable[in_p[5]] ), 1.0f / 2.2f ) );
  282. out_p[2] = idMath::Ftob( 255.0f * idMath::Pow( 0.5f * ( mip_gammaTable[in_p[2]] + mip_gammaTable[in_p[6]] ), 1.0f / 2.2f ) );
  283. out_p[3] = idMath::Ftob( 255.0f * idMath::Pow( 0.5f * ( mip_gammaTable[in_p[3]] + mip_gammaTable[in_p[7]] ), 1.0f / 2.2f ) );
  284. }
  285. return out;
  286. }
  287. for (i=0 ; i<height ; i++, in_p+=row) {
  288. for (j=0 ; j<width ; j++, out_p+=4, in_p+=8) {
  289. out_p[0] = idMath::Ftob( 255.0f * idMath::Pow( 0.25f * ( mip_gammaTable[in_p[0]] + mip_gammaTable[in_p[4]] + mip_gammaTable[in_p[row+0]] + mip_gammaTable[in_p[row+4]] ), 1.0f / 2.2f ) );
  290. out_p[1] = idMath::Ftob( 255.0f * idMath::Pow( 0.25f * ( mip_gammaTable[in_p[1]] + mip_gammaTable[in_p[5]] + mip_gammaTable[in_p[row+1]] + mip_gammaTable[in_p[row+5]] ), 1.0f / 2.2f ) );
  291. out_p[2] = idMath::Ftob( 255.0f * idMath::Pow( 0.25f * ( mip_gammaTable[in_p[2]] + mip_gammaTable[in_p[6]] + mip_gammaTable[in_p[row+2]] + mip_gammaTable[in_p[row+6]] ), 1.0f / 2.2f ) );
  292. out_p[3] = idMath::Ftob( 255.0f * idMath::Pow( 0.25f * ( mip_gammaTable[in_p[3]] + mip_gammaTable[in_p[7]] + mip_gammaTable[in_p[row+3]] + mip_gammaTable[in_p[row+7]] ), 1.0f / 2.2f ) );
  293. }
  294. }
  295. return out;
  296. }
  297. /*
  298. ================
  299. R_MipMap
  300. Returns a new copy of the texture, quartered in size and filtered.
  301. ================
  302. */
  303. byte * R_MipMap( const byte *in, int width, int height ) {
  304. int i, j;
  305. const byte *in_p;
  306. byte *out, *out_p;
  307. int row;
  308. int newWidth, newHeight;
  309. if ( width < 1 || height < 1 || ( width + height == 2 ) ) {
  310. return NULL;
  311. }
  312. row = width * 4;
  313. newWidth = width >> 1;
  314. newHeight = height >> 1;
  315. if ( !newWidth ) {
  316. newWidth = 1;
  317. }
  318. if ( !newHeight ) {
  319. newHeight = 1;
  320. }
  321. out = (byte *)R_StaticAlloc( newWidth * newHeight * 4, TAG_IMAGE );
  322. out_p = out;
  323. in_p = in;
  324. width >>= 1;
  325. height >>= 1;
  326. if ( width == 0 || height == 0 ) {
  327. width += height; // get largest
  328. for (i=0 ; i<width ; i++, out_p+=4, in_p+=8 ) {
  329. out_p[0] = ( in_p[0] + in_p[4] )>>1;
  330. out_p[1] = ( in_p[1] + in_p[5] )>>1;
  331. out_p[2] = ( in_p[2] + in_p[6] )>>1;
  332. out_p[3] = ( in_p[3] + in_p[7] )>>1;
  333. }
  334. return out;
  335. }
  336. for (i=0 ; i<height ; i++, in_p+=row) {
  337. for (j=0 ; j<width ; j++, out_p+=4, in_p+=8) {
  338. out_p[0] = (in_p[0] + in_p[4] + in_p[row+0] + in_p[row+4])>>2;
  339. out_p[1] = (in_p[1] + in_p[5] + in_p[row+1] + in_p[row+5])>>2;
  340. out_p[2] = (in_p[2] + in_p[6] + in_p[row+2] + in_p[row+6])>>2;
  341. out_p[3] = (in_p[3] + in_p[7] + in_p[row+3] + in_p[row+7])>>2;
  342. }
  343. }
  344. return out;
  345. }
  346. /*
  347. ==================
  348. R_BlendOverTexture
  349. Apply a color blend over a set of pixels
  350. ==================
  351. */
  352. void R_BlendOverTexture( byte *data, int pixelCount, const byte blend[4] ) {
  353. int i;
  354. int inverseAlpha;
  355. int premult[3];
  356. inverseAlpha = 255 - blend[3];
  357. premult[0] = blend[0] * blend[3];
  358. premult[1] = blend[1] * blend[3];
  359. premult[2] = blend[2] * blend[3];
  360. for ( i = 0 ; i < pixelCount ; i++, data+=4 ) {
  361. data[0] = ( data[0] * inverseAlpha + premult[0] ) >> 9;
  362. data[1] = ( data[1] * inverseAlpha + premult[1] ) >> 9;
  363. data[2] = ( data[2] * inverseAlpha + premult[2] ) >> 9;
  364. }
  365. }
  366. /*
  367. ==================
  368. R_HorizontalFlip
  369. Flip the image in place
  370. ==================
  371. */
  372. void R_HorizontalFlip( byte *data, int width, int height ) {
  373. int i, j;
  374. int temp;
  375. for ( i = 0 ; i < height ; i++ ) {
  376. for ( j = 0 ; j < width / 2 ; j++ ) {
  377. temp = *( (int *)data + i * width + j );
  378. *( (int *)data + i * width + j ) = *( (int *)data + i * width + width - 1 - j );
  379. *( (int *)data + i * width + width - 1 - j ) = temp;
  380. }
  381. }
  382. }
  383. void R_VerticalFlip( byte *data, int width, int height ) {
  384. int i, j;
  385. int temp;
  386. for ( i = 0 ; i < width ; i++ ) {
  387. for ( j = 0 ; j < height / 2 ; j++ ) {
  388. temp = *( (int *)data + j * width + i );
  389. *( (int *)data + j * width + i ) = *( (int *)data + ( height - 1 - j ) * width + i );
  390. *( (int *)data + ( height - 1 - j ) * width + i ) = temp;
  391. }
  392. }
  393. }
  394. void R_RotatePic( byte *data, int width ) {
  395. int i, j;
  396. int *temp;
  397. temp = (int *)R_StaticAlloc( width * width * 4, TAG_IMAGE );
  398. for ( i = 0 ; i < width ; i++ ) {
  399. for ( j = 0 ; j < width ; j++ ) {
  400. *( temp + i * width + j ) = *( (int *)data + j * width + i );
  401. }
  402. }
  403. memcpy( data, temp, width * width * 4 );
  404. R_StaticFree( temp );
  405. }