halflife-photomode/utils/qlumpy/quakegrb.cpp

846 lines
15 KiB
C++

/***
*
* Copyright (c) 1996-2002, Valve LLC. All rights reserved.
*
* This product contains software technology licensed from Id
* Software, Inc. ("Id Technology"). Id Technology (c) 1996 Id Software, Inc.
* All Rights Reserved.
*
****/
#include "qlumpy.h"
#include "math.h"
#pragma warning(disable : 4244)
typedef struct
{
short ofs, length;
} row_t;
typedef struct
{
int width, height;
int widthbits, heightbits;
unsigned char data[4];
} qtex_t;
typedef struct
{
int width, height;
byte data[4]; // variably sized
} qpic_t;
// Font stuff
#define NUM_GLYPHS 256
const unsigned kFontMarker = 254;
typedef struct
{
short startoffset;
short charwidth;
} charinfo;
typedef struct
{
int width, height;
int rowcount;
int rowheight;
charinfo fontinfo[NUM_GLYPHS];
byte data[4];
} qfont_t;
extern qboolean fTransparent255;
#define SCRN(x, y) (*(byteimage + (y)*byteimagewidth + x))
void GrabPalette16(void);
extern qboolean do16bit;
/*
==============
GrabRaw
filename RAW x y width height
==============
*/
void GrabRaw(void)
{
int x, y, xl, yl, xh, yh, w, h;
byte* screen_p;
int linedelta;
GetToken(false);
xl = atoi(token);
GetToken(false);
yl = atoi(token);
GetToken(false);
w = atoi(token);
GetToken(false);
h = atoi(token);
if (xl == -1)
{
xl = yl = 0;
w = byteimagewidth;
h = byteimageheight;
}
xh = xl + w;
yh = yl + h;
screen_p = byteimage + yl * byteimagewidth + xl;
linedelta = byteimagewidth - w;
for (y = yl; y < yh; y++)
{
for (x = xl; x < xh; x++)
{
*lump_p++ = *screen_p;
*screen_p++ = 0;
}
screen_p += linedelta;
}
}
/*
==============
GrabPalette
filename PALETTE [startcolor endcolor]
==============
*/
void GrabPalette(void)
{
int start, end, length;
if (TokenAvailable())
{
GetToken(false);
start = atoi(token);
GetToken(false);
end = atoi(token);
}
else
{
start = 0;
end = 255;
}
length = 3 * (end - start + 1);
memcpy(lump_p, lbmpalette + start * 3, length);
lump_p += length;
}
/*
==============
GrabPic
filename qpic x y width height
==============
*/
void GrabPic(void)
{
int x, y, xl, yl, xh, yh;
int width;
qpic_t* header;
GetToken(false);
xl = atoi(token);
GetToken(false);
yl = atoi(token);
GetToken(false);
xh = xl + atoi(token);
GetToken(false);
yh = yl + atoi(token);
if (xl == -1)
{
xl = yl = 0;
xh = byteimagewidth;
yh = byteimageheight;
}
if (xh < xl || yh < yl || xl < 0 || yl < 0) // || xh>319 || yh>239)
Error("GrabPic: Bad size: %i, %i, %i, %i", xl, yl, xh, yh);
//
// fill in header
//
header = (qpic_t*)lump_p;
width = xh - xl;
header->width = LittleLong(width);
header->height = LittleLong(yh - yl);
//
// start grabbing posts
//
lump_p = (byte*)header->data;
for (y = yl; y < yh; y++)
for (x = xl; x < xh; x++)
*lump_p++ = SCRN(x, y);
// New for 16bpp display
if (do16bit)
GrabPalette16();
}
/*
=============================================================================
COLORMAP GRABBING
=============================================================================
*/
/*
===============
BestColor
===============
*/
byte BestColor(int r, int g, int b, int start, int stop)
{
int i;
int dr, dg, db;
int bestdistortion, distortion;
int bestcolor;
byte* pal;
//
// let any color go to 0 as a last resort
//
bestdistortion = ((int)r * r + (int)g * g + (int)b * b) * 2;
bestcolor = 0;
pal = lbmpalette + start * 3;
for (i = start; i <= stop; i++)
{
dr = r - (int)pal[0];
dg = g - (int)pal[1];
db = b - (int)pal[2];
pal += 3;
distortion = dr * dr + dg * dg + db * db;
if (distortion < bestdistortion)
{
if (!distortion)
return i; // perfect match
bestdistortion = distortion;
bestcolor = i;
}
}
return bestcolor;
}
/*
==============
GrabColormap
filename COLORMAP levels fullbrights
the first map is an identiy 0-255
the final map is all black except for the fullbrights
the remaining maps are evenly spread
fullbright colors start at the top of the palette.
==============
*/
void GrabColormap(void)
{
int levels, brights;
int l, c;
float frac, red, green, blue;
GetToken(false);
levels = atoi(token);
GetToken(false);
brights = atoi(token);
// identity lump
for (l = 0; l < 256; l++)
*lump_p++ = l;
// shaded levels
for (l = 1; l < levels; l++)
{
frac = 1.0 - (float)l / (levels - 1);
for (c = 0; c < 256 - brights; c++)
{
red = lbmpalette[c * 3];
green = lbmpalette[c * 3 + 1];
blue = lbmpalette[c * 3 + 2];
red = (int)(red * frac + 0.5);
green = (int)(green * frac + 0.5);
blue = (int)(blue * frac + 0.5);
//
// note: 254 instead of 255 because 255 is the transparent color, and we
// don't want anything remapping to that
//
*lump_p++ = BestColor(red, green, blue, 0, 254);
}
for (; c < 256; c++)
*lump_p++ = c;
}
*lump_p++ = brights;
}
/*
==============
GrabColormap2
experimental -- not used by quake
filename COLORMAP2 range levels fullbrights
fullbright colors start at the top of the palette.
Range can be greater than 1 to allow overbright color tables.
the first map is all 0
the last (levels-1) map is at range
==============
*/
void GrabColormap2(void)
{
int levels, brights;
int l, c;
float frac, red, green, blue;
float range;
GetToken(false);
range = atof(token);
GetToken(false);
levels = atoi(token);
GetToken(false);
brights = atoi(token);
// shaded levels
for (l = 0; l < levels; l++)
{
frac = range - range * (float)l / (levels - 1);
for (c = 0; c < 256 - brights; c++)
{
red = lbmpalette[c * 3];
green = lbmpalette[c * 3 + 1];
blue = lbmpalette[c * 3 + 2];
red = (int)(red * frac + 0.5);
green = (int)(green * frac + 0.5);
blue = (int)(blue * frac + 0.5);
//
// note: 254 instead of 255 because 255 is the transparent color, and we
// don't want anything remapping to that
//
*lump_p++ = BestColor(red, green, blue, 0, 254);
}
// fullbrights allways stay the same
for (; c < 256; c++)
*lump_p++ = c;
}
*lump_p++ = brights;
}
/*
=============================================================================
MIPTEX GRABBING
=============================================================================
*/
typedef struct
{
char name[16];
unsigned width, height;
unsigned offsets[4]; // four mip maps stored
} miptex_t;
byte pixdata[256];
float linearpalette[256][3];
float d_red, d_green, d_blue;
int colors_used;
int color_used[256];
float maxdistortion;
byte AddColor(float r, float g, float b)
{
int i;
for (i = 0; i < 255; i++)
{
if (!color_used[i])
{
linearpalette[i][0] = r;
linearpalette[i][1] = g;
linearpalette[i][2] = b;
if (r < 0)
r = 0.0;
if (r > 1.0)
r = 1.0;
lbmpalette[i * 3 + 0] = pow(r, 1.0 / 2.2) * 255;
if (g < 0)
g = 0.0;
if (g > 1.0)
g = 1.0;
lbmpalette[i * 3 + 1] = pow(g, 1.0 / 2.2) * 255;
if (b < 0)
b = 0.0;
if (b > 1.0)
b = 1.0;
lbmpalette[i * 3 + 2] = pow(b, 1.0 / 2.2) * 255;
color_used[i] = 1;
colors_used++;
return i;
}
}
return 0;
}
/*
=============
AveragePixels
=============
*/
byte AveragePixels(int count)
{
float r, g, b;
int i;
int vis;
int pix;
float dr, dg, db;
float bestdistortion, distortion;
int bestcolor;
vis = 0;
r = g = b = 0;
for (i = 0; i < count; i++)
{
pix = pixdata[i];
r += linearpalette[pix][0];
g += linearpalette[pix][1];
b += linearpalette[pix][2];
}
r /= count;
g /= count;
b /= count;
r += d_red;
g += d_green;
b += d_blue;
//
// find the best color
//
// bestdistortion = r*r + g*g + b*b;
bestdistortion = 3.0;
bestcolor = -1;
for (i = 0; i < 255; i++)
{
if (color_used[i])
{
pix = i; //pixdata[i];
dr = r - linearpalette[i][0];
dg = g - linearpalette[i][1];
db = b - linearpalette[i][2];
distortion = dr * dr + dg * dg + db * db;
if (distortion < bestdistortion)
{
if (!distortion)
{
d_red = d_green = d_blue = 0; // no distortion yet
return pix; // perfect match
}
bestdistortion = distortion;
bestcolor = pix;
}
}
}
if (bestdistortion > 0.001 && colors_used < 255)
{
// printf("%f %f %f\n", r, g, b );
bestcolor = AddColor(r, g, b);
d_red = d_green = d_blue = 0;
bestdistortion = 0;
}
else
{
// error diffusion
d_red = r - linearpalette[bestcolor][0];
d_green = g - linearpalette[bestcolor][1];
d_blue = b - linearpalette[bestcolor][2];
}
if (bestdistortion > maxdistortion)
maxdistortion = bestdistortion;
return bestcolor;
}
/*
==============
GrabMip
filename MIP x y width height
must be multiples of sixteen
==============
*/
void GrabMip(void)
{
int i, j, x, y, xl, yl, xh, yh, w, h;
byte *screen_p, *source, testpixel;
int linedelta;
miptex_t* qtex;
int miplevel, mipstep;
int xx, yy, pix;
int count;
GetToken(false);
xl = atoi(token);
GetToken(false);
yl = atoi(token);
GetToken(false);
w = atoi(token);
GetToken(false);
h = atoi(token);
if (xl == -1)
{
xl = yl = 0;
w = byteimagewidth;
h = byteimageheight;
}
if ((w & 15) || (h & 15))
Error("line %i: miptex sizes must be multiples of 16", scriptline);
xh = xl + w;
yh = yl + h;
qtex = (miptex_t*)lump_p;
qtex->width = LittleLong(w);
qtex->height = LittleLong(h);
strcpy(qtex->name, lumpname);
lump_p = (byte*)&qtex->offsets[4];
screen_p = byteimage + yl * byteimagewidth + xl;
linedelta = byteimagewidth - w;
source = lump_p;
qtex->offsets[0] = LittleLong(lump_p - (byte*)qtex);
for (y = yl; y < yh; y++)
{
for (x = xl; x < xh; x++)
{
pix = *screen_p;
*screen_p++ = 0;
// if (pix == 255)
// pix = 0;
*lump_p++ = pix;
}
screen_p += linedelta;
}
// calculate gamma corrected linear palette
for (i = 0; i < 256; i++)
{
for (j = 0; j < 3; j++)
{
float f;
f = lbmpalette[i * 3 + j] / 255.0;
linearpalette[i][j] = pow(f, 2.2); // assume textures are done at 2.2, we want to remap them at 1.0
}
}
maxdistortion = 0;
if (!fTransparent255)
{
// figure out what palette entries are actually used
colors_used = 0;
for (i = 0; i < 256; i++)
color_used[i] = 0;
for (x = 0; x < w; x++)
{
for (y = 0; y < h; y++)
{
if (!color_used[source[y * w + x]])
{
color_used[source[y * w + x]] = 1;
colors_used++;
}
}
}
}
else
{
// assume palette full if it's a transparent texture
colors_used = 256;
for (i = 0; i < 256; i++)
color_used[i] = 1;
}
// printf("colors_used %d : ", colors_used );
//
// subsample for greater mip levels
//
for (miplevel = 1; miplevel < 4; miplevel++)
{
int pixTest;
d_red = d_green = d_blue = 0; // no distortion yet
qtex->offsets[miplevel] = LittleLong(lump_p - (byte*)qtex);
mipstep = 1 << miplevel;
pixTest = (int)((float)(mipstep * mipstep) * 0.4); // 40% of pixels
for (y = 0; y < h; y += mipstep)
{
for (x = 0; x < w; x += mipstep)
{
count = 0;
for (yy = 0; yy < mipstep; yy++)
for (xx = 0; xx < mipstep; xx++)
{
testpixel = source[(y + yy) * w + x + xx];
// If 255 is not transparent, or this isn't a transparent pixel, add it in to the image filter
if (!fTransparent255 || testpixel != 255)
{
pixdata[count] = testpixel;
count++;
}
}
if (count <= pixTest) // Solid pixels account for < 40% of this pixel, make it transparent
{
*lump_p++ = 255;
}
else
{
*lump_p++ = AveragePixels(count);
}
}
}
}
// printf(" %d %f\n", colors_used, maxdistortion );
if (do16bit)
GrabPalette16();
}
/*
=============================================================================
PALETTE GRABBING
=============================================================================
*/
void GrabPalette16(void)
{
// Write out palette in 16bit mode
*(unsigned short*)lump_p = 256; // palette size
lump_p += sizeof(short);
memcpy(lump_p, lbmpalette, 768);
lump_p += 768;
}
/*
=============================================================================
FONT GRABBING
=============================================================================
*/
/*
==============
GrabFont
font x y width height startglyph
==============
*/
void GrabFont(void)
{
int x, y, y2, xl, x2, yl, xh, yh, i, j;
int index, offset;
int iCurX; // current x in destination
int iMaxX; // max x in destination
byte *pbuf, *pCur;
qfont_t* header;
iMaxX = 255;
iCurX = 0;
// Set up header
header = (qfont_t*)lump_p;
memset(header, 0, sizeof(qfont_t));
GetToken(false);
header->width = header->rowheight = atoi(token); //mwh why does width equal rowheight?
header->height = 1;
lump_p = (byte*)header->data;
pCur = (byte*)lump_p;
memset(lump_p, 0xFF, 256 * 160);
GetToken(false);
index = atoi(token);
while (index != -1)
{
// Get/Process source bitmap coordinates
GetToken(false);
xl = atoi(token);
GetToken(false);
yl = atoi(token);
GetToken(false);
xh = xl - 1 + atoi(token);
GetToken(false);
yh = atoi(token) - 1;
if (xl == -1)
{
xl = yl = 0;
xh = byteimagewidth;
yh = byteimageheight;
}
if (xh < xl || yh < yl || xl < 0 || yl < 0)
Error("GrabFont line %1: Bad size: %i, %i, %i, %i", scriptline, xl, yl, xh, yh);
//
// Fill in font information
// Create a bitmap that is up to 256 wide and as tall as we need to accomadate the font.
// We limit the bitmap to 256 because some 3d boards have problems with textures bigger
// than that.
//
for (y = yl; y < yh; y += header->rowheight + 1)
{
// Make sure we're at a marker
if (y != yl)
{
for (y2 = y - header->rowheight; y2 < yh; y2++)
if (kFontMarker == (unsigned)SCRN(xl, y2))
break;
if (y2 == yh)
break;
else if (y2 != y)
Error("GrabFont line %d: rowheight doesn't seem to match bitmap (%d, %d)\n", scriptline, y, y2);
}
for (x = xl; x < xh;)
{
// find next marker
for (x2 = x + 1; x2 < xh; x2++)
if (kFontMarker == (unsigned)SCRN(x2, y))
break;
// check for end of row
if (x2 == xh)
break;
// Set up glyph information
if (index >= NUM_GLYPHS)
{
printf("GrabFont: Glyph out of range\n");
goto getout;
}
// Fill in glyph info
header->fontinfo[index].charwidth = x2 - x - 1;
// update header
// output glyph data
iCurX += header->fontinfo[index].charwidth;
// Will this glyph fit on this row?
if (iCurX >= iMaxX)
{
// Nope -- move to next row
pCur = (byte*)lump_p + 256 * header->rowheight * header->height;
header->height++;
iCurX = header->fontinfo[index].charwidth;
}
// copy over the glyph bytes
pbuf = pCur;
header->fontinfo[index].startoffset = pCur - (byte*)header->data;
for (j = 1; j <= header->rowheight; j++)
{
byte* psrc = byteimage + (y + j) * byteimagewidth + (x + 1);
for (i = x + 1; i < x2; i++)
*pbuf++ = *psrc++;
pbuf = pCur + j * 256;
}
// move the lump pointer to point at the next possible glyph
pCur += header->fontinfo[index].charwidth;
x = x2;
index++;
}
}
// Get next ASCII index
getout:
GetToken(false);
index = atoi(token);
}
// advance the lump pointer so that the last row is saved.
lump_p += (256 * header->rowheight) * header->height;
// JAY: Round up to the next power of 2 for GL
offset = header->height * header->rowheight;
y = (offset > 128) ? 256 : (offset > 64) ? 128
: (offset > 32) ? 64
: (offset > 16) ? 32
: 16;
if (offset != y)
{
printf("Rounding font from 256x%d to 256x%d\n", offset, y);
lump_p += (256 * (y - offset));
}
header->rowcount = header->height;
header->height = y;
if (do16bit)
GrabPalette16();
}