bitmaphandler.cpp 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383
  1. /*
  2. ** Command & Conquer Renegade(tm)
  3. ** Copyright 2025 Electronic Arts Inc.
  4. **
  5. ** This program is free software: you can redistribute it and/or modify
  6. ** it under the terms of the GNU General Public License as published by
  7. ** the Free Software Foundation, either version 3 of the License, or
  8. ** (at your option) any later version.
  9. **
  10. ** This program 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. **
  15. ** You should have received a copy of the GNU General Public License
  16. ** along with this program. If not, see <http://www.gnu.org/licenses/>.
  17. */
  18. #include "bitmaphandler.h"
  19. #include "wwdebug.h"
  20. void Bitmap_Assert(bool condition)
  21. {
  22. WWASSERT(condition);
  23. }
  24. void BitmapHandlerClass::Create_Mipmap_B8G8R8A8(
  25. unsigned char* dest_surface,
  26. unsigned dest_surface_pitch,
  27. unsigned char* src_surface,
  28. unsigned src_surface_pitch,
  29. unsigned width,
  30. unsigned height)
  31. {
  32. unsigned src_pitch=src_surface_pitch/4;
  33. for (unsigned y=0;y<height;y+=2) {
  34. unsigned* dest=(unsigned*)dest_surface;
  35. dest_surface+=dest_surface_pitch;
  36. unsigned* src=(unsigned*)src_surface;
  37. src_surface+=src_surface_pitch;
  38. for (unsigned x=0;x<width;x+=2) {
  39. unsigned bgra3=src[src_pitch];
  40. unsigned bgra1=*src++;
  41. unsigned bgra4=src[src_pitch];
  42. unsigned bgra2=*src++;
  43. *dest++=Combine_A8R8G8B8(bgra1,bgra2,bgra3,bgra4);
  44. }
  45. }
  46. }
  47. void BitmapHandlerClass::Copy_Image_Generate_Mipmap(
  48. unsigned width,
  49. unsigned height,
  50. unsigned char* dest_surface,
  51. unsigned dest_pitch,
  52. WW3DFormat dest_format,
  53. unsigned char* src_surface,
  54. unsigned src_pitch,
  55. WW3DFormat src_format,
  56. unsigned char* mip_surface,
  57. unsigned mip_pitch)
  58. {
  59. // Optimized loop if source and destination are 32 bit
  60. if (src_format==dest_format && src_format==WW3D_FORMAT_A8R8G8B8) {
  61. dest_pitch/=4;
  62. src_pitch/=4;
  63. mip_pitch/=4;
  64. for (unsigned y=0;y<height/2;++y) {
  65. unsigned* dest_ptr=(unsigned*)dest_surface;
  66. dest_ptr+=2*y*dest_pitch;
  67. unsigned* src_ptr=(unsigned*)src_surface;
  68. src_ptr+=y*2*src_pitch;
  69. unsigned* mip_ptr=(unsigned*)mip_surface;
  70. mip_ptr+=y*mip_pitch;
  71. unsigned b8g8r8a8_00;
  72. unsigned b8g8r8a8_01;
  73. unsigned b8g8r8a8_10;
  74. unsigned b8g8r8a8_11;
  75. for (unsigned x=0;x<width/2;x++) {
  76. b8g8r8a8_10=src_ptr[src_pitch];
  77. dest_ptr[dest_pitch]=b8g8r8a8_10;
  78. b8g8r8a8_00=*src_ptr++;
  79. *dest_ptr++=b8g8r8a8_00;
  80. b8g8r8a8_11=src_ptr[src_pitch];
  81. dest_ptr[dest_pitch]=b8g8r8a8_11;
  82. b8g8r8a8_01=*src_ptr++;
  83. *dest_ptr++=b8g8r8a8_01;
  84. *mip_ptr++=Combine_A8R8G8B8(b8g8r8a8_00,b8g8r8a8_01,b8g8r8a8_10,b8g8r8a8_11);
  85. }
  86. }
  87. return;
  88. }
  89. WWASSERT(src_format!=WW3D_FORMAT_P8); // This function doesn't support paletted formats
  90. unsigned src_bpp=Get_Bytes_Per_Pixel(src_format);
  91. unsigned dest_bpp=Get_Bytes_Per_Pixel(dest_format);
  92. for (unsigned y=0;y<height/2;++y) {
  93. unsigned char* dest_ptr=dest_surface+2*y*dest_pitch;
  94. unsigned char* src_ptr=src_surface+y*2*src_pitch;
  95. unsigned char* mip_ptr=mip_surface+y*mip_pitch;
  96. unsigned b8g8r8a8_00;
  97. unsigned b8g8r8a8_01;
  98. unsigned b8g8r8a8_10;
  99. unsigned b8g8r8a8_11;
  100. for (unsigned x=0;x<width/2;x++,dest_ptr+=dest_bpp*2,src_ptr+=src_bpp*2,mip_ptr+=dest_bpp) {
  101. Read_B8G8R8A8(b8g8r8a8_00,src_ptr,src_format,NULL,0);
  102. Write_B8G8R8A8(dest_ptr,dest_format,b8g8r8a8_00);
  103. Read_B8G8R8A8(b8g8r8a8_01,src_ptr+src_bpp,src_format,NULL,0);
  104. Write_B8G8R8A8(dest_ptr+dest_bpp,dest_format,b8g8r8a8_01);
  105. Read_B8G8R8A8(b8g8r8a8_10,src_ptr+src_pitch,src_format,NULL,0);
  106. Write_B8G8R8A8(dest_ptr+dest_pitch,dest_format,b8g8r8a8_10);
  107. Read_B8G8R8A8(b8g8r8a8_11,src_ptr+src_bpp+src_pitch,src_format,NULL,0);
  108. Write_B8G8R8A8(dest_ptr+dest_bpp+dest_pitch,dest_format,b8g8r8a8_11);
  109. unsigned b8g8r8a8=Combine_A8R8G8B8(b8g8r8a8_00,b8g8r8a8_01,b8g8r8a8_10,b8g8r8a8_11);
  110. Write_B8G8R8A8(mip_ptr,dest_format,b8g8r8a8);
  111. }
  112. }
  113. }
  114. // ----------------------------------------------------------------------------
  115. //
  116. // Copy image from source surface to destination surface with stretch and color
  117. // space conversion if needed. If 'generate_mip_level' is set, process image
  118. // in 2x2 blocks and generate mipmap on top of the original source image while
  119. // copying.
  120. //
  121. // ----------------------------------------------------------------------------
  122. void BitmapHandlerClass::Copy_Image(
  123. unsigned char* dest_surface,
  124. unsigned dest_surface_width,
  125. unsigned dest_surface_height,
  126. unsigned dest_surface_pitch,
  127. WW3DFormat dest_surface_format,
  128. unsigned char* src_surface,
  129. unsigned src_surface_width,
  130. unsigned src_surface_height,
  131. unsigned src_surface_pitch,
  132. WW3DFormat src_surface_format,
  133. const unsigned char* src_palette,
  134. unsigned src_palette_bpp,
  135. bool generate_mip_level)
  136. {
  137. WWASSERT(dest_surface_width);
  138. WWASSERT(dest_surface_height);
  139. // Bumpmap?
  140. if (dest_surface_format==WW3D_FORMAT_U8V8 ||
  141. dest_surface_format==WW3D_FORMAT_L6V5U5 ||
  142. dest_surface_format==WW3D_FORMAT_X8L8V8U8) {
  143. unsigned src_bpp=Get_Bytes_Per_Pixel(src_surface_format);
  144. for( unsigned y=0; y<dest_surface_height; y++ ) {
  145. unsigned char* dest_ptr=dest_surface;
  146. dest_ptr+=y*dest_surface_pitch;
  147. unsigned char* src_ptr_mid=src_surface;
  148. src_ptr_mid+=y*src_surface_pitch;
  149. unsigned char* src_ptr_next_line = ( src_ptr_mid + src_surface_pitch );
  150. unsigned char* src_ptr_prev_line = ( src_ptr_mid - src_surface_pitch );
  151. if( y == src_surface_height-1 ) // Don't go past the last line
  152. src_ptr_next_line = src_ptr_mid;
  153. if( y == 0 ) // Don't go before first line
  154. src_ptr_prev_line = src_ptr_mid;
  155. for( unsigned x=0; x<dest_surface_width; x++ ) {
  156. unsigned pixel00;
  157. unsigned pixel01;
  158. unsigned pixelM1;
  159. unsigned pixel10;
  160. unsigned pixel1M;
  161. Read_B8G8R8A8(pixel00,src_ptr_mid,src_surface_format,NULL,0);
  162. Read_B8G8R8A8(pixel01,src_ptr_mid+src_bpp,src_surface_format,NULL,0);
  163. Read_B8G8R8A8(pixelM1,src_ptr_mid-src_bpp,src_surface_format,NULL,0);
  164. Read_B8G8R8A8(pixel10,src_ptr_prev_line,src_surface_format,NULL,0);
  165. Read_B8G8R8A8(pixel1M,src_ptr_next_line,src_surface_format,NULL,0);
  166. // Convert to luminance
  167. unsigned char bv00;
  168. unsigned char bv01;
  169. unsigned char bvM1;
  170. unsigned char bv10;
  171. unsigned char bv1M;
  172. Write_B8G8R8A8(&bv00,WW3D_FORMAT_L8,pixel00);
  173. Write_B8G8R8A8(&bv01,WW3D_FORMAT_L8,pixel01);
  174. Write_B8G8R8A8(&bvM1,WW3D_FORMAT_L8,pixelM1);
  175. Write_B8G8R8A8(&bv10,WW3D_FORMAT_L8,pixel10);
  176. Write_B8G8R8A8(&bv1M,WW3D_FORMAT_L8,pixel1M);
  177. int v00=bv00,v01=bv01,vM1=bvM1,v10=bv10,v1M=bv1M;
  178. int iDu = (vM1-v01); // The delta-u bump value
  179. int iDv = (v1M-v10); // The delta-v bump value
  180. if( (v00 < vM1) && (v00 < v01) ) { // If we are at valley
  181. iDu = vM1-v00; // Choose greater of 1st order diffs
  182. if( iDu < v00-v01 )
  183. iDu = v00-v01;
  184. }
  185. // The luminance bump value (land masses are less shiny)
  186. unsigned short uL = ( v00>1 ) ? 63 : 127;
  187. switch(dest_surface_format) {
  188. case WW3D_FORMAT_U8V8:
  189. *dest_ptr++ = (unsigned char)iDu;
  190. *dest_ptr++ = (unsigned char)iDv;
  191. break;
  192. case WW3D_FORMAT_L6V5U5:
  193. *(unsigned short*)dest_ptr = (unsigned short)( ( (iDu>>3) & 0x1f ) << 0 );
  194. *(unsigned short*)dest_ptr |= (unsigned short)( ( (iDv>>3) & 0x1f ) << 5 );
  195. *(unsigned short*)dest_ptr |= (unsigned short)( ( ( uL>>2) & 0x3f ) << 10 );
  196. dest_ptr += 2;
  197. break;
  198. case WW3D_FORMAT_X8L8V8U8:
  199. *dest_ptr++ = (unsigned char)iDu;
  200. *dest_ptr++ = (unsigned char)iDv;
  201. *dest_ptr++ = (unsigned char)uL;
  202. *dest_ptr++ = (unsigned char)0L;
  203. break;
  204. default:
  205. WWASSERT(0); // Unknown bumpmap format
  206. break;
  207. }
  208. // Move one pixel to the left (src is 32-bpp)
  209. src_ptr_mid+=src_bpp;
  210. src_ptr_prev_line+=src_bpp;
  211. src_ptr_next_line+=src_bpp;
  212. }
  213. }
  214. return;
  215. }
  216. if (src_surface_format==dest_surface_format && (src_surface_format==WW3D_FORMAT_A8R8G8B8 || src_surface_format==WW3D_FORMAT_X8R8G8B8)) {
  217. // One-to-one copy or scaling?
  218. dest_surface_pitch/=4;
  219. src_surface_pitch/=4;
  220. if (dest_surface_width==src_surface_width && dest_surface_height==src_surface_height) {
  221. // Generate the next mip level while copying the current surface?
  222. if (generate_mip_level) {
  223. if (dest_surface_width==1) {
  224. *(unsigned*)dest_surface=*(unsigned*)src_surface;
  225. }
  226. else {
  227. for (unsigned y=0;y<dest_surface_height/2;++y) {
  228. unsigned* dest_ptr=(unsigned*)dest_surface;
  229. dest_ptr+=2*y*dest_surface_pitch;
  230. unsigned* src_ptr=(unsigned*)src_surface;
  231. unsigned* mip_ptr=src_ptr;
  232. src_ptr+=y*2*src_surface_pitch;
  233. mip_ptr+=y*src_surface_pitch;
  234. unsigned b8g8r8a8_00;
  235. unsigned b8g8r8a8_01;
  236. unsigned b8g8r8a8_10;
  237. unsigned b8g8r8a8_11;
  238. for (unsigned x=0;x<dest_surface_width/2;x++) {
  239. b8g8r8a8_10=src_ptr[src_surface_pitch];
  240. dest_ptr[dest_surface_pitch]=b8g8r8a8_10;
  241. b8g8r8a8_00=*src_ptr++;
  242. *dest_ptr++=b8g8r8a8_00;
  243. b8g8r8a8_11=src_ptr[src_surface_pitch];
  244. dest_ptr[dest_surface_pitch]=b8g8r8a8_11;
  245. b8g8r8a8_01=*src_ptr++;
  246. *dest_ptr++=b8g8r8a8_01;
  247. *mip_ptr++=Combine_A8R8G8B8(b8g8r8a8_00,b8g8r8a8_01,b8g8r8a8_10,b8g8r8a8_11);
  248. }
  249. }
  250. }
  251. }
  252. else {
  253. for (unsigned y=0;y<dest_surface_height;++y) {
  254. unsigned* dest_ptr=(unsigned*)dest_surface;
  255. dest_ptr+=y*dest_surface_pitch;
  256. const unsigned* src_ptr=(unsigned*)src_surface;
  257. src_ptr+=y*src_surface_pitch;
  258. for (unsigned x=0;x<dest_surface_width;++x) {
  259. *dest_ptr++=*src_ptr++;
  260. }
  261. }
  262. }
  263. }
  264. else {
  265. // For now do only point-sampling
  266. for (unsigned y=0;y<dest_surface_height;++y) {
  267. unsigned* dest_ptr=(unsigned*)dest_surface;
  268. dest_ptr+=y*dest_surface_pitch;
  269. unsigned src_y=y*src_surface_height/dest_surface_height;
  270. const unsigned* src_ptr=(unsigned*)src_surface;
  271. src_ptr+=src_y*src_surface_pitch;
  272. for (unsigned x=0;x<dest_surface_width;++x) {
  273. unsigned src_x=x*src_surface_width/dest_surface_width;
  274. *dest_ptr++=src_ptr[src_x];
  275. }
  276. }
  277. }
  278. return;
  279. }
  280. unsigned dest_bpp=Get_Bytes_Per_Pixel(dest_surface_format);
  281. unsigned src_bpp=Get_Bytes_Per_Pixel(src_surface_format);
  282. // One-to-one copy or scaling?
  283. if (dest_surface_width==src_surface_width && dest_surface_height==src_surface_height) {
  284. // Generate the next mip level while copying the current surface?
  285. if (generate_mip_level) {
  286. WWASSERT(src_surface_format!=WW3D_FORMAT_P8); // Paletted textures can't be mipmapped
  287. if (dest_surface_width==1) {
  288. unsigned char* dest_ptr=dest_surface;
  289. unsigned char* src_ptr=src_surface;
  290. unsigned b8g8r8a8;
  291. Read_B8G8R8A8(b8g8r8a8,src_ptr,src_surface_format,src_palette,src_palette_bpp);
  292. Write_B8G8R8A8(dest_ptr,dest_surface_format,b8g8r8a8);
  293. }
  294. else {
  295. for (unsigned y=0;y<dest_surface_height/2;++y) {
  296. unsigned char* dest_ptr=dest_surface+2*y*dest_surface_pitch;
  297. unsigned char* src_ptr=src_surface+y*2*src_surface_pitch;
  298. unsigned char* mip_ptr=src_surface+y*src_surface_pitch;
  299. unsigned b8g8r8a8_00;
  300. unsigned b8g8r8a8_01;
  301. unsigned b8g8r8a8_10;
  302. unsigned b8g8r8a8_11;
  303. for (unsigned x=0;x<dest_surface_width/2;x++,dest_ptr+=dest_bpp*2,src_ptr+=src_bpp*2,mip_ptr+=src_bpp) {
  304. Read_B8G8R8A8(b8g8r8a8_00,src_ptr,src_surface_format,src_palette,src_palette_bpp);
  305. Write_B8G8R8A8(dest_ptr,dest_surface_format,b8g8r8a8_00);
  306. Read_B8G8R8A8(b8g8r8a8_01,src_ptr+src_bpp,src_surface_format,src_palette,src_palette_bpp);
  307. Write_B8G8R8A8(dest_ptr+dest_bpp,dest_surface_format,b8g8r8a8_01);
  308. Read_B8G8R8A8(b8g8r8a8_10,src_ptr+src_surface_pitch,src_surface_format,src_palette,src_palette_bpp);
  309. Write_B8G8R8A8(dest_ptr+dest_surface_pitch,dest_surface_format,b8g8r8a8_10);
  310. Read_B8G8R8A8(b8g8r8a8_11,src_ptr+src_bpp+src_surface_pitch,src_surface_format,src_palette,src_palette_bpp);
  311. Write_B8G8R8A8(dest_ptr+dest_bpp+dest_surface_pitch,dest_surface_format,b8g8r8a8_11);
  312. unsigned b8g8r8a8=Combine_A8R8G8B8(b8g8r8a8_00,b8g8r8a8_01,b8g8r8a8_10,b8g8r8a8_11);
  313. Write_B8G8R8A8(mip_ptr,src_surface_format,b8g8r8a8);
  314. }
  315. }
  316. }
  317. }
  318. else {
  319. for (unsigned y=0;y<dest_surface_height;++y) {
  320. unsigned char* dest_ptr=dest_surface+y*dest_surface_pitch;
  321. const unsigned char* src_ptr=src_surface+y*src_surface_pitch;
  322. for (unsigned x=0;x<dest_surface_width;++x,dest_ptr+=dest_bpp,src_ptr+=src_bpp) {
  323. Copy_Pixel(dest_ptr,dest_surface_format,src_ptr,src_surface_format,src_palette,src_palette_bpp);
  324. }
  325. }
  326. }
  327. }
  328. else {
  329. // For now do only point-sampling
  330. for (unsigned y=0;y<dest_surface_height;++y) {
  331. unsigned char* dest_ptr=dest_surface+y*dest_surface_pitch;
  332. unsigned src_y=y*src_surface_height/dest_surface_height;
  333. const unsigned char* src_ptr=src_surface+src_y*src_surface_pitch;
  334. for (unsigned x=0;x<dest_surface_width;++x,dest_ptr+=dest_bpp) {
  335. unsigned src_x=x*src_surface_width/dest_surface_width;
  336. src_x*=src_bpp;
  337. Copy_Pixel(dest_ptr,dest_surface_format,src_ptr+src_x,src_surface_format,src_palette,src_palette_bpp);
  338. }
  339. }
  340. }
  341. }