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