This repository has been archived on 2025-02-27. You can view files and clone it, but cannot push or open issues or pull requests.
CnC_Renegade/Code/ww3d2/bitmaphandler.cpp

383 lines
13 KiB
C++

/*
** Command & Conquer Renegade(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"
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)
{
// Optimized loop if source and destination are 32 bit
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;
*mip_ptr++=Combine_A8R8G8B8(b8g8r8a8_00,b8g8r8a8_01,b8g8r8a8_10,b8g8r8a8_11);
}
}
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);
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)
{
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;
}
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*)dest_surface=*(unsigned*)src_surface;
}
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++) {
b8g8r8a8_10=src_ptr[src_surface_pitch];
dest_ptr[dest_surface_pitch]=b8g8r8a8_10;
b8g8r8a8_00=*src_ptr++;
*dest_ptr++=b8g8r8a8_00;
b8g8r8a8_11=src_ptr[src_surface_pitch];
dest_ptr[dest_surface_pitch]=b8g8r8a8_11;
b8g8r8a8_01=*src_ptr++;
*dest_ptr++=b8g8r8a8_01;
*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;
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;
*dest_ptr++=src_ptr[src_x];
}
}
}
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);
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_B8G8R8A8(b8g8r8a8_00,src_ptr,src_surface_format,src_palette,src_palette_bpp);
Write_B8G8R8A8(dest_ptr,dest_surface_format,b8g8r8a8_00);
Read_B8G8R8A8(b8g8r8a8_01,src_ptr+src_bpp,src_surface_format,src_palette,src_palette_bpp);
Write_B8G8R8A8(dest_ptr+dest_bpp,dest_surface_format,b8g8r8a8_01);
Read_B8G8R8A8(b8g8r8a8_10,src_ptr+src_surface_pitch,src_surface_format,src_palette,src_palette_bpp);
Write_B8G8R8A8(dest_ptr+dest_surface_pitch,dest_surface_format,b8g8r8a8_10);
Read_B8G8R8A8(b8g8r8a8_11,src_ptr+src_bpp+src_surface_pitch,src_surface_format,src_palette,src_palette_bpp);
Write_B8G8R8A8(dest_ptr+dest_bpp+dest_surface_pitch,dest_surface_format,b8g8r8a8_11);
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;
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;
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);
}
}
}
}