| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448 |
- /*
- ** Command & Conquer Generals Zero Hour(tm)
- ** Copyright 2025 Electronic Arts Inc.
- **
- ** This program is free software: you can redistribute it and/or modify
- ** it under the terms of the GNU General Public License as published by
- ** the Free Software Foundation, either version 3 of the License, or
- ** (at your option) any later version.
- **
- ** This program is distributed in the hope that it will be useful,
- ** but WITHOUT ANY WARRANTY; without even the implied warranty of
- ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- ** GNU General Public License for more details.
- **
- ** You should have received a copy of the GNU General Public License
- ** along with this program. If not, see <http://www.gnu.org/licenses/>.
- */
- #include "bitmaphandler.h"
- #include "wwdebug.h"
- #include "colorspace.h"
- void Bitmap_Assert(bool condition)
- {
- WWASSERT(condition);
- }
- void BitmapHandlerClass::Create_Mipmap_B8G8R8A8(
- unsigned char* dest_surface,
- unsigned dest_surface_pitch,
- unsigned char* src_surface,
- unsigned src_surface_pitch,
- unsigned width,
- unsigned height)
- {
- unsigned src_pitch=src_surface_pitch/4;
- for (unsigned y=0;y<height;y+=2) {
- unsigned* dest=(unsigned*)dest_surface;
- dest_surface+=dest_surface_pitch;
- unsigned* src=(unsigned*)src_surface;
- src_surface+=src_surface_pitch;
- for (unsigned x=0;x<width;x+=2) {
- unsigned bgra3=src[src_pitch];
- unsigned bgra1=*src++;
- unsigned bgra4=src[src_pitch];
- unsigned bgra2=*src++;
- *dest++=Combine_A8R8G8B8(bgra1,bgra2,bgra3,bgra4);
- }
- }
- }
- void BitmapHandlerClass::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)
- {
- // Optimized loop if source and destination are 32 bit
- bool has_hsv_shift = hsv_shift[0]!=0.0f || hsv_shift[1]!=0.0f || hsv_shift[2]!=0.0f;
- if (src_format==dest_format && src_format==WW3D_FORMAT_A8R8G8B8) {
- dest_pitch/=4;
- src_pitch/=4;
- mip_pitch/=4;
- for (unsigned y=0;y<height/2;++y) {
- unsigned* dest_ptr=(unsigned*)dest_surface;
- dest_ptr+=2*y*dest_pitch;
- unsigned* src_ptr=(unsigned*)src_surface;
- src_ptr+=y*2*src_pitch;
- unsigned* mip_ptr=(unsigned*)mip_surface;
- mip_ptr+=y*mip_pitch;
- unsigned b8g8r8a8_00;
- unsigned b8g8r8a8_01;
- unsigned b8g8r8a8_10;
- unsigned b8g8r8a8_11;
- for (unsigned x=0;x<width/2;x++) {
- b8g8r8a8_10=src_ptr[src_pitch];
- dest_ptr[dest_pitch]=b8g8r8a8_10;
- b8g8r8a8_00=*src_ptr++;
- *dest_ptr++=b8g8r8a8_00;
- b8g8r8a8_11=src_ptr[src_pitch];
- dest_ptr[dest_pitch]=b8g8r8a8_11;
- b8g8r8a8_01=*src_ptr++;
- *dest_ptr++=b8g8r8a8_01;
- unsigned b8g8r8a8=Combine_A8R8G8B8(b8g8r8a8_00,b8g8r8a8_01,b8g8r8a8_10,b8g8r8a8_11);
- if (has_hsv_shift) {
- Recolor(b8g8r8a8,hsv_shift);
- }
- *mip_ptr++=b8g8r8a8;
- }
- }
- return;
- }
- WWASSERT(src_format!=WW3D_FORMAT_P8); // This function doesn't support paletted formats
- unsigned src_bpp=Get_Bytes_Per_Pixel(src_format);
- unsigned dest_bpp=Get_Bytes_Per_Pixel(dest_format);
- for (unsigned y=0;y<height/2;++y) {
- unsigned char* dest_ptr=dest_surface+2*y*dest_pitch;
- unsigned char* src_ptr=src_surface+y*2*src_pitch;
- unsigned char* mip_ptr=mip_surface+y*mip_pitch;
- unsigned b8g8r8a8_00;
- unsigned b8g8r8a8_01;
- unsigned b8g8r8a8_10;
- unsigned b8g8r8a8_11;
- for (unsigned x=0;x<width/2;x++,dest_ptr+=dest_bpp*2,src_ptr+=src_bpp*2,mip_ptr+=dest_bpp) {
- Read_B8G8R8A8(b8g8r8a8_00,src_ptr,src_format,NULL,0);
- Write_B8G8R8A8(dest_ptr,dest_format,b8g8r8a8_00);
- Read_B8G8R8A8(b8g8r8a8_01,src_ptr+src_bpp,src_format,NULL,0);
- Write_B8G8R8A8(dest_ptr+dest_bpp,dest_format,b8g8r8a8_01);
- Read_B8G8R8A8(b8g8r8a8_10,src_ptr+src_pitch,src_format,NULL,0);
- Write_B8G8R8A8(dest_ptr+dest_pitch,dest_format,b8g8r8a8_10);
- Read_B8G8R8A8(b8g8r8a8_11,src_ptr+src_bpp+src_pitch,src_format,NULL,0);
- Write_B8G8R8A8(dest_ptr+dest_bpp+dest_pitch,dest_format,b8g8r8a8_11);
- unsigned b8g8r8a8=Combine_A8R8G8B8(b8g8r8a8_00,b8g8r8a8_01,b8g8r8a8_10,b8g8r8a8_11);
- if (has_hsv_shift) {
- Recolor(b8g8r8a8,hsv_shift);
- }
- Write_B8G8R8A8(mip_ptr,dest_format,b8g8r8a8);
- }
- }
- }
- // ----------------------------------------------------------------------------
- //
- // Copy image from source surface to destination surface with stretch and color
- // space conversion if needed. If 'generate_mip_level' is set, process image
- // in 2x2 blocks and generate mipmap on top of the original source image while
- // copying.
- //
- // ----------------------------------------------------------------------------
- void BitmapHandlerClass::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)
- {
- WWASSERT(dest_surface_width);
- WWASSERT(dest_surface_height);
- // Bumpmap?
- if (dest_surface_format==WW3D_FORMAT_U8V8 ||
- dest_surface_format==WW3D_FORMAT_L6V5U5 ||
- dest_surface_format==WW3D_FORMAT_X8L8V8U8) {
- unsigned src_bpp=Get_Bytes_Per_Pixel(src_surface_format);
- for( unsigned y=0; y<dest_surface_height; y++ ) {
- unsigned char* dest_ptr=dest_surface;
- dest_ptr+=y*dest_surface_pitch;
- unsigned char* src_ptr_mid=src_surface;
- src_ptr_mid+=y*src_surface_pitch;
- unsigned char* src_ptr_next_line = ( src_ptr_mid + src_surface_pitch );
- unsigned char* src_ptr_prev_line = ( src_ptr_mid - src_surface_pitch );
- if( y == src_surface_height-1 ) // Don't go past the last line
- src_ptr_next_line = src_ptr_mid;
- if( y == 0 ) // Don't go before first line
- src_ptr_prev_line = src_ptr_mid;
- for( unsigned x=0; x<dest_surface_width; x++ ) {
- unsigned pixel00;
- unsigned pixel01;
- unsigned pixelM1;
- unsigned pixel10;
- unsigned pixel1M;
- Read_B8G8R8A8(pixel00,src_ptr_mid,src_surface_format,NULL,0);
- Read_B8G8R8A8(pixel01,src_ptr_mid+src_bpp,src_surface_format,NULL,0);
- Read_B8G8R8A8(pixelM1,src_ptr_mid-src_bpp,src_surface_format,NULL,0);
- Read_B8G8R8A8(pixel10,src_ptr_prev_line,src_surface_format,NULL,0);
- Read_B8G8R8A8(pixel1M,src_ptr_next_line,src_surface_format,NULL,0);
- // Convert to luminance
- unsigned char bv00;
- unsigned char bv01;
- unsigned char bvM1;
- unsigned char bv10;
- unsigned char bv1M;
- Write_B8G8R8A8(&bv00,WW3D_FORMAT_L8,pixel00);
- Write_B8G8R8A8(&bv01,WW3D_FORMAT_L8,pixel01);
- Write_B8G8R8A8(&bvM1,WW3D_FORMAT_L8,pixelM1);
- Write_B8G8R8A8(&bv10,WW3D_FORMAT_L8,pixel10);
- Write_B8G8R8A8(&bv1M,WW3D_FORMAT_L8,pixel1M);
- int v00=bv00,v01=bv01,vM1=bvM1,v10=bv10,v1M=bv1M;
- int iDu = (vM1-v01); // The delta-u bump value
- int iDv = (v1M-v10); // The delta-v bump value
- if( (v00 < vM1) && (v00 < v01) ) { // If we are at valley
- iDu = vM1-v00; // Choose greater of 1st order diffs
- if( iDu < v00-v01 )
- iDu = v00-v01;
- }
- // The luminance bump value (land masses are less shiny)
- unsigned short uL = ( v00>1 ) ? 63 : 127;
- switch(dest_surface_format) {
- case WW3D_FORMAT_U8V8:
- *dest_ptr++ = (unsigned char)iDu;
- *dest_ptr++ = (unsigned char)iDv;
- break;
- case WW3D_FORMAT_L6V5U5:
- *(unsigned short*)dest_ptr = (unsigned short)( ( (iDu>>3) & 0x1f ) << 0 );
- *(unsigned short*)dest_ptr |= (unsigned short)( ( (iDv>>3) & 0x1f ) << 5 );
- *(unsigned short*)dest_ptr |= (unsigned short)( ( ( uL>>2) & 0x3f ) << 10 );
- dest_ptr += 2;
- break;
- case WW3D_FORMAT_X8L8V8U8:
- *dest_ptr++ = (unsigned char)iDu;
- *dest_ptr++ = (unsigned char)iDv;
- *dest_ptr++ = (unsigned char)uL;
- *dest_ptr++ = (unsigned char)0L;
- break;
- default:
- WWASSERT(0); // Unknown bumpmap format
- break;
- }
- // Move one pixel to the left (src is 32-bpp)
- src_ptr_mid+=src_bpp;
- src_ptr_prev_line+=src_bpp;
- src_ptr_next_line+=src_bpp;
- }
- }
- return;
- }
- bool has_hsv_shift = hsv_shift[0]!=0.0f || hsv_shift[1]!=0.0f || hsv_shift[2]!=0.0f;
- if (src_surface_format==dest_surface_format && (src_surface_format==WW3D_FORMAT_A8R8G8B8 || src_surface_format==WW3D_FORMAT_X8R8G8B8)) {
- // One-to-one copy or scaling?
- dest_surface_pitch/=4;
- src_surface_pitch/=4;
- if (dest_surface_width==src_surface_width && dest_surface_height==src_surface_height) {
- // Generate the next mip level while copying the current surface?
- if (generate_mip_level) {
- if (dest_surface_width==1) {
- unsigned b8g8r8a8=*(unsigned*)src_surface;
- if (has_hsv_shift) Recolor(b8g8r8a8,hsv_shift);
- *(unsigned*)dest_surface=b8g8r8a8;
- }
- else {
- for (unsigned y=0;y<dest_surface_height/2;++y) {
- unsigned* dest_ptr=(unsigned*)dest_surface;
- dest_ptr+=2*y*dest_surface_pitch;
- unsigned* src_ptr=(unsigned*)src_surface;
- unsigned* mip_ptr=src_ptr;
- src_ptr+=y*2*src_surface_pitch;
- mip_ptr+=y*src_surface_pitch;
- unsigned b8g8r8a8_00;
- unsigned b8g8r8a8_01;
- unsigned b8g8r8a8_10;
- unsigned b8g8r8a8_11;
- for (unsigned x=0;x<dest_surface_width/2;x++) {
- // Read four pixels from the source
- b8g8r8a8_10=src_ptr[src_surface_pitch];
- b8g8r8a8_00=*src_ptr++;
- b8g8r8a8_11=src_ptr[src_surface_pitch];
- b8g8r8a8_01=*src_ptr++;
- // Recolor if necessary
- if (has_hsv_shift) {
- Recolor(b8g8r8a8_00,hsv_shift);
- Recolor(b8g8r8a8_01,hsv_shift);
- Recolor(b8g8r8a8_10,hsv_shift);
- Recolor(b8g8r8a8_11,hsv_shift);
- }
- // Write the four pixels to the destination
- dest_ptr[dest_surface_pitch]=b8g8r8a8_10;
- *dest_ptr++=b8g8r8a8_00;
- dest_ptr[dest_surface_pitch]=b8g8r8a8_11;
- *dest_ptr++=b8g8r8a8_01;
- // Write combined four pixels to the destination mip map level
- *mip_ptr++=Combine_A8R8G8B8(b8g8r8a8_00,b8g8r8a8_01,b8g8r8a8_10,b8g8r8a8_11);
- }
- }
- }
- }
- else {
- for (unsigned y=0;y<dest_surface_height;++y) {
- unsigned* dest_ptr=(unsigned*)dest_surface;
- dest_ptr+=y*dest_surface_pitch;
- const unsigned* src_ptr=(unsigned*)src_surface;
- src_ptr+=y*src_surface_pitch;
- if (has_hsv_shift) {
- for (unsigned x=0;x<dest_surface_width;++x) {
- unsigned b8g8r8a8=*src_ptr++;
- Recolor(b8g8r8a8,hsv_shift);
- *dest_ptr++=b8g8r8a8;
- }
- }
- else {
- for (unsigned x=0;x<dest_surface_width;++x) {
- *dest_ptr++=*src_ptr++;
- }
- }
- }
- }
- }
- else {
-
- // For now do only point-sampling
- for (unsigned y=0;y<dest_surface_height;++y) {
- unsigned* dest_ptr=(unsigned*)dest_surface;
- dest_ptr+=y*dest_surface_pitch;
- unsigned src_y=y*src_surface_height/dest_surface_height;
- const unsigned* src_ptr=(unsigned*)src_surface;
- src_ptr+=src_y*src_surface_pitch;
- for (unsigned x=0;x<dest_surface_width;++x) {
- unsigned src_x=x*src_surface_width/dest_surface_width;
- unsigned b8g8r8a8=src_ptr[src_x];
- if (has_hsv_shift) {
- Recolor(b8g8r8a8,hsv_shift);
- }
- *dest_ptr++=b8g8r8a8;
- }
- }
- }
- return;
- }
- unsigned dest_bpp=Get_Bytes_Per_Pixel(dest_surface_format);
- unsigned src_bpp=Get_Bytes_Per_Pixel(src_surface_format);
- // One-to-one copy or scaling?
- if (dest_surface_width==src_surface_width && dest_surface_height==src_surface_height) {
- // Generate the next mip level while copying the current surface?
- if (generate_mip_level) {
- WWASSERT(src_surface_format!=WW3D_FORMAT_P8); // Paletted textures can't be mipmapped
- if (dest_surface_width==1) {
- unsigned char* dest_ptr=dest_surface;
- unsigned char* src_ptr=src_surface;
- unsigned b8g8r8a8;
- Read_B8G8R8A8(b8g8r8a8,src_ptr,src_surface_format,src_palette,src_palette_bpp);
- if (has_hsv_shift) {
- Recolor(b8g8r8a8,hsv_shift);
- }
- Write_B8G8R8A8(dest_ptr,dest_surface_format,b8g8r8a8);
- }
- else {
- for (unsigned y=0;y<dest_surface_height/2;++y) {
- unsigned char* dest_ptr=dest_surface+2*y*dest_surface_pitch;
- unsigned char* src_ptr=src_surface+y*2*src_surface_pitch;
- unsigned char* mip_ptr=src_surface+y*src_surface_pitch;
- unsigned b8g8r8a8_00;
- unsigned b8g8r8a8_01;
- unsigned b8g8r8a8_10;
- unsigned b8g8r8a8_11;
- for (unsigned x=0;x<dest_surface_width/2;x++,dest_ptr+=dest_bpp*2,src_ptr+=src_bpp*2,mip_ptr+=src_bpp) {
- // Read four pixels from the source
- Read_B8G8R8A8(b8g8r8a8_00,src_ptr,src_surface_format,src_palette,src_palette_bpp);
- Read_B8G8R8A8(b8g8r8a8_01,src_ptr+src_bpp,src_surface_format,src_palette,src_palette_bpp);
- Read_B8G8R8A8(b8g8r8a8_10,src_ptr+src_surface_pitch,src_surface_format,src_palette,src_palette_bpp);
- Read_B8G8R8A8(b8g8r8a8_11,src_ptr+src_bpp+src_surface_pitch,src_surface_format,src_palette,src_palette_bpp);
- // Recolor if necessary
- if (has_hsv_shift) {
- Recolor(b8g8r8a8_00,hsv_shift);
- Recolor(b8g8r8a8_01,hsv_shift);
- Recolor(b8g8r8a8_10,hsv_shift);
- Recolor(b8g8r8a8_11,hsv_shift);
- }
- // Write the four pixels to the destination
- Write_B8G8R8A8(dest_ptr,dest_surface_format,b8g8r8a8_00);
- Write_B8G8R8A8(dest_ptr+dest_bpp,dest_surface_format,b8g8r8a8_01);
- Write_B8G8R8A8(dest_ptr+dest_surface_pitch,dest_surface_format,b8g8r8a8_10);
- Write_B8G8R8A8(dest_ptr+dest_bpp+dest_surface_pitch,dest_surface_format,b8g8r8a8_11);
- // Write combined four pixels to the destination mip map level
- unsigned b8g8r8a8=Combine_A8R8G8B8(b8g8r8a8_00,b8g8r8a8_01,b8g8r8a8_10,b8g8r8a8_11);
- Write_B8G8R8A8(mip_ptr,src_surface_format,b8g8r8a8);
- }
- }
- }
- }
- else {
- for (unsigned y=0;y<dest_surface_height;++y) {
- unsigned char* dest_ptr=dest_surface+y*dest_surface_pitch;
- const unsigned char* src_ptr=src_surface+y*src_surface_pitch;
- if (has_hsv_shift) {
- for (unsigned x=0;x<dest_surface_width;++x,dest_ptr+=dest_bpp,src_ptr+=src_bpp) {
- Copy_Pixel(dest_ptr,dest_surface_format,src_ptr,src_surface_format,src_palette,src_palette_bpp,hsv_shift);
- }
- }
- else {
- for (unsigned x=0;x<dest_surface_width;++x,dest_ptr+=dest_bpp,src_ptr+=src_bpp) {
- Copy_Pixel(dest_ptr,dest_surface_format,src_ptr,src_surface_format,src_palette,src_palette_bpp);
- }
- }
- }
- }
- }
- else {
-
- // For now do only point-sampling
- for (unsigned y=0;y<dest_surface_height;++y) {
- unsigned char* dest_ptr=dest_surface+y*dest_surface_pitch;
- unsigned src_y=y*src_surface_height/dest_surface_height;
- const unsigned char* src_ptr=src_surface+src_y*src_surface_pitch;
- if (has_hsv_shift) {
- for (unsigned x=0;x<dest_surface_width;++x,dest_ptr+=dest_bpp) {
- unsigned src_x=x*src_surface_width/dest_surface_width;
- src_x*=src_bpp;
- Copy_Pixel(dest_ptr,dest_surface_format,src_ptr+src_x,src_surface_format,src_palette,src_palette_bpp,hsv_shift);
- }
- }
- else {
- for (unsigned x=0;x<dest_surface_width;++x,dest_ptr+=dest_bpp) {
- unsigned src_x=x*src_surface_width/dest_surface_width;
- src_x*=src_bpp;
- Copy_Pixel(dest_ptr,dest_surface_format,src_ptr+src_x,src_surface_format,src_palette,src_palette_bpp);
- }
- }
- }
- }
- }
|