bitmaphandler.cpp 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448
  1. /*
  2. ** Command & Conquer Generals Zero Hour(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. #include "colorspace.h"
  21. void Bitmap_Assert(bool condition)
  22. {
  23. WWASSERT(condition);
  24. }
  25. void BitmapHandlerClass::Create_Mipmap_B8G8R8A8(
  26. unsigned char* dest_surface,
  27. unsigned dest_surface_pitch,
  28. unsigned char* src_surface,
  29. unsigned src_surface_pitch,
  30. unsigned width,
  31. unsigned height)
  32. {
  33. unsigned src_pitch=src_surface_pitch/4;
  34. for (unsigned y=0;y<height;y+=2) {
  35. unsigned* dest=(unsigned*)dest_surface;
  36. dest_surface+=dest_surface_pitch;
  37. unsigned* src=(unsigned*)src_surface;
  38. src_surface+=src_surface_pitch;
  39. for (unsigned x=0;x<width;x+=2) {
  40. unsigned bgra3=src[src_pitch];
  41. unsigned bgra1=*src++;
  42. unsigned bgra4=src[src_pitch];
  43. unsigned bgra2=*src++;
  44. *dest++=Combine_A8R8G8B8(bgra1,bgra2,bgra3,bgra4);
  45. }
  46. }
  47. }
  48. void BitmapHandlerClass::Copy_Image_Generate_Mipmap(
  49. unsigned width,
  50. unsigned height,
  51. unsigned char* dest_surface,
  52. unsigned dest_pitch,
  53. WW3DFormat dest_format,
  54. unsigned char* src_surface,
  55. unsigned src_pitch,
  56. WW3DFormat src_format,
  57. unsigned char* mip_surface,
  58. unsigned mip_pitch,
  59. const Vector3& hsv_shift)
  60. {
  61. // Optimized loop if source and destination are 32 bit
  62. bool has_hsv_shift = hsv_shift[0]!=0.0f || hsv_shift[1]!=0.0f || hsv_shift[2]!=0.0f;
  63. if (src_format==dest_format && src_format==WW3D_FORMAT_A8R8G8B8) {
  64. dest_pitch/=4;
  65. src_pitch/=4;
  66. mip_pitch/=4;
  67. for (unsigned y=0;y<height/2;++y) {
  68. unsigned* dest_ptr=(unsigned*)dest_surface;
  69. dest_ptr+=2*y*dest_pitch;
  70. unsigned* src_ptr=(unsigned*)src_surface;
  71. src_ptr+=y*2*src_pitch;
  72. unsigned* mip_ptr=(unsigned*)mip_surface;
  73. mip_ptr+=y*mip_pitch;
  74. unsigned b8g8r8a8_00;
  75. unsigned b8g8r8a8_01;
  76. unsigned b8g8r8a8_10;
  77. unsigned b8g8r8a8_11;
  78. for (unsigned x=0;x<width/2;x++) {
  79. b8g8r8a8_10=src_ptr[src_pitch];
  80. dest_ptr[dest_pitch]=b8g8r8a8_10;
  81. b8g8r8a8_00=*src_ptr++;
  82. *dest_ptr++=b8g8r8a8_00;
  83. b8g8r8a8_11=src_ptr[src_pitch];
  84. dest_ptr[dest_pitch]=b8g8r8a8_11;
  85. b8g8r8a8_01=*src_ptr++;
  86. *dest_ptr++=b8g8r8a8_01;
  87. unsigned b8g8r8a8=Combine_A8R8G8B8(b8g8r8a8_00,b8g8r8a8_01,b8g8r8a8_10,b8g8r8a8_11);
  88. if (has_hsv_shift) {
  89. Recolor(b8g8r8a8,hsv_shift);
  90. }
  91. *mip_ptr++=b8g8r8a8;
  92. }
  93. }
  94. return;
  95. }
  96. WWASSERT(src_format!=WW3D_FORMAT_P8); // This function doesn't support paletted formats
  97. unsigned src_bpp=Get_Bytes_Per_Pixel(src_format);
  98. unsigned dest_bpp=Get_Bytes_Per_Pixel(dest_format);
  99. for (unsigned y=0;y<height/2;++y) {
  100. unsigned char* dest_ptr=dest_surface+2*y*dest_pitch;
  101. unsigned char* src_ptr=src_surface+y*2*src_pitch;
  102. unsigned char* mip_ptr=mip_surface+y*mip_pitch;
  103. unsigned b8g8r8a8_00;
  104. unsigned b8g8r8a8_01;
  105. unsigned b8g8r8a8_10;
  106. unsigned b8g8r8a8_11;
  107. for (unsigned x=0;x<width/2;x++,dest_ptr+=dest_bpp*2,src_ptr+=src_bpp*2,mip_ptr+=dest_bpp) {
  108. Read_B8G8R8A8(b8g8r8a8_00,src_ptr,src_format,NULL,0);
  109. Write_B8G8R8A8(dest_ptr,dest_format,b8g8r8a8_00);
  110. Read_B8G8R8A8(b8g8r8a8_01,src_ptr+src_bpp,src_format,NULL,0);
  111. Write_B8G8R8A8(dest_ptr+dest_bpp,dest_format,b8g8r8a8_01);
  112. Read_B8G8R8A8(b8g8r8a8_10,src_ptr+src_pitch,src_format,NULL,0);
  113. Write_B8G8R8A8(dest_ptr+dest_pitch,dest_format,b8g8r8a8_10);
  114. Read_B8G8R8A8(b8g8r8a8_11,src_ptr+src_bpp+src_pitch,src_format,NULL,0);
  115. Write_B8G8R8A8(dest_ptr+dest_bpp+dest_pitch,dest_format,b8g8r8a8_11);
  116. unsigned b8g8r8a8=Combine_A8R8G8B8(b8g8r8a8_00,b8g8r8a8_01,b8g8r8a8_10,b8g8r8a8_11);
  117. if (has_hsv_shift) {
  118. Recolor(b8g8r8a8,hsv_shift);
  119. }
  120. Write_B8G8R8A8(mip_ptr,dest_format,b8g8r8a8);
  121. }
  122. }
  123. }
  124. // ----------------------------------------------------------------------------
  125. //
  126. // Copy image from source surface to destination surface with stretch and color
  127. // space conversion if needed. If 'generate_mip_level' is set, process image
  128. // in 2x2 blocks and generate mipmap on top of the original source image while
  129. // copying.
  130. //
  131. // ----------------------------------------------------------------------------
  132. void BitmapHandlerClass::Copy_Image(
  133. unsigned char* dest_surface,
  134. unsigned dest_surface_width,
  135. unsigned dest_surface_height,
  136. unsigned dest_surface_pitch,
  137. WW3DFormat dest_surface_format,
  138. unsigned char* src_surface,
  139. unsigned src_surface_width,
  140. unsigned src_surface_height,
  141. unsigned src_surface_pitch,
  142. WW3DFormat src_surface_format,
  143. const unsigned char* src_palette,
  144. unsigned src_palette_bpp,
  145. bool generate_mip_level,
  146. const Vector3& hsv_shift)
  147. {
  148. WWASSERT(dest_surface_width);
  149. WWASSERT(dest_surface_height);
  150. // Bumpmap?
  151. if (dest_surface_format==WW3D_FORMAT_U8V8 ||
  152. dest_surface_format==WW3D_FORMAT_L6V5U5 ||
  153. dest_surface_format==WW3D_FORMAT_X8L8V8U8) {
  154. unsigned src_bpp=Get_Bytes_Per_Pixel(src_surface_format);
  155. for( unsigned y=0; y<dest_surface_height; y++ ) {
  156. unsigned char* dest_ptr=dest_surface;
  157. dest_ptr+=y*dest_surface_pitch;
  158. unsigned char* src_ptr_mid=src_surface;
  159. src_ptr_mid+=y*src_surface_pitch;
  160. unsigned char* src_ptr_next_line = ( src_ptr_mid + src_surface_pitch );
  161. unsigned char* src_ptr_prev_line = ( src_ptr_mid - src_surface_pitch );
  162. if( y == src_surface_height-1 ) // Don't go past the last line
  163. src_ptr_next_line = src_ptr_mid;
  164. if( y == 0 ) // Don't go before first line
  165. src_ptr_prev_line = src_ptr_mid;
  166. for( unsigned x=0; x<dest_surface_width; x++ ) {
  167. unsigned pixel00;
  168. unsigned pixel01;
  169. unsigned pixelM1;
  170. unsigned pixel10;
  171. unsigned pixel1M;
  172. Read_B8G8R8A8(pixel00,src_ptr_mid,src_surface_format,NULL,0);
  173. Read_B8G8R8A8(pixel01,src_ptr_mid+src_bpp,src_surface_format,NULL,0);
  174. Read_B8G8R8A8(pixelM1,src_ptr_mid-src_bpp,src_surface_format,NULL,0);
  175. Read_B8G8R8A8(pixel10,src_ptr_prev_line,src_surface_format,NULL,0);
  176. Read_B8G8R8A8(pixel1M,src_ptr_next_line,src_surface_format,NULL,0);
  177. // Convert to luminance
  178. unsigned char bv00;
  179. unsigned char bv01;
  180. unsigned char bvM1;
  181. unsigned char bv10;
  182. unsigned char bv1M;
  183. Write_B8G8R8A8(&bv00,WW3D_FORMAT_L8,pixel00);
  184. Write_B8G8R8A8(&bv01,WW3D_FORMAT_L8,pixel01);
  185. Write_B8G8R8A8(&bvM1,WW3D_FORMAT_L8,pixelM1);
  186. Write_B8G8R8A8(&bv10,WW3D_FORMAT_L8,pixel10);
  187. Write_B8G8R8A8(&bv1M,WW3D_FORMAT_L8,pixel1M);
  188. int v00=bv00,v01=bv01,vM1=bvM1,v10=bv10,v1M=bv1M;
  189. int iDu = (vM1-v01); // The delta-u bump value
  190. int iDv = (v1M-v10); // The delta-v bump value
  191. if( (v00 < vM1) && (v00 < v01) ) { // If we are at valley
  192. iDu = vM1-v00; // Choose greater of 1st order diffs
  193. if( iDu < v00-v01 )
  194. iDu = v00-v01;
  195. }
  196. // The luminance bump value (land masses are less shiny)
  197. unsigned short uL = ( v00>1 ) ? 63 : 127;
  198. switch(dest_surface_format) {
  199. case WW3D_FORMAT_U8V8:
  200. *dest_ptr++ = (unsigned char)iDu;
  201. *dest_ptr++ = (unsigned char)iDv;
  202. break;
  203. case WW3D_FORMAT_L6V5U5:
  204. *(unsigned short*)dest_ptr = (unsigned short)( ( (iDu>>3) & 0x1f ) << 0 );
  205. *(unsigned short*)dest_ptr |= (unsigned short)( ( (iDv>>3) & 0x1f ) << 5 );
  206. *(unsigned short*)dest_ptr |= (unsigned short)( ( ( uL>>2) & 0x3f ) << 10 );
  207. dest_ptr += 2;
  208. break;
  209. case WW3D_FORMAT_X8L8V8U8:
  210. *dest_ptr++ = (unsigned char)iDu;
  211. *dest_ptr++ = (unsigned char)iDv;
  212. *dest_ptr++ = (unsigned char)uL;
  213. *dest_ptr++ = (unsigned char)0L;
  214. break;
  215. default:
  216. WWASSERT(0); // Unknown bumpmap format
  217. break;
  218. }
  219. // Move one pixel to the left (src is 32-bpp)
  220. src_ptr_mid+=src_bpp;
  221. src_ptr_prev_line+=src_bpp;
  222. src_ptr_next_line+=src_bpp;
  223. }
  224. }
  225. return;
  226. }
  227. bool has_hsv_shift = hsv_shift[0]!=0.0f || hsv_shift[1]!=0.0f || hsv_shift[2]!=0.0f;
  228. if (src_surface_format==dest_surface_format && (src_surface_format==WW3D_FORMAT_A8R8G8B8 || src_surface_format==WW3D_FORMAT_X8R8G8B8)) {
  229. // One-to-one copy or scaling?
  230. dest_surface_pitch/=4;
  231. src_surface_pitch/=4;
  232. if (dest_surface_width==src_surface_width && dest_surface_height==src_surface_height) {
  233. // Generate the next mip level while copying the current surface?
  234. if (generate_mip_level) {
  235. if (dest_surface_width==1) {
  236. unsigned b8g8r8a8=*(unsigned*)src_surface;
  237. if (has_hsv_shift) Recolor(b8g8r8a8,hsv_shift);
  238. *(unsigned*)dest_surface=b8g8r8a8;
  239. }
  240. else {
  241. for (unsigned y=0;y<dest_surface_height/2;++y) {
  242. unsigned* dest_ptr=(unsigned*)dest_surface;
  243. dest_ptr+=2*y*dest_surface_pitch;
  244. unsigned* src_ptr=(unsigned*)src_surface;
  245. unsigned* mip_ptr=src_ptr;
  246. src_ptr+=y*2*src_surface_pitch;
  247. mip_ptr+=y*src_surface_pitch;
  248. unsigned b8g8r8a8_00;
  249. unsigned b8g8r8a8_01;
  250. unsigned b8g8r8a8_10;
  251. unsigned b8g8r8a8_11;
  252. for (unsigned x=0;x<dest_surface_width/2;x++) {
  253. // Read four pixels from the source
  254. b8g8r8a8_10=src_ptr[src_surface_pitch];
  255. b8g8r8a8_00=*src_ptr++;
  256. b8g8r8a8_11=src_ptr[src_surface_pitch];
  257. b8g8r8a8_01=*src_ptr++;
  258. // Recolor if necessary
  259. if (has_hsv_shift) {
  260. Recolor(b8g8r8a8_00,hsv_shift);
  261. Recolor(b8g8r8a8_01,hsv_shift);
  262. Recolor(b8g8r8a8_10,hsv_shift);
  263. Recolor(b8g8r8a8_11,hsv_shift);
  264. }
  265. // Write the four pixels to the destination
  266. dest_ptr[dest_surface_pitch]=b8g8r8a8_10;
  267. *dest_ptr++=b8g8r8a8_00;
  268. dest_ptr[dest_surface_pitch]=b8g8r8a8_11;
  269. *dest_ptr++=b8g8r8a8_01;
  270. // Write combined four pixels to the destination mip map level
  271. *mip_ptr++=Combine_A8R8G8B8(b8g8r8a8_00,b8g8r8a8_01,b8g8r8a8_10,b8g8r8a8_11);
  272. }
  273. }
  274. }
  275. }
  276. else {
  277. for (unsigned y=0;y<dest_surface_height;++y) {
  278. unsigned* dest_ptr=(unsigned*)dest_surface;
  279. dest_ptr+=y*dest_surface_pitch;
  280. const unsigned* src_ptr=(unsigned*)src_surface;
  281. src_ptr+=y*src_surface_pitch;
  282. if (has_hsv_shift) {
  283. for (unsigned x=0;x<dest_surface_width;++x) {
  284. unsigned b8g8r8a8=*src_ptr++;
  285. Recolor(b8g8r8a8,hsv_shift);
  286. *dest_ptr++=b8g8r8a8;
  287. }
  288. }
  289. else {
  290. for (unsigned x=0;x<dest_surface_width;++x) {
  291. *dest_ptr++=*src_ptr++;
  292. }
  293. }
  294. }
  295. }
  296. }
  297. else {
  298. // For now do only point-sampling
  299. for (unsigned y=0;y<dest_surface_height;++y) {
  300. unsigned* dest_ptr=(unsigned*)dest_surface;
  301. dest_ptr+=y*dest_surface_pitch;
  302. unsigned src_y=y*src_surface_height/dest_surface_height;
  303. const unsigned* src_ptr=(unsigned*)src_surface;
  304. src_ptr+=src_y*src_surface_pitch;
  305. for (unsigned x=0;x<dest_surface_width;++x) {
  306. unsigned src_x=x*src_surface_width/dest_surface_width;
  307. unsigned b8g8r8a8=src_ptr[src_x];
  308. if (has_hsv_shift) {
  309. Recolor(b8g8r8a8,hsv_shift);
  310. }
  311. *dest_ptr++=b8g8r8a8;
  312. }
  313. }
  314. }
  315. return;
  316. }
  317. unsigned dest_bpp=Get_Bytes_Per_Pixel(dest_surface_format);
  318. unsigned src_bpp=Get_Bytes_Per_Pixel(src_surface_format);
  319. // One-to-one copy or scaling?
  320. if (dest_surface_width==src_surface_width && dest_surface_height==src_surface_height) {
  321. // Generate the next mip level while copying the current surface?
  322. if (generate_mip_level) {
  323. WWASSERT(src_surface_format!=WW3D_FORMAT_P8); // Paletted textures can't be mipmapped
  324. if (dest_surface_width==1) {
  325. unsigned char* dest_ptr=dest_surface;
  326. unsigned char* src_ptr=src_surface;
  327. unsigned b8g8r8a8;
  328. Read_B8G8R8A8(b8g8r8a8,src_ptr,src_surface_format,src_palette,src_palette_bpp);
  329. if (has_hsv_shift) {
  330. Recolor(b8g8r8a8,hsv_shift);
  331. }
  332. Write_B8G8R8A8(dest_ptr,dest_surface_format,b8g8r8a8);
  333. }
  334. else {
  335. for (unsigned y=0;y<dest_surface_height/2;++y) {
  336. unsigned char* dest_ptr=dest_surface+2*y*dest_surface_pitch;
  337. unsigned char* src_ptr=src_surface+y*2*src_surface_pitch;
  338. unsigned char* mip_ptr=src_surface+y*src_surface_pitch;
  339. unsigned b8g8r8a8_00;
  340. unsigned b8g8r8a8_01;
  341. unsigned b8g8r8a8_10;
  342. unsigned b8g8r8a8_11;
  343. for (unsigned x=0;x<dest_surface_width/2;x++,dest_ptr+=dest_bpp*2,src_ptr+=src_bpp*2,mip_ptr+=src_bpp) {
  344. // Read four pixels from the source
  345. Read_B8G8R8A8(b8g8r8a8_00,src_ptr,src_surface_format,src_palette,src_palette_bpp);
  346. Read_B8G8R8A8(b8g8r8a8_01,src_ptr+src_bpp,src_surface_format,src_palette,src_palette_bpp);
  347. Read_B8G8R8A8(b8g8r8a8_10,src_ptr+src_surface_pitch,src_surface_format,src_palette,src_palette_bpp);
  348. Read_B8G8R8A8(b8g8r8a8_11,src_ptr+src_bpp+src_surface_pitch,src_surface_format,src_palette,src_palette_bpp);
  349. // Recolor if necessary
  350. if (has_hsv_shift) {
  351. Recolor(b8g8r8a8_00,hsv_shift);
  352. Recolor(b8g8r8a8_01,hsv_shift);
  353. Recolor(b8g8r8a8_10,hsv_shift);
  354. Recolor(b8g8r8a8_11,hsv_shift);
  355. }
  356. // Write the four pixels to the destination
  357. Write_B8G8R8A8(dest_ptr,dest_surface_format,b8g8r8a8_00);
  358. Write_B8G8R8A8(dest_ptr+dest_bpp,dest_surface_format,b8g8r8a8_01);
  359. Write_B8G8R8A8(dest_ptr+dest_surface_pitch,dest_surface_format,b8g8r8a8_10);
  360. Write_B8G8R8A8(dest_ptr+dest_bpp+dest_surface_pitch,dest_surface_format,b8g8r8a8_11);
  361. // Write combined four pixels to the destination mip map level
  362. unsigned b8g8r8a8=Combine_A8R8G8B8(b8g8r8a8_00,b8g8r8a8_01,b8g8r8a8_10,b8g8r8a8_11);
  363. Write_B8G8R8A8(mip_ptr,src_surface_format,b8g8r8a8);
  364. }
  365. }
  366. }
  367. }
  368. else {
  369. for (unsigned y=0;y<dest_surface_height;++y) {
  370. unsigned char* dest_ptr=dest_surface+y*dest_surface_pitch;
  371. const unsigned char* src_ptr=src_surface+y*src_surface_pitch;
  372. if (has_hsv_shift) {
  373. for (unsigned x=0;x<dest_surface_width;++x,dest_ptr+=dest_bpp,src_ptr+=src_bpp) {
  374. Copy_Pixel(dest_ptr,dest_surface_format,src_ptr,src_surface_format,src_palette,src_palette_bpp,hsv_shift);
  375. }
  376. }
  377. else {
  378. for (unsigned x=0;x<dest_surface_width;++x,dest_ptr+=dest_bpp,src_ptr+=src_bpp) {
  379. Copy_Pixel(dest_ptr,dest_surface_format,src_ptr,src_surface_format,src_palette,src_palette_bpp);
  380. }
  381. }
  382. }
  383. }
  384. }
  385. else {
  386. // For now do only point-sampling
  387. for (unsigned y=0;y<dest_surface_height;++y) {
  388. unsigned char* dest_ptr=dest_surface+y*dest_surface_pitch;
  389. unsigned src_y=y*src_surface_height/dest_surface_height;
  390. const unsigned char* src_ptr=src_surface+src_y*src_surface_pitch;
  391. if (has_hsv_shift) {
  392. for (unsigned x=0;x<dest_surface_width;++x,dest_ptr+=dest_bpp) {
  393. unsigned src_x=x*src_surface_width/dest_surface_width;
  394. src_x*=src_bpp;
  395. Copy_Pixel(dest_ptr,dest_surface_format,src_ptr+src_x,src_surface_format,src_palette,src_palette_bpp,hsv_shift);
  396. }
  397. }
  398. else {
  399. for (unsigned x=0;x<dest_surface_width;++x,dest_ptr+=dest_bpp) {
  400. unsigned src_x=x*src_surface_width/dest_surface_width;
  401. src_x*=src_bpp;
  402. Copy_Pixel(dest_ptr,dest_surface_format,src_ptr+src_x,src_surface_format,src_palette,src_palette_bpp);
  403. }
  404. }
  405. }
  406. }
  407. }