This repository has been archived on 2022-06-27. You can view files and clone it, but cannot push or open issues or pull requests.
Xash3DArchive/launch/imagelib/img_dds.c

2481 lines
72 KiB
C

//=======================================================================
// Copyright XashXT Group 2008 ©
// img_dds.c - dds format load & save
//=======================================================================
#include "imagelib.h"
#include "mathlib.h"
// TODO: tune this ?
#define REDWEIGHT 4
#define GREENWEIGHT 16
#define BLUEWEIGHT 1
#define CHAN_MAX 255
#define ALPHACUT 127
static void Image_BaseColorSearch( byte *blkaddr, byte srccolors[4][4][4], byte *bestcolor[2], int numxpixels, int numypixels, int type, qboolean haveAlpha )
{
// use same luminance-weighted distance metric to determine encoding as for finding the base colors
// TODO: could also try to find a better encoding for the 3-color-encoding type, this really should be done
// if it's rgba_dxt1 and we have alpha in the block, currently even values which will be mapped to black
// due to their alpha value will influence the result
int i, j, colors, z;
uint pixerror, pixerrorred, pixerrorgreen, pixerrorblue, pixerrorbest;
int colordist, blockerrlin[2][3];
byte nrcolor[2];
int pixerrorcolorbest[3];
byte enc = 0;
byte cv[4][4];
byte testcolor[2][3];
if(((bestcolor[0][0] & 0xf8) << 8 | (bestcolor[0][1] & 0xfc) << 3 | bestcolor[0][2] >> 3) < ((bestcolor[1][0] & 0xf8) << 8 | (bestcolor[1][1] & 0xfc) << 3 | bestcolor[1][2] >> 3))
{
testcolor[0][0] = bestcolor[0][0];
testcolor[0][1] = bestcolor[0][1];
testcolor[0][2] = bestcolor[0][2];
testcolor[1][0] = bestcolor[1][0];
testcolor[1][1] = bestcolor[1][1];
testcolor[1][2] = bestcolor[1][2];
}
else
{
testcolor[1][0] = bestcolor[0][0];
testcolor[1][1] = bestcolor[0][1];
testcolor[1][2] = bestcolor[0][2];
testcolor[0][0] = bestcolor[1][0];
testcolor[0][1] = bestcolor[1][1];
testcolor[0][2] = bestcolor[1][2];
}
for( i = 0; i < 3; i ++ )
{
cv[0][i] = testcolor[0][i];
cv[1][i] = testcolor[1][i];
cv[2][i] = (testcolor[0][i] * 2 + testcolor[1][i]) / 3;
cv[3][i] = (testcolor[0][i] + testcolor[1][i] * 2) / 3;
}
blockerrlin[0][0] = 0;
blockerrlin[0][1] = 0;
blockerrlin[0][2] = 0;
blockerrlin[1][0] = 0;
blockerrlin[1][1] = 0;
blockerrlin[1][2] = 0;
nrcolor[0] = 0;
nrcolor[1] = 0;
for( j = 0; j < numypixels; j++ )
{
for( i = 0; i < numxpixels; i++ )
{
pixerrorbest = 0xffffffff;
for( colors = 0; colors < 4; colors++ )
{
colordist = srccolors[j][i][0] - (cv[colors][0]);
pixerror = colordist * colordist * REDWEIGHT;
pixerrorred = colordist;
colordist = srccolors[j][i][1] - (cv[colors][1]);
pixerror += colordist * colordist * GREENWEIGHT;
pixerrorgreen = colordist;
colordist = srccolors[j][i][2] - (cv[colors][2]);
pixerror += colordist * colordist * BLUEWEIGHT;
pixerrorblue = colordist;
if( pixerror < pixerrorbest )
{
enc = colors;
pixerrorbest = pixerror;
pixerrorcolorbest[0] = pixerrorred;
pixerrorcolorbest[1] = pixerrorgreen;
pixerrorcolorbest[2] = pixerrorblue;
}
}
if( enc == 0 )
{
for( z = 0; z < 3; z++ )
{
blockerrlin[0][z] += 3 * pixerrorcolorbest[z];
}
nrcolor[0] += 3;
}
else if( enc == 2 )
{
for( z = 0; z < 3; z++ )
{
blockerrlin[0][z] += 2 * pixerrorcolorbest[z];
}
nrcolor[0] += 2;
for( z = 0; z < 3; z++ )
{
blockerrlin[1][z] += 1 * pixerrorcolorbest[z];
}
nrcolor[1] += 1;
}
else if( enc == 3 )
{
for( z = 0; z < 3; z++ )
{
blockerrlin[0][z] += 1 * pixerrorcolorbest[z];
}
nrcolor[0] += 1;
for( z = 0; z < 3; z++ )
{
blockerrlin[1][z] += 2 * pixerrorcolorbest[z];
}
nrcolor[1] += 2;
}
else if( enc == 1 )
{
for( z = 0; z < 3; z++ )
{
blockerrlin[1][z] += 3 * pixerrorcolorbest[z];
}
nrcolor[1] += 3;
}
}
}
if( nrcolor[0] == 0 ) nrcolor[0] = 1;
if( nrcolor[1] == 0 ) nrcolor[1] = 1;
for( j = 0; j < 2; j++ )
{
for( i = 0; i < 3; i++ )
{
int newvalue = testcolor[j][i] + blockerrlin[j][i] / nrcolor[j];
if( newvalue <= 0 ) testcolor[j][i] = 0;
else if( newvalue >= 255 ) testcolor[j][i] = 255;
else testcolor[j][i] = newvalue;
}
}
if((abs(testcolor[0][0] - testcolor[1][0]) < 8) && (abs(testcolor[0][1] - testcolor[1][1]) < 4) && (abs(testcolor[0][2] - testcolor[1][2]) < 8))
{
// both colors are so close they might get encoded as the same 16bit values
byte coldiffred, coldiffgreen, coldiffblue, coldiffmax, factor, ind0, ind1;
coldiffred = abs(testcolor[0][0] - testcolor[1][0]);
coldiffgreen = 2 * abs(testcolor[0][1] - testcolor[1][1]);
coldiffblue = abs(testcolor[0][2] - testcolor[1][2]);
coldiffmax = coldiffred;
if( coldiffmax < coldiffgreen ) coldiffmax = coldiffgreen;
if( coldiffmax < coldiffblue ) coldiffmax = coldiffblue;
if( coldiffmax > 0 )
{
if( coldiffmax > 4 ) factor = 2;
else if( coldiffmax > 2 ) factor = 3;
else factor = 4;
// won't do much if the color value is near 255...
// argh so many ifs
if( testcolor[1][1] >= testcolor[0][1] )
{
ind1 = 1;
ind0 = 0;
}
else
{
ind1 = 0;
ind0 = 1;
}
if((testcolor[ind1][1] + factor * coldiffgreen) <= 255 )
testcolor[ind1][1] += factor * coldiffgreen;
else testcolor[ind1][1] = 255;
if((testcolor[ind1][0] - testcolor[ind0][1]) > 0 )
{
if((testcolor[ind1][0] + factor * coldiffred) <= 255 )
testcolor[ind1][0] += factor * coldiffred;
else testcolor[ind1][0] = 255;
}
else
{
if((testcolor[ind0][0] + factor * coldiffred) <= 255 )
testcolor[ind0][0] += factor * coldiffred;
else testcolor[ind0][0] = 255;
}
if((testcolor[ind1][2] - testcolor[ind0][2]) > 0 )
{
if((testcolor[ind1][2] + factor * coldiffblue) <= 255 )
testcolor[ind1][2] += factor * coldiffblue;
else testcolor[ind1][2] = 255;
}
else
{
if((testcolor[ind0][2] + factor * coldiffblue) <= 255 )
testcolor[ind0][2] += factor * coldiffblue;
else testcolor[ind0][2] = 255;
}
}
}
if(((testcolor[0][0] & 0xf8) << 8 | (testcolor[0][1] & 0xfc) << 3 | testcolor[0][2] >> 3) < ((testcolor[1][0] & 0xf8) << 8 | (testcolor[1][1] & 0xfc) << 3 | testcolor[1][2]) >> 3)
{
for( i = 0; i < 3; i++ )
{
bestcolor[0][i] = testcolor[0][i];
bestcolor[1][i] = testcolor[1][i];
}
}
else
{
for( i = 0; i < 3; i++ )
{
bestcolor[0][i] = testcolor[1][i];
bestcolor[1][i] = testcolor[0][i];
}
}
}
static void Image_StoreBlock( byte *blkaddr, byte srccolors[4][4][4], byte *bestcolor[2], int numxpixels, int numypixels, uint type, qboolean haveAlpha )
{
// use same luminance-weighted distance metric to determine encoding as for finding the base colors
int i, j, colors;
uint testerror, testerror2, pixerror, pixerrorbest;
int colordist;
word color0, color1, tempcolor;
uint bits = 0, bits2 = 0;
byte *colorptr;
byte enc = 0;
byte cv[4][4];
bestcolor[0][0] = bestcolor[0][0] & 0xf8;
bestcolor[0][1] = bestcolor[0][1] & 0xfc;
bestcolor[0][2] = bestcolor[0][2] & 0xf8;
bestcolor[1][0] = bestcolor[1][0] & 0xf8;
bestcolor[1][1] = bestcolor[1][1] & 0xfc;
bestcolor[1][2] = bestcolor[1][2] & 0xf8;
color0 = bestcolor[0][0] << 8 | bestcolor[0][1] << 3 | bestcolor[0][2] >> 3;
color1 = bestcolor[1][0] << 8 | bestcolor[1][1] << 3 | bestcolor[1][2] >> 3;
if( color0 < color1 )
{
tempcolor = color0;
color0 = color1;
color1 = tempcolor;
colorptr = bestcolor[0];
bestcolor[0] = bestcolor[1];
bestcolor[1] = colorptr;
}
for( i = 0; i < 3; i++ )
{
cv[0][i] = bestcolor[0][i];
cv[1][i] = bestcolor[1][i];
cv[2][i] = (bestcolor[0][i] * 2 + bestcolor[1][i]) / 3;
cv[3][i] = (bestcolor[0][i] + bestcolor[1][i] * 2) / 3;
}
testerror = 0;
for( j = 0; j < numypixels; j++ )
{
for( i = 0; i < numxpixels; i++ )
{
pixerrorbest = 0xffffffff;
for( colors = 0; colors < 4; colors++ )
{
colordist = srccolors[j][i][0] - cv[colors][0];
pixerror = colordist * colordist * REDWEIGHT;
colordist = srccolors[j][i][1] - cv[colors][1];
pixerror += colordist * colordist * GREENWEIGHT;
colordist = srccolors[j][i][2] - cv[colors][2];
pixerror += colordist * colordist * BLUEWEIGHT;
if( pixerror < pixerrorbest )
{
pixerrorbest = pixerror;
enc = colors;
}
}
testerror += pixerrorbest;
bits |= enc << (2 * (j * 4 + i));
}
}
for( i = 0; i < 3; i++ )
{
cv[2][i] = (bestcolor[0][i] + bestcolor[1][i]) / 2;
// this isn't used. Looks like the black color constant can only be used
// with RGB_DXT1 if I read the spec correctly (note though that the radeon gpu disagrees,
// it will decode 3 to black even with DXT3/5), and due to how the color searching works
// it won't get used even then
cv[3][i] = 0;
}
testerror2 = 0;
for( j = 0; j < numypixels; j++ )
{
for( i = 0; i < numxpixels; i++ )
{
pixerrorbest = 0xffffffff;
if((type == PF_DXT1) && (srccolors[j][i][3] <= ALPHACUT))
{
enc = 3;
pixerrorbest = 0; // don't calculate error
}
else
{
// we're calculating the same what we have done already for colors 0-1 above...
for( colors = 0; colors < 3; colors++ )
{
colordist = srccolors[j][i][0] - cv[colors][0];
pixerror = colordist * colordist * REDWEIGHT;
colordist = srccolors[j][i][1] - cv[colors][1];
pixerror += colordist * colordist * GREENWEIGHT;
colordist = srccolors[j][i][2] - cv[colors][2];
pixerror += colordist * colordist * BLUEWEIGHT;
if( pixerror < pixerrorbest )
{
pixerrorbest = pixerror;
// need to exchange colors later
if( colors > 1 ) enc = colors;
else enc = colors ^ 1;
}
}
}
testerror2 += pixerrorbest;
bits2 |= enc << (2 * (j * 4 + i));
}
}
// finally we're finished, write back colors and bits
if((testerror > testerror2) || (haveAlpha))
{
*blkaddr++ = color1 & 0xFF;
*blkaddr++ = color1 >> 8;
*blkaddr++ = color0 & 0xFF;
*blkaddr++ = color0 >> 8;
*blkaddr++ = bits2 & 0xFF;
*blkaddr++ = (bits2 >> 8) & 0xFF;
*blkaddr++ = (bits2 >> 16) & 0xFF;
*blkaddr = bits2 >> 24;
}
else
{
*blkaddr++ = color0 & 0xFF;
*blkaddr++ = color0 >> 8;
*blkaddr++ = color1 & 0xFF;
*blkaddr++ = color1 >> 8;
*blkaddr++ = bits & 0xFF;
*blkaddr++ = ( bits >> 8) & 0xFF;
*blkaddr++ = ( bits >> 16) & 0xFF;
*blkaddr = bits >> 24;
}
}
static void Image_EncodeColorBlock( byte *blkaddr, byte srccolors[4][4][4], int numxpixels, int numypixels, uint type, int flags )
{
// simplistic approach. We need two base colors, simply use the "highest" and the "lowest" color
// present in the picture as base colors
// define lowest and highest color as shortest and longest vector to 0/0/0, though the
// vectors are weighted similar to their importance in rgb-luminance conversion
// doesn't work too well though...
// this seems to be a rather difficult problem
byte *bestcolor[2];
byte basecolors[2][3];
uint lowcv, highcv, testcv;
qboolean haveAlpha = false;
byte i, j;
lowcv = highcv = srccolors[0][0][0] * srccolors[0][0][0] * REDWEIGHT + srccolors[0][0][1] * srccolors[0][0][1] * GREENWEIGHT + srccolors[0][0][2] * srccolors[0][0][2] * BLUEWEIGHT;
bestcolor[0] = bestcolor[1] = srccolors[0][0];
for( j = 0; j < numypixels; j++ )
{
for( i = 0; i < numxpixels; i++ )
{
// don't use this as a base color if the pixel will get black/transparent anyway
if((type != PF_DXT1) || (srccolors[j][i][3] <= ALPHACUT))
{
testcv = srccolors[j][i][0] * srccolors[j][i][0] * REDWEIGHT + srccolors[j][i][1] * srccolors[j][i][1] * GREENWEIGHT + srccolors[j][i][2] * srccolors[j][i][2] * BLUEWEIGHT;
if( testcv > highcv )
{
highcv = testcv;
bestcolor[1] = srccolors[j][i];
}
else if( testcv < lowcv )
{
lowcv = testcv;
bestcolor[0] = srccolors[j][i];
}
}
else haveAlpha = true;
}
}
if( type != PF_DXT1 )
{
// manually set alpha for DXT3 or DXT5
if( flags & IMAGE_HAS_ALPHA ) haveAlpha = true;
else haveAlpha = false;
}
// make sure the original color values won't get touched...
for( j = 0; j < 2; j++ )
{
for( i = 0; i < 3; i++ )
{
basecolors[j][i] = bestcolor[j][i];
}
}
bestcolor[0] = basecolors[0];
bestcolor[1] = basecolors[1];
// try to find better base colors
Image_BaseColorSearch( blkaddr, srccolors, bestcolor, numxpixels, numypixels, type, haveAlpha );
// find the best encoding for these colors, and store the result
Image_StoreBlock( blkaddr, srccolors, bestcolor, numxpixels, numypixels, type, haveAlpha );
}
static void Image_StoreAlphaBlock( byte *blkaddr, byte alphabase1, byte alphabase2, byte alphaenc[16] )
{
*blkaddr++ = alphabase1;
*blkaddr++ = alphabase2;
*blkaddr++ = alphaenc[0] | (alphaenc[1] << 3) | ((alphaenc[2] & 3) << 6);
*blkaddr++ = (alphaenc[2] >> 2) | (alphaenc[3] << 1) | (alphaenc[4] << 4) | ((alphaenc[5] & 1) << 7);
*blkaddr++ = (alphaenc[5] >> 1) | (alphaenc[6] << 2) | (alphaenc[7] << 5);
*blkaddr++ = alphaenc[8] | (alphaenc[9] << 3) | ((alphaenc[10] & 3) << 6);
*blkaddr++ = (alphaenc[10] >> 2) | (alphaenc[11] << 1) | (alphaenc[12] << 4) | ((alphaenc[13] & 1) << 7);
*blkaddr++ = (alphaenc[13] >> 1) | (alphaenc[14] << 2) | (alphaenc[15] << 5);
}
static void Image_EncodeDXT5alpha( byte *blkaddr, byte srccolors[4][4][4], int numxpixels, int numypixels )
{
byte alphabase[2], alphause[2];
short alphatest[2];
uint alphablockerror1, alphablockerror2, alphablockerror3;
byte i, j, aindex, acutValues[7];
byte alphaenc1[16], alphaenc2[16], alphaenc3[16];
qboolean alphaabsmin = false;
qboolean alphaabsmax = false;
short alphadist;
// find lowest and highest alpha value in block, alphabase[0] lowest, alphabase[1] highest
alphabase[0] = 0xFF;
alphabase[1] = 0x00;
for( j = 0; j < numypixels; j++ )
{
for( i = 0; i < numxpixels; i++ )
{
if( srccolors[j][i][3] == 0 )
alphaabsmin = true;
else if( srccolors[j][i][3] == 255 )
alphaabsmax = true;
else
{
if( srccolors[j][i][3] > alphabase[1] )
alphabase[1] = srccolors[j][i][3];
if( srccolors[j][i][3] < alphabase[0] )
alphabase[0] = srccolors[j][i][3];
}
}
}
if((alphabase[0] > alphabase[1]) && !(alphaabsmin && alphaabsmax))
{
// one color, either max or min
// shortcut here since it is a very common case (and also avoids later problems)
// || (alphabase[0] == alphabase[1] && !alphaabsmin && !alphaabsmax)
// could also thest for alpha0 == alpha1 (and not min/max), but probably not common, so don't bother
alphabase[0] = srccolors[0][0][3];
*blkaddr++ = alphabase[0];
*blkaddr++;
*blkaddr++ = 0;
*blkaddr++ = 0;
*blkaddr++ = 0;
*blkaddr++ = 0;
*blkaddr++ = 0;
*blkaddr++ = 0;
return;
}
// find best encoding for alpha0 > alpha1
// it's possible this encoding is better even if both alphaabsmin and alphaabsmax are true
alphablockerror1 = 0x0;
alphablockerror2 = 0xffffffff;
alphablockerror3 = 0xffffffff;
if( alphaabsmin ) alphause[0] = 0;
else alphause[0] = alphabase[0];
if( alphaabsmax ) alphause[1] = 255;
else alphause[1] = alphabase[1];
// calculate the 7 cut values, just the middle between 2 of the computed alpha values
for( aindex = 0; aindex < 7; aindex++ )
{
// don't forget here is always rounded down
acutValues[aindex] = (alphause[0] * (2*aindex + 1) + alphause[1] * (14 - (2*aindex + 1))) / 14;
}
for( j = 0; j < numypixels; j++ )
{
for( i = 0; i < numxpixels; i++ )
{
// maybe it's overkill to have the most complicated calculation just for the error
// calculation which we only need to figure out if encoding1 or encoding2 is better...
if( srccolors[j][i][3] > acutValues[0] )
{
alphaenc1[4*j + i] = 0;
alphadist = srccolors[j][i][3] - alphause[1];
}
else if( srccolors[j][i][3] > acutValues[1] )
{
alphaenc1[4*j + i] = 2;
alphadist = srccolors[j][i][3] - (alphause[1] * 6 + alphause[0] * 1) / 7;
}
else if( srccolors[j][i][3] > acutValues[2] )
{
alphaenc1[4*j + i] = 3;
alphadist = srccolors[j][i][3] - (alphause[1] * 5 + alphause[0] * 2) / 7;
}
else if( srccolors[j][i][3] > acutValues[3] )
{
alphaenc1[4*j + i] = 4;
alphadist = srccolors[j][i][3] - (alphause[1] * 4 + alphause[0] * 3) / 7;
}
else if( srccolors[j][i][3] > acutValues[4] )
{
alphaenc1[4*j + i] = 5;
alphadist = srccolors[j][i][3] - (alphause[1] * 3 + alphause[0] * 4) / 7;
}
else if( srccolors[j][i][3] > acutValues[5] )
{
alphaenc1[4*j + i] = 6;
alphadist = srccolors[j][i][3] - (alphause[1] * 2 + alphause[0] * 5) / 7;
}
else if( srccolors[j][i][3] > acutValues[6] )
{
alphaenc1[4*j + i] = 7;
alphadist = srccolors[j][i][3] - (alphause[1] * 1 + alphause[0] * 6) / 7;
}
else
{
alphaenc1[4*j + i] = 1;
alphadist = srccolors[j][i][3] - alphause[0];
}
alphablockerror1 += alphadist * alphadist;
}
}
// it's not very likely this encoding is better if both alphaabsmin and alphaabsmax
// are false but try it anyway
if( alphablockerror1 >= 32 )
{
// don't bother if encoding is already very good, this condition should also imply
// we have valid alphabase colors which we absolutely need (alphabase[0] <= alphabase[1])
alphablockerror2 = 0;
for( aindex = 0; aindex < 5; aindex++ )
{
// don't forget here is always rounded down
acutValues[aindex] = (alphabase[0] * (10 - (2*aindex + 1)) + alphabase[1] * (2*aindex + 1)) / 10;
}
for( j = 0; j < numypixels; j++ )
{
for( i = 0; i < numxpixels; i++ )
{
// maybe it's overkill to have the most complicated calculation just for the error
// calculation which we only need to figure out if encoding1 or encoding2 is better...
if( srccolors[j][i][3] == 0 )
{
alphaenc2[4*j + i] = 6;
alphadist = 0;
}
else if( srccolors[j][i][3] == 255 )
{
alphaenc2[4*j + i] = 7;
alphadist = 0;
}
else if( srccolors[j][i][3] <= acutValues[0] )
{
alphaenc2[4*j + i] = 0;
alphadist = srccolors[j][i][3] - alphabase[0];
}
else if( srccolors[j][i][3] <= acutValues[1] )
{
alphaenc2[4*j + i] = 2;
alphadist = srccolors[j][i][3] - (alphabase[0] * 4 + alphabase[1] * 1) / 5;
}
else if( srccolors[j][i][3] <= acutValues[2] )
{
alphaenc2[4*j + i] = 3;
alphadist = srccolors[j][i][3] - (alphabase[0] * 3 + alphabase[1] * 2) / 5;
}
else if( srccolors[j][i][3] <= acutValues[3] )
{
alphaenc2[4*j + i] = 4;
alphadist = srccolors[j][i][3] - (alphabase[0] * 2 + alphabase[1] * 3) / 5;
}
else if( srccolors[j][i][3] <= acutValues[4] )
{
alphaenc2[4*j + i] = 5;
alphadist = srccolors[j][i][3] - (alphabase[0] * 1 + alphabase[1] * 4) / 5;
}
else
{
alphaenc2[4*j + i] = 1;
alphadist = srccolors[j][i][3] - alphabase[1];
}
alphablockerror2 += alphadist * alphadist;
}
}
// skip this if the error is already very small
// this encoding is MUCH better on average than #2 though, but expensive!
if((alphablockerror2 > 96) && (alphablockerror1 > 96))
{
short blockerrlin1 = 0;
short blockerrlin2 = 0;
byte nralphainrangelow = 0;
byte nralphainrangehigh = 0;
alphatest[0] = 0xff;
alphatest[1] = 0x0;
// if we have large range it's likely there are values close to 0/255, try to map them to 0/255
for( j = 0; j < numypixels; j++ )
{
for( i = 0; i < numxpixels; i++ )
{
if((srccolors[j][i][3] > alphatest[1]) && (srccolors[j][i][3] < (255 -(alphabase[1] - alphabase[0]) / 28)))
alphatest[1] = srccolors[j][i][3];
if((srccolors[j][i][3] < alphatest[0]) && (srccolors[j][i][3] > (alphabase[1] - alphabase[0]) / 28))
alphatest[0] = srccolors[j][i][3];
}
}
// shouldn't happen too often, don't really care about those degenerated cases
if( alphatest[1] <= alphatest[0] )
{
alphatest[0] = 1;
alphatest[1] = 254;
}
for( aindex = 0; aindex < 5; aindex++ )
{
// don't forget here is always rounded down
acutValues[aindex] = (alphatest[0] * (10 - (2*aindex + 1)) + alphatest[1] * (2*aindex + 1)) / 10;
}
// find the "average" difference between the alpha values and the next encoded value.
// this is then used to calculate new base values.
// should there be some weighting, i.e. those values closer to alphatest[x] have more weight,
// since they will see more improvement, and also because the values in the middle are somewhat
// likely to get no improvement at all (because the base values might move in different directions)?
// OTOH it would mean the values in the middle are even less likely to get an improvement
for( j = 0; j < numypixels; j++ )
{
for( i = 0; i < numxpixels; i++ )
{
if( srccolors[j][i][3] <= alphatest[0] / 2 )
{
// this does nothing
}
else if( srccolors[j][i][3] > ((255 + alphatest[1]) / 2))
{
// this does nothing
}
else if( srccolors[j][i][3] <= acutValues[0] )
{
blockerrlin1 += (srccolors[j][i][3] - alphatest[0]);
nralphainrangelow += 1;
}
else if( srccolors[j][i][3] <= acutValues[1] )
{
blockerrlin1 += (srccolors[j][i][3] - (alphatest[0] * 4 + alphatest[1] * 1) / 5);
blockerrlin2 += (srccolors[j][i][3] - (alphatest[0] * 4 + alphatest[1] * 1) / 5);
nralphainrangelow += 1;
nralphainrangehigh += 1;
}
else if( srccolors[j][i][3] <= acutValues[2] )
{
blockerrlin1 += (srccolors[j][i][3] - (alphatest[0] * 3 + alphatest[1] * 2) / 5);
blockerrlin2 += (srccolors[j][i][3] - (alphatest[0] * 3 + alphatest[1] * 2) / 5);
nralphainrangelow += 1;
nralphainrangehigh += 1;
}
else if( srccolors[j][i][3] <= acutValues[3] )
{
blockerrlin1 += (srccolors[j][i][3] - (alphatest[0] * 2 + alphatest[1] * 3) / 5);
blockerrlin2 += (srccolors[j][i][3] - (alphatest[0] * 2 + alphatest[1] * 3) / 5);
nralphainrangelow += 1;
nralphainrangehigh += 1;
}
else if( srccolors[j][i][3] <= acutValues[4] )
{
blockerrlin1 += (srccolors[j][i][3] - (alphatest[0] * 1 + alphatest[1] * 4) / 5);
blockerrlin2 += (srccolors[j][i][3] - (alphatest[0] * 1 + alphatest[1] * 4) / 5);
nralphainrangelow += 1;
nralphainrangehigh += 1;
}
else
{
blockerrlin2 += (srccolors[j][i][3] - alphatest[1]);
nralphainrangehigh += 1;
}
}
}
// shouldn't happen often, needed to avoid div by zero
if( nralphainrangelow == 0 ) nralphainrangelow = 1;
if( nralphainrangehigh == 0 ) nralphainrangehigh = 1;
alphatest[0] = alphatest[0] + (blockerrlin1 / nralphainrangelow);
// again shouldn't really happen often...
if( alphatest[0] < 0 )
{
alphatest[0] = 0;
}
alphatest[1] = alphatest[1] + (blockerrlin2 / nralphainrangehigh);
if( alphatest[1] > 255 )
{
alphatest[1] = 255;
}
alphablockerror3 = 0;
for( aindex = 0; aindex < 5; aindex++ )
{
// don't forget here is always rounded down
acutValues[aindex] = (alphatest[0] * (10 - (2*aindex + 1)) + alphatest[1] * (2*aindex + 1)) / 10;
}
for( j = 0; j < numypixels; j++ )
{
for( i = 0; i < numxpixels; i++ )
{
// maybe it's overkill to have the most complicated calculation just for the error
// calculation which we only need to figure out if encoding1 or encoding2 is better...
if( srccolors[j][i][3] <= alphatest[0] / 2 )
{
alphaenc3[4*j + i] = 6;
alphadist = srccolors[j][i][3];
}
else if( srccolors[j][i][3] > ((255 + alphatest[1]) / 2))
{
alphaenc3[4*j + i] = 7;
alphadist = 255 - srccolors[j][i][3];
}
else if( srccolors[j][i][3] <= acutValues[0] )
{
alphaenc3[4*j + i] = 0;
alphadist = srccolors[j][i][3] - alphatest[0];
}
else if( srccolors[j][i][3] <= acutValues[1] )
{
alphaenc3[4*j + i] = 2;
alphadist = srccolors[j][i][3] - (alphatest[0] * 4 + alphatest[1] * 1) / 5;
}
else if( srccolors[j][i][3] <= acutValues[2] )
{
alphaenc3[4*j + i] = 3;
alphadist = srccolors[j][i][3] - (alphatest[0] * 3 + alphatest[1] * 2) / 5;
}
else if( srccolors[j][i][3] <= acutValues[3] )
{
alphaenc3[4*j + i] = 4;
alphadist = srccolors[j][i][3] - (alphatest[0] * 2 + alphatest[1] * 3) / 5;
}
else if( srccolors[j][i][3] <= acutValues[4] )
{
alphaenc3[4*j + i] = 5;
alphadist = srccolors[j][i][3] - (alphatest[0] * 1 + alphatest[1] * 4) / 5;
}
else
{
alphaenc3[4*j + i] = 1;
alphadist = srccolors[j][i][3] - alphatest[1];
}
alphablockerror3 += alphadist * alphadist;
}
}
}
}
// write the alpha values and encoding back.
if((alphablockerror1 <= alphablockerror2) && (alphablockerror1 <= alphablockerror3))
Image_StoreAlphaBlock( blkaddr, alphause[1], alphause[0], alphaenc1 );
else if( alphablockerror2 <= alphablockerror3 )
Image_StoreAlphaBlock( blkaddr, alphabase[0], alphabase[1], alphaenc2 );
else Image_StoreAlphaBlock( blkaddr, (byte)alphatest[0], (byte)alphatest[1], alphaenc3 );
}
static void Image_ExtractColors( byte srcpixels[4][4][4], const byte *srcaddr, int srcRowStride, int numxpixels, int numypixels, int comps )
{
const byte *curaddr;
byte i, j, c;
for( j = 0; j < numypixels; j++ )
{
curaddr = srcaddr + j * srcRowStride * comps;
for( i = 0; i < numxpixels; i++ )
{
for( c = 0; c < comps; c++ )
srcpixels[j][i][c] = *curaddr++ / (CHAN_MAX / 255);
}
}
}
/*
====================
Image_DXTReadColors
read colors from dxt image
====================
*/
void Image_DXTReadColors( const byte* data, color32* out )
{
byte r0, g0, b0, r1, g1, b1;
b0 = data[0] & 0x1F;
g0 = ((data[0] & 0xE0) >> 5) | ((data[1] & 0x7) << 3);
r0 = (data[1] & 0xF8) >> 3;
b1 = data[2] & 0x1F;
g1 = ((data[2] & 0xE0) >> 5) | ((data[3] & 0x7) << 3);
r1 = (data[3] & 0xF8) >> 3;
out[0].r = r0<<3;
out[0].g = g0<<2;
out[0].b = b0<<3;
out[1].r = r1<<3;
out[1].g = g1<<2;
out[1].b = b1<<3;
}
/*
====================
Image_DXTReadColor
read one color from dxt image
====================
*/
void Image_DXTReadColor( word data, color32* out )
{
byte r, g, b;
b = data & 0x1f;
g = (data & 0x7E0) >>5;
r = (data & 0xF800)>>11;
out->r = r << 3;
out->g = g << 2;
out->b = b << 3;
}
void Image_CorrectPreMult( uint *data, int datasize )
{
int i;
for( i = 0; i < datasize; i += 4 )
{
if( data[i+3] != 0 ) // don't divide by 0.
{
data[i+0] = (byte)(((uint)data[i+0]<<8) / data[i+3]);
data[i+1] = (byte)(((uint)data[i+1]<<8) / data[i+3]);
data[i+2] = (byte)(((uint)data[i+2]<<8) / data[i+3]);
}
}
}
/*
====================
Image_GetBitsFromMask
====================
*/
void Image_GetBitsFromMask( uint Mask, uint *ShiftLeft, uint *ShiftRight )
{
uint Temp, i;
if( Mask == 0 )
{
*ShiftLeft = *ShiftRight = 0;
return;
}
Temp = Mask;
for (i = 0; i < 32; i++, Temp >>= 1)
{
if (Temp & 1) break;
}
*ShiftRight = i;
// Temp is preserved, so use it again:
for (i = 0; i < 8; i++, Temp >>= 1)
{
if(!(Temp & 1)) break;
}
*ShiftLeft = 8 - i;
}
qboolean Image_DXTWriteHeader( vfile_t *f, rgbdata_t *pix )
{
uint dwFourCC = 0, dwFlags1 = 0, dwFlags2 = 0, dwCaps1 = 0, dwCaps2 = 0;
uint dwLinearSize, dwBlockSize, dwSize = 124, dwSize2 = sizeof( dds_pixf_t );
uint dwRBitMask = 0, dwGBitMask = 0, dwBBitMask = 0, dwABitMask = 0;
uint dwWidth, dwHeight, dwDepth, dwMipCount;
uint dwIdent = DDSHEADER;
uint dwRGBBitCount = 0;
if( !pix || !pix->buffer )
return false;
// setup flags
dwFlags1 |= DDS_LINEARSIZE|DDS_WIDTH|DDS_HEIGHT|DDS_CAPS|DDS_PIXELFORMAT|DDS_PITCH;
dwFlags2 |= DDS_FOURCC;
dwMipCount = pix->numMips;
if( dwMipCount > 1 ) dwFlags1 |= DDS_MIPMAPCOUNT;
if( pix->depth > 1 ) dwFlags1 |= DDS_DEPTH;
switch( pix->type )
{
case PF_DXT1:
dwFourCC = TYPE_DXT1;
break;
case PF_DXT2:
dwFourCC = TYPE_DXT2;
break;
case PF_DXT3:
dwFourCC = TYPE_DXT3;
break;
case PF_DXT4:
dwFourCC = TYPE_DXT4;
break;
case PF_DXT5:
dwFourCC = TYPE_DXT5;
break;
case PF_ABGR_64:
dwFourCC = TYPE_$;
break;
case PF_R_16F:
dwFourCC = TYPE_o;
break;
case PF_GR_32F:
dwFourCC = TYPE_p;
break;
case PF_ABGR_64F:
dwFourCC = TYPE_q;
break;
case PF_R_32F:
dwFourCC = TYPE_r;
break;
case PF_GR_64F:
dwFourCC = TYPE_s;
break;
case PF_ABGR_128F:
dwFourCC = TYPE_t;
break;
default:
dwFlags2 &= ~DDS_FOURCC;
break;
}
// continue choosing format
if(!(dwFlags2 & DDS_FOURCC ))
{
switch( pix->type )
{
case PF_LUMINANCE:
dwABitMask = 0x000000FF;
dwFlags2 |= DDS_LUMINANCE;
dwRGBBitCount = 8;
break;
case PF_UV_16:
dwRBitMask = 0x000000FF;
dwGBitMask = 0x0000FF00;
dwFlags2 |= DDS_DUDV;
dwRGBBitCount = 16;
break;
case PF_UV_32:
dwRBitMask = 0x0000FFFF;
dwGBitMask = 0xFFFF0000;
dwFlags2 |= DDS_DUDV;
dwRGBBitCount = 32;
break;
case PF_LUMINANCE_ALPHA:
dwRBitMask = 0x000000FF;
dwABitMask = 0xFF000000;
dwFlags2 |= DDS_LUMINANCE;
dwFlags2 |= DDS_ALPHAPIXELS;
dwRGBBitCount = 16;
break;
case PF_BGR_24:
dwRBitMask = 0x000000FF;
dwGBitMask = 0x0000FF00;
dwBBitMask = 0x00FF0000;
dwFlags2 |= DDS_RGB;
dwRGBBitCount = 24;
break;
case PF_RGB_24:
dwRBitMask = 0x00FF0000;
dwGBitMask = 0x0000FF00;
dwBBitMask = 0x000000FF;
dwFlags2 |= DDS_RGB;
dwRGBBitCount = 24;
break;
case PF_BGRA_32:
dwRBitMask = 0x000000FF;
dwGBitMask = 0x0000FF00;
dwBBitMask = 0x00FF0000;
dwABitMask = 0xFF000000;
dwFlags2 |= DDS_RGBA;
dwRGBBitCount = 32;
break;
case PF_RGBA_32:
dwRBitMask = 0x00FF0000;
dwGBitMask = 0x0000FF00;
dwBBitMask = 0x000000FF;
dwABitMask = 0xFF000000;
dwFlags2 |= DDS_RGBA;
dwRGBBitCount = 32;
break;
default:
MsgDev( D_ERROR, "Image_DXTWriteHeader: unsupported type %s\n", PFDesc[pix->type].name );
return false;
}
}
dwWidth = pix->width;
dwHeight = pix->height;
VFS_Write( f, &dwIdent, sizeof(uint));
VFS_Write( f, &dwSize, sizeof(uint));
VFS_Write( f, &dwFlags1, sizeof(uint));
VFS_Write( f, &dwHeight, sizeof(uint));
VFS_Write( f, &dwWidth, sizeof(uint));
dwBlockSize = PFDesc[pix->type].block;
dwLinearSize = Image_DXTGetLinearSize( pix->type, pix->width, pix->height, 1, pix->bitsCount / 8 );
VFS_Write( f, &dwLinearSize, sizeof( uint ));
if( pix->depth > 1 )
{
dwDepth = pix->depth;
VFS_Write( f, &dwDepth, sizeof( uint ));
dwCaps2 |= DDS_VOLUME;
}
else VFS_Write( f, 0, sizeof( uint ));
VFS_Write( f, &dwMipCount, sizeof( uint ));
VFS_Write( f, 0, sizeof(uint));
VFS_Write( f, 0, sizeof(uint) * 10 ); // reserved fields
VFS_Write( f, &dwSize2, sizeof( uint ));
VFS_Write( f, &dwFlags2, sizeof( uint ));
VFS_Write( f, &dwFourCC, sizeof( uint ));
VFS_Write( f, &dwRGBBitCount, sizeof( uint ));
VFS_Write( f, &dwRBitMask, sizeof( uint ));
VFS_Write( f, &dwGBitMask, sizeof( uint ));
VFS_Write( f, &dwBBitMask, sizeof( uint ));
VFS_Write( f, &dwABitMask, sizeof( uint ));
dwCaps1 |= DDS_TEXTURE;
if( pix->numMips > 1 ) dwCaps1 |= DDS_MIPMAP|DDS_COMPLEX;
if( pix->flags & IMAGE_CUBEMAP )
{
dwCaps1 |= DDS_COMPLEX;
dwCaps2 |= DDS_CUBEMAP;
dwCaps2 |= DDS_CUBEMAP_ALL_SIDES;
}
VFS_Write( f, &dwCaps1, sizeof( uint ));
VFS_Write( f, &dwCaps2, sizeof( uint ));
VFS_Write( f, 0, sizeof( uint ) * 3 ); // other caps and TextureStage
return true;
}
size_t Image_DXTGetLinearSize( int type, int width, int height, int depth, int rgbcount )
{
size_t BlockSize = 0;
int block, bpp;
// right calcualte blocksize
block = PFDesc[type].block;
bpp = PFDesc[type].bpp;
if( block == 0 ) BlockSize = width * height * bpp;
else if( block > 0 ) BlockSize = ((width + 3)/4) * ((height + 3)/4) * depth * block;
else if( block < 0 && rgbcount > 0 ) BlockSize = width * height * depth * rgbcount;
else BlockSize = width * height * abs( block );
return BlockSize;
}
size_t Image_CompressDXT( vfile_t *f, int saveformat, rgbdata_t *pix )
{
byte srcpixels[4][4][4];
int numxpixels, numypixels;
int i, j, width, height;
const byte *srcaddr;
byte *blkaddr;
size_t dst_size;
int srccomps;
if( !pix || !pix->buffer )
return 0;
width = pix->width;
height = pix->height;
dst_size = Image_DXTGetLinearSize( saveformat, width, height, 1, 0 );
srccomps = PFDesc[pix->type].bpp;
blkaddr = image.tempbuffer = Mem_Realloc( Sys.imagepool, image.tempbuffer, dst_size ); // alloc dst image size
switch( saveformat )
{
case PF_DXT1:
for( j = 0; j < height; j += 4 )
{
if( height > j + 3 ) numypixels = 4;
else numypixels = height - j;
srcaddr = pix->buffer + j * width * srccomps;
for( i = 0; i < width; i += 4 )
{
if( width > i + 3 ) numxpixels = 4;
else numxpixels = width - i;
Image_ExtractColors( srcpixels, srcaddr, width, numxpixels, numypixels, srccomps );
Image_EncodeColorBlock( blkaddr, srcpixels, numxpixels, numypixels, saveformat, pix->flags );
srcaddr += srccomps * numxpixels;
blkaddr += 8;
}
}
break;
case PF_DXT3:
for( j = 0; j < height; j += 4 )
{
if( height > j + 3 ) numypixels = 4;
else numypixels = height - j;
srcaddr = pix->buffer + j * width * srccomps;
for( i = 0; i < width; i += 4 )
{
if( width > i + 3 ) numxpixels = 4;
else numxpixels = width - i;
Image_ExtractColors( srcpixels, srcaddr, width, numxpixels, numypixels, srccomps );
*blkaddr++ = (srcpixels[0][0][3] >> 4) | (srcpixels[0][1][3] & 0xf0);
*blkaddr++ = (srcpixels[0][2][3] >> 4) | (srcpixels[0][3][3] & 0xf0);
*blkaddr++ = (srcpixels[1][0][3] >> 4) | (srcpixels[1][1][3] & 0xf0);
*blkaddr++ = (srcpixels[1][2][3] >> 4) | (srcpixels[1][3][3] & 0xf0);
*blkaddr++ = (srcpixels[2][0][3] >> 4) | (srcpixels[2][1][3] & 0xf0);
*blkaddr++ = (srcpixels[2][2][3] >> 4) | (srcpixels[2][3][3] & 0xf0);
*blkaddr++ = (srcpixels[3][0][3] >> 4) | (srcpixels[3][1][3] & 0xf0);
*blkaddr++ = (srcpixels[3][2][3] >> 4) | (srcpixels[3][3][3] & 0xf0);
Image_EncodeColorBlock( blkaddr, srcpixels, numxpixels, numypixels, saveformat, pix->flags );
srcaddr += srccomps * numxpixels;
blkaddr += 8;
}
}
break;
case PF_DXT5:
for( j = 0; j < height; j += 4 )
{
if( height > j + 3 ) numypixels = 4;
else numypixels = height - j;
srcaddr = pix->buffer + j * width * srccomps;
for( i = 0; i < width; i += 4 )
{
if( width > i + 3 ) numxpixels = 4;
else numxpixels = width - i;
Image_ExtractColors( srcpixels, srcaddr, width, numxpixels, numypixels, srccomps );
Image_EncodeDXT5alpha( blkaddr, srcpixels, numxpixels, numypixels );
Image_EncodeColorBlock( blkaddr + 8, srcpixels, numxpixels, numypixels, saveformat, pix->flags );
srcaddr += srccomps * numxpixels;
blkaddr += 16;
}
}
break;
default:
MsgDev( D_ERROR, "Image_CompressDXT: bad destination format %s\n", PFDesc[saveformat].name );
return 0;
}
dst_size = VFS_Write( f, image.tempbuffer, dst_size );
return dst_size;
}
void Image_CompressDDS( vfile_t *f, byte *buffer )
{
#if 0
int i, size = 0;
int w = image.curwidth;
int h = image.curheight;
int d = image.curdepth;
for( i = 0; i < image.cur_mips; i++, buffer += size )
{
Image_SetPixelFormat( w, h, d );
size = image.SizeOfFile;
if(!image.decompress( target, i, image.type, w, h, size, buffer ))
break; // there were errors
w = (w+1)>>1, h = (h+1)>>1, d = (d+1)>>1; // calc size of next mip
}
#endif
}
qboolean Image_DXTWriteImage( vfile_t *f, rgbdata_t *pix )
{
byte *in, *out, *buffer;
int i, bpp = PFDesc[pix->type].bpp;
// just write input buffer as valid
switch( pix->type )
{
case PF_RGB_24:
case PF_RGBA_32:
in = pix->buffer; // filp to BGRA
buffer = image.tempbuffer = Mem_Realloc( Sys.imagepool, image.tempbuffer, pix->size );
for( i = 0, out = image.tempbuffer; i < pix->size; i += bpp, in += bpp )
{
*out++ = in[2];
*out++ = in[1];
*out++ = in[0];
if( pix->type == PF_RGBA_32 )
*out++ = in[3];
}
break;
case PF_BGR_24:
case PF_BGRA_32:
case PF_LUMINANCE:
case PF_LUMINANCE_ALPHA:
case PF_UV_16:
case PF_UV_32:
case PF_DXT1:
case PF_DXT3:
case PF_DXT5:
case PF_ABGR_64F:
buffer = pix->buffer;
break;
default: return false;
}
VFS_Write( f, buffer, pix->size );
return true;
}
void Image_DXTGetPixelFormat( dds_t *hdr )
{
uint bits = hdr->dsPixelFormat.dwRGBBitCount;
// All volume textures I've seem so far didn't have the DDS_COMPLEX flag set,
// even though this is normally required. But because noone does set it,
// also read images without it (TODO: check file size for 3d texture?)
if (!(hdr->dsCaps.dwCaps2 & DDS_VOLUME)) hdr->dwDepth = 1;
image.flags |= IMAGE_HAS_COLOR; // predict state
if( hdr->dsPixelFormat.dwFlags & DDS_ALPHA )
image.flags |= IMAGE_HAS_ALPHA;
if( hdr->dsPixelFormat.dwFlags & DDS_LUMINANCE )
image.flags &= ~IMAGE_HAS_COLOR;
if( hdr->dsPixelFormat.dwFlags & DDS_FOURCC )
{
switch( hdr->dsPixelFormat.dwFourCC )
{
case TYPE_DXT1: image.type = PF_DXT1; break;
case TYPE_DXT2: image.type = PF_DXT2; break;
case TYPE_DXT3: image.type = PF_DXT3; break;
case TYPE_DXT4: image.type = PF_DXT4; break;
case TYPE_DXT5: image.type = PF_DXT5; break;
case TYPE_ATI1: image.type = PF_ATI1N; break;
case TYPE_ATI2: image.type = PF_ATI2N; break;
case TYPE_RXGB: image.type = PF_RXGB; break;
case TYPE_$: image.type = PF_ABGR_64; break;
case TYPE_o: image.type = PF_R_16F; break;
case TYPE_p: image.type = PF_GR_32F; break;
case TYPE_q: image.type = PF_ABGR_64F; break;
case TYPE_r: image.type = PF_R_32F;
case TYPE_s: image.type = PF_GR_64F;
case TYPE_t: image.type = PF_ABGR_128F; break;
default: image.type = PF_UNKNOWN; break;
}
}
else
{
// this dds texture isn't compressed so write out ARGB or luminance format
if( hdr->dsPixelFormat.dwFlags & DDS_DUDV )
{
if( hdr->dsPixelFormat.dwRGBBitCount == 16 )
image.type = PF_UV_16;
else if( hdr->dsPixelFormat.dwRGBBitCount == 32 )
image.type = PF_UV_32;
}
else if( hdr->dsPixelFormat.dwFlags & DDS_LUMINANCE )
{
if( hdr->dsPixelFormat.dwFlags & DDS_ALPHAPIXELS )
image.type = PF_LUMINANCE_ALPHA;
else if( hdr->dsPixelFormat.dwRGBBitCount == 16 && hdr->dsPixelFormat.dwRBitMask == 0xFFFF )
image.type = PF_LUMINANCE_16;
else image.type = PF_LUMINANCE;
}
else
{
if( bits == 32 ) image.type = PF_ABGR_64;
else if( bits == 24 ) image.type = PF_BGR_24;
else image.type = PF_ARGB_32;
}
}
// setup additional flags
if( hdr->dsCaps.dwCaps1 & DDS_COMPLEX && hdr->dsCaps.dwCaps2 & DDS_CUBEMAP )
image.flags |= IMAGE_CUBEMAP;
if( hdr->dsPixelFormat.dwFlags & DDS_ALPHAPIXELS )
image.flags |= IMAGE_HAS_ALPHA;
if( hdr->dwFlags & DDS_MIPMAPCOUNT )
image.num_mips = hdr->dwMipMapCount;
else image.num_mips = 1;
if( image.type == PF_ARGB_32 || image.type == PF_LUMINANCE || image.type == PF_LUMINANCE_16 || image.type == PF_LUMINANCE_ALPHA )
{
// store RGBA mask into palette space
byte *tmp = image.palette = Mem_Alloc( Sys.imagepool, sizeof(uint) * 4 );
Mem_Copy( tmp, &hdr->dsPixelFormat.dwRBitMask, sizeof( uint )); tmp += 4;
Mem_Copy( tmp, &hdr->dsPixelFormat.dwGBitMask, sizeof( uint )); tmp += 4;
Mem_Copy( tmp, &hdr->dsPixelFormat.dwBBitMask, sizeof( uint )); tmp += 4;
Mem_Copy( tmp, &hdr->dsPixelFormat.dwABitMask, sizeof( uint )); tmp += 4;
}
}
void Image_DXTAdjustVolume( dds_t *hdr )
{
uint bits;
if( hdr->dwDepth <= 1 ) return;
bits = hdr->dsPixelFormat.dwRGBBitCount / 8;
hdr->dwFlags |= DDS_LINEARSIZE;
hdr->dwLinearSize = Image_DXTGetLinearSize( image.type, hdr->dwWidth, hdr->dwHeight, hdr->dwDepth, bits );
}
uint Image_DXTCalcMipmapSize( dds_t *hdr )
{
uint buffsize = 0;
int w = hdr->dwWidth;
int h = hdr->dwHeight;
int d = hdr->dwDepth;
int i, mipsize = 0;
int bits = hdr->dsPixelFormat.dwRGBBitCount / 8;
// now correct buffer size
for( i = 0; i < image.num_mips; i++, buffsize += mipsize )
{
mipsize = Image_DXTGetLinearSize( image.type, w, h, d, bits );
w = (w+1)>>1, h = (h+1)>>1, d = (d+1)>>1;
}
return buffsize;
}
uint Image_DXTCalcSize( const char *name, dds_t *hdr, size_t filesize )
{
size_t buffsize = 0;
int w = image.width;
int h = image.height;
int d = image.depth;
if( hdr->dsCaps.dwCaps2 & DDS_CUBEMAP )
{
// cubemap w*h always match for all sides
buffsize = Image_DXTCalcMipmapSize( hdr ) * 6;
}
else if( hdr->dwFlags & DDS_MIPMAPCOUNT )
{
// if mipcount > 1
buffsize = Image_DXTCalcMipmapSize( hdr );
}
else if( hdr->dwFlags & (DDS_LINEARSIZE|DDS_PITCH))
{
// just in case (no need, really)
buffsize = hdr->dwLinearSize;
}
else
{
// pretty solution for microsoft bug
buffsize = Image_DXTCalcMipmapSize( hdr );
}
if( filesize != buffsize ) // main check
{
MsgDev( D_WARN, "LoadDDS: (%s) probably corrupted(%i should be %i)\n", name, buffsize, filesize );
return false;
}
return buffsize;
}
void Image_AddRGBAToPack( uint target, int level, uint imageSize, const void* data )
{
// NOTE: just update bufer without checking for a type
image.rgba = Mem_Realloc( Sys.imagepool, image.rgba, image.ptr + imageSize );
Mem_Copy( image.rgba + image.ptr, data, imageSize ); // add mipmap or cubemapside
image.size += imageSize; // update image size
image.ptr += imageSize;
image.num_mips++;
}
/*
=============
Image_SetPixelFormat
update pixel format for miplevel or layer
=============
*/
void Image_SetPixelFormat( int width, int height, int depth )
{
image.bpp = PFDesc[image.type].bpp;
image.bpc = PFDesc[image.type].bpc;
image.curdepth = depth;
image.curwidth = width;
image.curheight = height;
image.bps = image.curwidth * image.bpp * image.bpc;
image.SizeOfPlane = image.bps * image.curheight;
image.SizeOfData = image.SizeOfPlane * image.curdepth;
// NOTE: size of current miplevel or cubemap side, not total (filesize - sizeof( header ))
image.SizeOfFile = Image_DXTGetLinearSize( image.type, width, height, depth, image.bits_count / 8 );
}
qboolean Image_DecompressFloat( uint target, int level, int intformat, uint width, uint height, uint imageSize, const void* data )
{
uint floatformat = 0;
uint i, size = 0;
uint pixformat = PFDesc[intformat].format;
word *src = (word *)data;
uint *dest;
if( !src ) return false;
switch( pixformat )
{
case PF_R_16F:
case PF_GR_32F:
case PF_ABGR_64F:
size = width * height * image.curdepth * image.bpp;
image.tempbuffer = Mem_Realloc( Sys.imagepool, image.tempbuffer, size );
dest = (uint *)image.tempbuffer;
for( i = 0; i < size; i++, dest++, src++ )
*dest = Image_ShortToFloat( *src );
break;
case PF_R_32F:
case PF_GR_64F:
case PF_ABGR_128F:
size = image.SizeOfData;
image.tempbuffer = Mem_Realloc( Sys.imagepool, image.tempbuffer, size );
dest = (uint *)image.tempbuffer;
Mem_Copy( dest, data, image.SizeOfData );
break;
default: return false;
}
Image_AddRGBAToPack( target, level, size, dest );
return true;
}
qboolean Image_DecompressATI( uint target, int level, int intformat, uint width, uint height, uint imageSize, const void* data )
{
int x, y, z, i, j, k, t1, t2, size;
byte Colours[8], XColours[8], YColours[8];
uint bitmask, bitmask2, CurrOffset, Offset = 0;
byte *fin, *fin2, *fout;
qboolean has_alpha = false;
if( !data ) return false;
fin = (byte *)data;
size = width * height * image.curdepth * 4;
image.tempbuffer = Mem_Realloc( Sys.imagepool, image.tempbuffer, size );
fout = image.tempbuffer;
switch( PFDesc[intformat].format )
{
case PF_ATI1N:
image.flags &= ~IMAGE_HAS_COLOR;
image.flags |= IMAGE_HAS_ALPHA;
for( z = 0; z < image.curdepth; z++ )
{
for( y = 0; y < height; y += 4 )
{
for( x = 0; x < width; x += 4 )
{
// read palette
t1 = Colours[0] = fin[0];
t2 = Colours[1] = fin[1];
fin += 2;
if( t1 > t2 )
{
for( i = 2; i < 8; i++ )
Colours[i] = t1 + ((t2 - t1) * (i - 1)) / 7;
}
else
{
for( i = 2; i < 6; i++ )
Colours[i] = t1 + ((t2 - t1) * (i - 1)) / 5;
Colours[6] = 0;
Colours[7] = 0xFF;
}
// decompress pixel data
CurrOffset = Offset;
for( k = 0; k < 4; k += 2 )
{
// first three bytes
bitmask = ((uint)(fin[0]) << 0) | ((uint)(fin[1]) << 8) | ((uint)(fin[2]) << 16);
for( j = 0; j < 2; j++ )
{
// only put pixels out < height
if((y + k + j) < height )
{
for( i = 0; i < 4; i++ )
{
// only put pixels out < width
if((x + i) < width )
{
t1 = CurrOffset + (x + i);
fout[t1*4+0] = Colours[bitmask & 0x07];
fout[t1*4+1] = Colours[bitmask & 0x07];
fout[t1*4+2] = Colours[bitmask & 0x07];
fout[t1*4+3] = Colours[bitmask & 0x07];
}
bitmask >>= 3;
}
CurrOffset += image.bps;
}
}
fin += 3;
}
}
Offset += image.bps * 4;
}
}
break;
case PF_ATI2N:
image.flags |= IMAGE_HAS_COLOR;
image.flags &= ~IMAGE_HAS_ALPHA;
for( z = 0; z < image.curdepth; z++ )
{
for( y = 0; y < height; y += 4 )
{
for( x = 0; x < width; x += 4 )
{
fin2 = fin + 8;
// read Y palette
t1 = YColours[0] = fin[0];
t2 = YColours[1] = fin[1];
fin += 2;
if( t1 > t2 )
{
for( i = 2; i < 8; i++ )
YColours[i] = t1 + ((t2 - t1) * (i - 1)) / 7;
}
else
{
for( i = 2; i < 6; i++ )
YColours[i] = t1 + ((t2 - t1) * (i - 1)) / 5;
YColours[6] = 0;
YColours[7] = 0xFF;
}
// read X palette
t1 = XColours[0] = fin2[0];
t2 = XColours[1] = fin2[1];
fin2 += 2;
if( t1 > t2 )
{
for( i = 2; i < 8; ++i )
XColours[i] = t1 + ((t2 - t1) * (i - 1)) / 7;
}
else
{
for( i = 2; i < 6; ++i )
XColours[i] = t1 + ((t2 - t1) * (i - 1)) / 5;
XColours[6] = 0;
XColours[7] = 0xFF;
}
// decompress pixel data
CurrOffset = Offset;
for( k = 0; k < 4; k += 2 )
{
// first three bytes
bitmask = ((uint)(fin[0]) << 0) | ((uint)(fin[1]) << 8) | ((uint)(fin[2]) << 16);
bitmask2 = ((uint)(fin2[0]) << 0) | ((uint)(fin2[1]) << 8) | ((uint)(fin2[2]) << 16);
for( j = 0; j < 2; j++ )
{
// only put pixels out < height
if((y + k + j) < height )
{
for( i = 0; i < 4; i++ )
{
// only put pixels out < width
if((x + i) < width )
{
int t, tx, ty;
t1 = CurrOffset + (x + i) * 4;
fout[t1+1] = ty = YColours[bitmask & 0x07];
fout[t1+0] = tx = XColours[bitmask2 & 0x07];
// calculate b (z) component ((r/255)^2 + (g/255)^2 + (b/255)^2 = 1
t = 127 * 128 - (tx - 127) * (tx - 128) - (ty - 127) * (ty - 128);
if( t > 0 ) fout[t1+2] = (byte)(com.sqrt( t ) + 128);
else fout[t1+2] = 0x7F;
fout[t1+3] = 0xFF;
}
bitmask >>= 3;
bitmask2 >>= 3;
}
CurrOffset += image.bps;
}
}
fin += 3;
fin2 += 3;
}
// skip bytes that were read via fin2
fin += 8;
}
Offset += image.bps * 4;
}
}
break;
default: return false;
}
// make some post operations
Image_AddRGBAToPack( target, level, size, fout );
return true;
}
qboolean Image_DecompressDXT( uint target, int level, int intformat, uint width, uint height, uint imageSize, const void* data )
{
color32 colours[4], *col;
color16 *color_0, *color_1;
word sAlpha, sColor0, sColor1;
uint bits, bitmask, Offset, size;
byte alphas[8], *alpha, *alphamask;
int w, h, x, y, z, i, j, k, Select;
qboolean has_alpha = false;
byte *fin, *fout;
if( !data ) return false;
fin = (byte *)data;
w = width;
h = height;
size = width * height * image.curdepth * 4;
image.tempbuffer = Mem_Realloc( Sys.imagepool, image.tempbuffer, size );
fout = image.tempbuffer;
switch( PFDesc[intformat].format )
{
case PF_DXT1:
colours[0].a = 0xFF;
colours[1].a = 0xFF;
colours[2].a = 0xFF;
for( z = 0; z < image.curdepth; z++ )
{
for( y = 0; y < h; y += 4 )
{
for( x = 0; x < w; x += 4 )
{
sColor0 = *((word*)fin);
sColor0 = sColor0;
sColor1 = *((word*)(fin + 2));
sColor1 = sColor1;
Image_DXTReadColor(sColor0, colours);
Image_DXTReadColor(sColor1, colours + 1);
bitmask = ((uint*)fin)[1];
fin += 8;
if (sColor0 > sColor1)
{
// four-color block: derive the other two colors.
// 00 = color_0, 01 = color_1, 10 = color_2, 11 = color_3
// These 2-bit codes correspond to the 2-bit fields
// stored in the 64-bit block.
colours[2].b = (2 * colours[0].b + colours[1].b + 1) / 3;
colours[2].g = (2 * colours[0].g + colours[1].g + 1) / 3;
colours[2].r = (2 * colours[0].r + colours[1].r + 1) / 3;
colours[3].b = (colours[0].b + 2 * colours[1].b + 1) / 3;
colours[3].g = (colours[0].g + 2 * colours[1].g + 1) / 3;
colours[3].r = (colours[0].r + 2 * colours[1].r + 1) / 3;
colours[3].a = 0xFF;
}
else
{
// three-color block: derive the other color.
// 00 = color_0, 01 = color_1, 10 = color_2,
// 11 = transparent.
// These 2-bit codes correspond to the 2-bit fields
// stored in the 64-bit block.
colours[2].b = (colours[0].b + colours[1].b) / 2;
colours[2].g = (colours[0].g + colours[1].g) / 2;
colours[2].r = (colours[0].r + colours[1].r) / 2;
colours[3].b = (colours[0].b + 2 * colours[1].b + 1) / 3;
colours[3].g = (colours[0].g + 2 * colours[1].g + 1) / 3;
colours[3].r = (colours[0].r + 2 * colours[1].r + 1) / 3;
colours[3].a = 0x00;
}
for( j = 0, k = 0; j < 4; j++ )
{
for( i = 0; i < 4; i++, k++ )
{
Select = (bitmask & (0x03 << k*2)) >> k*2;
col = &colours[Select];
if (((x + i) < w) && ((y + j) < h))
{
uint ofs = z * image.SizeOfPlane + (y + j) * image.bps + (x + i) * image.bpp;
fout[ofs+0] = col->r;
fout[ofs+1] = col->g;
fout[ofs+2] = col->b;
fout[ofs+3] = col->a;
if( col->a == 0 ) has_alpha = true;
}
}
}
}
}
}
break;
case PF_DXT2:
image.flags |= IMAGE_PREMULT;
// intentional fallthrough
case PF_DXT3:
for( z = 0; z < image.curdepth; z++ )
{
for( y = 0; y < h; y += 4 )
{
for( x = 0; x < w; x += 4 )
{
alpha = fin;
fin += 8;
Image_DXTReadColors( fin, colours );
bitmask = ((uint*)fin)[1];
fin += 8;
// four-color block: derive the other two colors.
// 00 = color_0, 01 = color_1, 10 = color_2, 11 = color_3
// These 2-bit codes correspond to the 2-bit fields
// stored in the 64-bit block.
colours[2].b = (2 * colours[0].b + colours[1].b + 1) / 3;
colours[2].g = (2 * colours[0].g + colours[1].g + 1) / 3;
colours[2].r = (2 * colours[0].r + colours[1].r + 1) / 3;
colours[3].b = (colours[0].b + 2 * colours[1].b + 1) / 3;
colours[3].g = (colours[0].g + 2 * colours[1].g + 1) / 3;
colours[3].r = (colours[0].r + 2 * colours[1].r + 1) / 3;
k = 0;
for( j = 0; j < 4; j++ )
{
for( i = 0; i < 4; i++, k++ )
{
Select = (bitmask & (0x03 << k*2)) >> k*2;
col = &colours[Select];
if(((x + i) < w) && ((y + j) < h))
{
Offset = z * image.SizeOfPlane + (y + j) * image.bps + (x + i) * image.bpp;
fout[Offset + 0] = col->r;
fout[Offset + 1] = col->g;
fout[Offset + 2] = col->b;
}
}
}
for( j = 0; j < 4; j++ )
{
sAlpha = alpha[2*j] + 256*alpha[2*j+1];
for( i = 0; i < 4; i++ )
{
if(((x + i) < w) && ((y + j) < h))
{
Offset = z * image.SizeOfPlane + (y + j) * image.bps + (x + i) * image.bpp + 3;
fout[Offset] = sAlpha & 0x0F;
fout[Offset] = fout[Offset] | (fout[Offset]<<4);
if(sAlpha == 0) has_alpha = true;
}
sAlpha >>= 4;
}
}
}
}
}
break;
case PF_DXT4:
image.flags |= IMAGE_PREMULT;
// intentional fallthrough
case PF_DXT5:
for( z = 0; z < image.curdepth; z++ )
{
for( y = 0; y < h; y += 4 )
{
for( x = 0; x < w; x += 4 )
{
if( y >= h || x >= w ) break;
alphas[0] = fin[0];
alphas[1] = fin[1];
alphamask = fin + 2;
fin += 8;
Image_DXTReadColors(fin, colours);
bitmask = ((uint*)fin)[1];
fin += 8;
// four-color block: derive the other two colors.
// 00 = color_0, 01 = color_1, 10 = color_2, 11 = color_3
// these 2-bit codes correspond to the 2-bit fields
// stored in the 64-bit block.
colours[2].b = (2 * colours[0].b + colours[1].b + 1) / 3;
colours[2].g = (2 * colours[0].g + colours[1].g + 1) / 3;
colours[2].r = (2 * colours[0].r + colours[1].r + 1) / 3;
colours[3].b = (colours[0].b + 2 * colours[1].b + 1) / 3;
colours[3].g = (colours[0].g + 2 * colours[1].g + 1) / 3;
colours[3].r = (colours[0].r + 2 * colours[1].r + 1) / 3;
k = 0;
for( j = 0; j < 4; j++ )
{
for( i = 0; i < 4; i++, k++ )
{
Select = (bitmask & (0x03 << k*2)) >> k*2;
col = &colours[Select];
// only put pixels out < width or height
if(((x + i) < w) && ((y + j) < h))
{
Offset = z * image.SizeOfPlane + (y + j) * image.bps + (x + i) * image.bpp;
fout[Offset+0] = col->r;
fout[Offset+1] = col->g;
fout[Offset+2] = col->b;
}
}
}
// 8-alpha or 6-alpha block?
if( alphas[0] > alphas[1] )
{
// 8-alpha block: derive the other six alphas.
// bit code 000 = alpha_0, 001 = alpha_1, others are interpolated.
alphas[2] = (6 * alphas[0] + 1 * alphas[1] + 3) / 7; // bit code 010
alphas[3] = (5 * alphas[0] + 2 * alphas[1] + 3) / 7; // bit code 011
alphas[4] = (4 * alphas[0] + 3 * alphas[1] + 3) / 7; // bit code 100
alphas[5] = (3 * alphas[0] + 4 * alphas[1] + 3) / 7; // bit code 101
alphas[6] = (2 * alphas[0] + 5 * alphas[1] + 3) / 7; // bit code 110
alphas[7] = (1 * alphas[0] + 6 * alphas[1] + 3) / 7; // bit code 111
}
else
{
// 6-alpha block.
// bit code 000 = alpha_0, 001 = alpha_1, others are interpolated.
alphas[2] = (4 * alphas[0] + 1 * alphas[1] + 2) / 5; // bit code 010
alphas[3] = (3 * alphas[0] + 2 * alphas[1] + 2) / 5; // bit code 011
alphas[4] = (2 * alphas[0] + 3 * alphas[1] + 2) / 5; // bit code 100
alphas[5] = (1 * alphas[0] + 4 * alphas[1] + 2) / 5; // bit code 101
alphas[6] = 0x00; // bit code 110
alphas[7] = 0xFF; // bit code 111
}
// NOTE: Have to separate the next two loops,
// it operates on a 6-byte system.
// first three bytes
bits = (alphamask[0]) | (alphamask[1] << 8) | (alphamask[2] << 16);
for( j = 0; j < 2; j++ )
{
for( i = 0; i < 4; i++ )
{
// only put pixels out < width or height
if(((x + i) < w) && ((y + j) < h))
{
Offset = z * image.SizeOfPlane + (y + j) * image.bps + (x + i) * image.bpp + 3;
fout[Offset] = alphas[bits & 0x07];
}
bits >>= 3;
}
}
// last three bytes
bits = (alphamask[3]) | (alphamask[4] << 8) | (alphamask[5] << 16);
for( j = 2; j < 4; j++ )
{
for( i = 0; i < 4; i++ )
{
// only put pixels out < width or height
if (((x + i) < w) && ((y + j) < h))
{
Offset = z * image.SizeOfPlane + (y + j) * image.bps + (x + i) * image.bpp + 3;
fout[Offset] = alphas[bits & 0x07];
if( bits & 0x07 ) has_alpha = true;
}
bits >>= 3;
}
}
}
}
}
break;
case PF_RXGB:
for( z = 0; z < image.curdepth; z++ )
{
for( y = 0; y < h; y += 4 )
{
for( x = 0; x < w; x += 4 )
{
if( y >= h || x >= w ) break;
alphas[0] = fin[0];
alphas[1] = fin[1];
alphamask = fin + 2;
fin += 8;
color_0 = ((color16*)fin);
color_1 = ((color16*)(fin+2));
bitmask = ((uint*)fin)[1];
fin += 8;
colours[0].r = color_0->r << 3;
colours[0].g = color_0->g << 2;
colours[0].b = color_0->b << 3;
colours[0].a = 0xFF;
colours[1].r = color_1->r << 3;
colours[1].g = color_1->g << 2;
colours[1].b = color_1->b << 3;
colours[1].a = 0xFF;
// four-color block: derive the other two colors.
// 00 = color_0, 01 = color_1, 10 = color_2, 11 = color_3
// these 2-bit codes correspond to the 2-bit fields
// stored in the 64-bit block.
colours[2].b = (2 * colours[0].b + colours[1].b + 1) / 3;
colours[2].g = (2 * colours[0].g + colours[1].g + 1) / 3;
colours[2].r = (2 * colours[0].r + colours[1].r + 1) / 3;
colours[2].a = 0xFF;
colours[3].b = (colours[0].b + 2 * colours[1].b + 1) / 3;
colours[3].g = (colours[0].g + 2 * colours[1].g + 1) / 3;
colours[3].r = (colours[0].r + 2 * colours[1].r + 1) / 3;
colours[3].a = 0xFF;
k = 0;
for( j = 0; j < 4; j++ )
{
for( i = 0; i < 4; i++, k++ )
{
Select = (bitmask & (0x03 << k*2)) >> k*2;
col = &colours[Select];
// only put pixels out < width or height
if(((x + i) < w) && ((y + j) < h))
{
Offset = z * image.SizeOfPlane + (y + j) * image.bps + (x + i) * image.bpp;
fout[Offset + 0] = col->r;
fout[Offset + 1] = col->g;
fout[Offset + 2] = col->b;
}
}
}
// 8-alpha or 6-alpha block?
if (alphas[0] > alphas[1])
{
// 8-alpha block: derive the other six alphas.
// bit code 000 = alpha_0, 001 = alpha_1, others are interpolated.
alphas[2] = (6 * alphas[0] + 1 * alphas[1] + 3) / 7; // bit code 010
alphas[3] = (5 * alphas[0] + 2 * alphas[1] + 3) / 7; // bit code 011
alphas[4] = (4 * alphas[0] + 3 * alphas[1] + 3) / 7; // bit code 100
alphas[5] = (3 * alphas[0] + 4 * alphas[1] + 3) / 7; // bit code 101
alphas[6] = (2 * alphas[0] + 5 * alphas[1] + 3) / 7; // bit code 110
alphas[7] = (1 * alphas[0] + 6 * alphas[1] + 3) / 7; // bit code 111
}
else
{
// 6-alpha block.
// bit code 000 = alpha_0, 001 = alpha_1, others are interpolated.
alphas[2] = (4 * alphas[0] + 1 * alphas[1] + 2) / 5; // bit code 010
alphas[3] = (3 * alphas[0] + 2 * alphas[1] + 2) / 5; // bit code 011
alphas[4] = (2 * alphas[0] + 3 * alphas[1] + 2) / 5; // bit code 100
alphas[5] = (1 * alphas[0] + 4 * alphas[1] + 2) / 5; // bit code 101
alphas[6] = 0x00; // bit code 110
alphas[7] = 0xFF; // bit code 111
}
// NOTE: Have to separate the next two loops,
// it operates on a 6-byte system.
// first three bytes
bits = *((int*)alphamask);
for( j = 0; j < 2; j++ )
{
for( i = 0; i < 4; i++ )
{
// only put pixels out < width or height
if(((x + i) < w) && ((y + j) < h))
{
Offset = z * image.SizeOfPlane + (y + j) * image.bps + (x + i) * image.bpp;
fout[Offset] = alphas[bits & 0x07];
}
bits >>= 3;
}
}
// last three bytes
bits = *((int*)&alphamask[3]);
for( j = 2; j < 4; j++ )
{
for( i = 0; i < 4; i++ )
{
// only put pixels out < width or height
if(((x + i) < w) && ((y + j) < h))
{
Offset = z * image.SizeOfPlane + (y + j) * image.bps + (x + i) * image.bpp;
fout[Offset] = alphas[bits & 0x07];
}
bits >>= 3;
}
}
}
}
}
default: return false;
}
// make some post operations
if( has_alpha ) image.flags |= IMAGE_HAS_ALPHA;
if( image.flags & IMAGE_PREMULT )
{
Image_CorrectPreMult((uint *)fout, size );
image.flags &= ~IMAGE_PREMULT;
}
Image_AddRGBAToPack( target, level, size, fout );
return true;
}
qboolean Image_DecompressARGB( uint target, int level, int intformat, uint width, uint height, uint imageSize, const void* data )
{
uint ReadI = 0, TempBpp;
uint RedL, RedR, GreenL, GreenR, BlueL, BlueR, AlphaL, AlphaR;
uint r_bitmask, g_bitmask, b_bitmask, a_bitmask;
int i, w, h, size;
byte *fin, *fout;
if( !data ) return false;
w = width;
h = height;
fin = (byte *)data;
size = width * height * image.curdepth * 4;
image.tempbuffer = Mem_Realloc( Sys.imagepool, image.tempbuffer, size );
fout = image.tempbuffer;
if( PFDesc[intformat].format == PF_LUMINANCE_16 )
{
Mem_Copy( fout, data, image.SizeOfData);
}
else if( image.palette )
{
byte *pal = image.palette; //copy ptr
r_bitmask = *(long *)pal; pal += 4;
g_bitmask = *(long *)pal; pal += 4;
b_bitmask = *(long *)pal; pal += 4;
a_bitmask = *(long *)pal; pal += 4;
}
else return false; // rgba mask unset
Image_GetBitsFromMask( r_bitmask, &RedL, &RedR );
Image_GetBitsFromMask( g_bitmask, &GreenL, &GreenR );
Image_GetBitsFromMask( b_bitmask, &BlueL, &BlueR );
Image_GetBitsFromMask( a_bitmask, &AlphaL, &AlphaR );
TempBpp = image.bits_count / 8;
for( i = 0; i < image.SizeOfData; i += image.bpp )
{
// TODO: This is SLOOOW...
// but the old version crashed in release build under
// winxp (and xp is right to stop this code - I always
// wondered that it worked the old way at all)
if( image.SizeOfData - i < 4 )
{
// less than 4 byte to write?
if( TempBpp == 1 ) ReadI = *((byte*) fin );
else if( TempBpp == 2 ) ReadI = *(short *)fin;
else if( TempBpp == 3 ) ReadI = *(short *)fin;
}
else ReadI = *(long *)fin;
fin += TempBpp;
fout[i] = ((ReadI & r_bitmask)>> RedR) << RedL;
if( image.bpp >= 3 )
{
fout[i+1] = ((ReadI & g_bitmask) >> GreenR) << GreenL;
fout[i+2] = ((ReadI & b_bitmask) >> BlueR) << BlueL;
if( image.bpp == 4 )
{
fout[i+3] = ((ReadI & a_bitmask) >> AlphaR) << AlphaL;
if( AlphaL >= 7 ) fout[i+3] = fout[i+3] ? 0xFF : 0x00;
else if( AlphaL >= 4 ) fout[i+3] = fout[i+3]|(fout[i+3] >> 4);
}
}
else if( image.bpp == 2 )
{
fout[i+1] = ((ReadI & a_bitmask) >> AlphaR) << AlphaL;
if( AlphaL >= 7 ) fout[i+1] = fout[i+1] ? 0xFF : 0x00;
else if( AlphaL >= 4 ) fout[i+1] = fout[i+1]|(fout[i+3] >> 4);
}
}
Image_AddRGBAToPack( target, level, size, fout );
return true;
}
qboolean Image_DecompressPal8( uint target, int level, int intformat, uint width, uint height, uint imageSize, const void* data )
{
byte *fin, *fout;
int size;
if( !data ) return false;
fin = (byte *)data;
size = width * height * image.curdepth * 4;
image.tempbuffer = Mem_Realloc( Sys.imagepool, image.tempbuffer, size );
fout = image.tempbuffer;
switch( PFDesc[intformat].format )
{
case PF_INDEXED_24:
if( image.flags & IMAGE_HAS_ALPHA )
{
if( image.flags & IMAGE_COLORINDEX )
Image_GetPaletteLMP( image.palette, LUMP_DECAL );
else Image_GetPaletteLMP( image.palette, LUMP_TRANSPARENT );
}
else Image_GetPaletteLMP( image.palette, LUMP_NORMAL );
// intentional falltrough
case PF_INDEXED_32:
if( !image.d_currentpal ) image.d_currentpal = ( uint *)image.palette;
if( !Image_Copy8bitRGBA( fin, fout, width * height )) return false;
break;
}
Image_AddRGBAToPack( target, level, size, fout );
return true;
}
qboolean Image_DecompressDUDV( uint target, int level, int intformat, uint width, uint height, uint imageSize, const void* data )
{
byte *fin, *fout;
int i, size;
short *col;
if( !data ) return false;
fin = (byte *)data;
size = width * height * image.curdepth * 4;
image.tempbuffer = Mem_Realloc( Sys.imagepool, image.tempbuffer, size );
fout = image.tempbuffer;
switch( PFDesc[intformat].format )
{
case PF_UV_16:
for( i = 0, col = (short *)fin; i < width * height; i++, col += sizeof( short ))
{
fout[(i<<2)+0] = col[0];
fout[(i<<2)+1] = col[1];
fout[(i<<2)+2] = 0;
fout[(i<<2)+3] = 0;
}
break;
case PF_UV_32:
Mem_Copy( fout, fin, size ); // nothing to process
break;
}
Image_AddRGBAToPack( target, level, size, fout );
return true;
}
qboolean Image_DecompressRGBA( uint target, int level, int intformat, uint width, uint height, uint imageSize, const void* data )
{
byte *fin, *fout;
int i, size;
color16 *col;
if( !data ) return false;
fin = (byte *)data;
size = width * height * image.curdepth * 4;
image.tempbuffer = Mem_Realloc( Sys.imagepool, image.tempbuffer, size );
fout = image.tempbuffer;
switch( PFDesc[intformat].format )
{
case PF_RGB_16:
for( i = 0, col = (color16 *)fin; i < width * height; i++ )
{
fout[(i<<2)+0] = col->r << 3;
fout[(i<<2)+1] = col->g << 2;
fout[(i<<2)+2] = col->b << 3;
fout[(i<<2)+3] = 255;
col += sizeof( color16 );
}
break;
case PF_RGB_24:
for (i = 0; i < width * height; i++ )
{
fout[(i<<2)+0] = fin[i*3+0];
fout[(i<<2)+1] = fin[i*3+1];
fout[(i<<2)+2] = fin[i*3+2];
fout[(i<<2)+3] = 255;
}
break;
case PF_BGR_24:
for (i = 0; i < width * height; i++ )
{
fout[(i<<2)+0] = fin[i*3+2];
fout[(i<<2)+1] = fin[i*3+1];
fout[(i<<2)+2] = fin[i*3+0];
fout[(i<<2)+3] = 255;
}
break;
case PF_RGBA_GN:
case PF_RGBA_32:
Mem_Copy( fout, fin, size );
break;
case PF_BGRA_32:
case PF_ABGR_64:
for( i = 0; i < width * height; i++ )
{
fout[i*4+0] = fin[i*4+2];
fout[i*4+1] = fin[i*4+1];
fout[i*4+2] = fin[i*4+0];
fout[i*4+3] = fin[i*4+3];
}
break;
default: return false;
}
Image_AddRGBAToPack( target, level, size, fout );
return true;
}
void Image_DecompressDDS( const byte *buffer, uint target )
{
int i, size = 0;
int w = image.curwidth;
int h = image.curheight;
int d = image.curdepth;
// filter by cubemap side
if( image.filter != CB_HINT_NO && image.filter != target )
return;
switch( image.type )
{
case PF_INDEXED_24:
case PF_INDEXED_32: image.decompress = Image_DecompressPal8; break;
case PF_RGBA_32:
case PF_BGRA_32: image.decompress = Image_DecompressRGBA; break;
case PF_ARGB_32: image.decompress = Image_DecompressARGB; break;
case PF_ABGR_64:
case PF_RGB_24:
case PF_BGR_24:
case PF_RGB_16: image.decompress = Image_DecompressRGBA; break;
case PF_DXT1:
case PF_DXT2:
case PF_DXT3:
case PF_DXT4:
case PF_DXT5:
case PF_RXGB: image.decompress = Image_DecompressDXT; break;
case PF_ATI1N:
case PF_ATI2N: image.decompress = Image_DecompressATI; break;
case PF_LUMINANCE:
case PF_LUMINANCE_16:
case PF_LUMINANCE_ALPHA: image.decompress = Image_DecompressARGB; break;
case PF_UV_16:
case PF_UV_32: image.decompress = Image_DecompressDUDV; break;
case PF_R_16F:
case PF_R_32F:
case PF_GR_32F:
case PF_GR_64F:
case PF_ABGR_64F:
case PF_ABGR_128F: image.decompress = Image_DecompressFloat; break;
case PF_RGBA_GN: image.decompress = Image_DecompressRGBA; break;
default: Sys_Error( "Image_DecompressDDS: unknown image format\n" );
}
for( i = 0; i < image.cur_mips; i++ )
{
Image_SetPixelFormat( w, h, d );
size = image.SizeOfFile;
if(!image.decompress( target, i, image.type, w, h, size, buffer ))
break; // there were errors
w = (w+1)>>1, h = (h+1)>>1, d = (d+1)>>1; // calc size of next mip
buffer += size;
}
}
qboolean Image_ForceDecompress( void )
{
int w, h;
// GL_ARB_texture_compression missing, force to decompress anyway
if(!( image.cmd_flags & IL_DDS_HARDWARE )) return true;
// non power of two images needs resample
// but resample code not supported compressed images
w = image.width, h = image.height;
Image_RoundDimensions( &w, &h );
if( w != image.width || h != image.height )
return true;
// extract cubemap side from complex image
if( image.filter != CB_HINT_NO )
return true;
// UNDONE: load it properly with gl loader
switch( image.type )
{
case PF_RXGB: return true; // g-cont. test it with GL_COMPRESSED_RGBA_S3TC_DXT5_EXT ?
case PF_ATI1N: return true; // hey, how called your OpenGL extension, ATI ?
case PF_ATI2N: return true;
}
return false;
}
/*
=============
Image_LoadDDS
=============
*/
qboolean Image_LoadDDS( const char *name, const byte *buffer, size_t filesize )
{
dds_t header;
byte *fin;
uint i;
if( filesize < sizeof( dds_t ))
{
MsgDev( D_ERROR, "Image_LoadDDS: file (%s) have invalid size\n", name );
return false;
}
Mem_Copy( &header, buffer, sizeof( dds_t ));
if( header.dwIdent != DDSHEADER ) return false; // it's not a dds file, just skip it
if( header.dwSize != sizeof(dds_t) - sizeof( uint )) // size of the structure (minus MagicNum)
{
MsgDev( D_ERROR, "LoadDDS: (%s) have corrupted header\n", name );
return false;
}
if( header.dsPixelFormat.dwSize != sizeof(dds_pixf_t)) // size of the structure
{
MsgDev( D_ERROR, "LoadDDS: (%s) have corrupt pixelformat header\n", name );
return false;
}
image.width = header.dwWidth;
image.height = header.dwHeight;
image.bits_count = header.dsPixelFormat.dwRGBBitCount;
if( header.dwFlags & DDS_DEPTH) image.depth = header.dwDepth;
if(!Image_ValidSize( name )) return false;
Image_DXTGetPixelFormat( &header ); // and image type too :)
Image_DXTAdjustVolume( &header );
if( image.type == PF_UNKNOWN )
{
MsgDev( D_WARN, "LoadDDS: (%s) have unsupported compression type\n", name );
return false;
}
image.size = Image_DXTCalcSize( name, &header, filesize - 128 );
if( image.size == 0 ) return false; // just in case
fin = (byte *)(buffer + sizeof( dds_t ));
if( Image_ForceDecompress())
{
int offset, numsides = 1;
uint target = 1;
byte *buf = fin;
// if hardware loader is absent or image not power of two
// or user want load current side from cubemap we run software decompressing
if( image.flags & IMAGE_CUBEMAP ) numsides = 6;
Image_SetPixelFormat( image.width, image.height, image.depth ); // setup
image.size = image.ptr = 0;
if( image.cmd_flags & IL_IGNORE_MIPS )
image.cur_mips = 1;
else image.cur_mips = image.num_mips;
image.num_mips = 0; // clear mipcount
for( i = 0, offset = 0; i < numsides; i++, buf += offset )
{
Image_SetPixelFormat( image.curwidth, image.curheight, image.curdepth );
offset = image.SizeOfFile; // move pointer
Image_DecompressDDS( buf, target + i );
}
// now we can change type to RGBA
if( image.filter != CB_HINT_NO ) image.flags &= ~IMAGE_CUBEMAP; // side extracted
image.type = PF_RGBA_32;
image.bits_count = 32;
}
else
{
// dds files will be uncompressed on a render. requires minimal of info for set this
image.rgba = Mem_Alloc( Sys.imagepool, image.size );
Mem_Copy( image.rgba, fin, image.size );
}
return true;
}
qboolean Image_SaveDDS( const char *name, rgbdata_t *pix )
{
vfile_t *file; // virtual file
if( FS_FileExists( name, false ) && !(image.cmd_flags & IL_ALLOW_OVERWRITE ))
return false; // already existed
file = VFS_Open( NULL, "w" );
if( !file ) return false;
if( !Image_DXTWriteHeader( file, pix ))
{
MsgDev( D_ERROR, "Image_SaveDDS: unsupported format %s\n", PFDesc[pix->type].name );
VFS_Close( file );
return false;
}
if( !Image_DXTWriteImage( file, pix ))
{
MsgDev( D_ERROR, "Image_SaveDDS: can't create dds file\n" );
VFS_Close( file );
return false;
}
FS_WriteFile( name, VFS_GetBuffer( file ), VFS_Tell( file ));
VFS_Close( file ); // release virtual file
return true;
}