2011-02-18 14:31:32 +00:00
|
|
|
/*
|
|
|
|
===========================================================================
|
|
|
|
Copyright (C) 1999-2005 Id Software, Inc.
|
2021-03-24 20:13:01 +00:00
|
|
|
Copyright (C) 2002-2021 Q3Rally Team (Per Thormann - q3rally@gmail.com)
|
2011-02-18 14:31:32 +00:00
|
|
|
|
|
|
|
This file is part of q3rally source code.
|
|
|
|
|
|
|
|
q3rally source code 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 2 of the License,
|
|
|
|
or (at your option) any later version.
|
|
|
|
|
|
|
|
q3rally source code 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 q3rally; if not, write to the Free Software
|
|
|
|
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
|
|
|
===========================================================================
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include "cg_local.h"
|
|
|
|
|
|
|
|
#define POOLSIZE (512 * 1024)
|
|
|
|
|
|
|
|
static char memoryPool[POOLSIZE];
|
2011-07-26 08:52:24 +00:00
|
|
|
static int allocPoint = 0;
|
2011-02-18 14:31:32 +00:00
|
|
|
|
|
|
|
static void *CG_Alloc( int size ) {
|
|
|
|
char *p;
|
|
|
|
|
|
|
|
if ( allocPoint + size > POOLSIZE ) {
|
|
|
|
CG_Error( "CG_Alloc: failed on allocation of %i bytes\n", size );
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
p = &memoryPool[allocPoint];
|
|
|
|
|
|
|
|
allocPoint += ( size + 31 ) & ~31;
|
|
|
|
|
|
|
|
return p;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
2011-07-26 08:52:24 +00:00
|
|
|
static void CG_Free( int size ) {
|
2011-02-18 14:31:32 +00:00
|
|
|
allocPoint -= ( size + 31 ) & ~31;
|
|
|
|
}
|
|
|
|
*/
|
|
|
|
|
|
|
|
typedef struct
|
|
|
|
{
|
|
|
|
byte *imageData; // Image Data (Up To 32 Bits)
|
|
|
|
int bpp; // Image Color Depth In Bits Per Pixel
|
|
|
|
int width; // Image Width
|
|
|
|
int height; // Image Height
|
|
|
|
} TextureImage;
|
|
|
|
|
|
|
|
/*
|
|
|
|
This function is based on code in the Tokens, Extensions, Scissor Testing And TGA Loading
|
|
|
|
tutorial by Jeff Molofee at NeHe.
|
|
|
|
|
|
|
|
NeHe URL: http://nehe.gamedev.net
|
|
|
|
Tutorial URL: http://nehe.gamedev.net/data/lessons/lesson.asp?lesson=24
|
|
|
|
*/
|
|
|
|
qboolean LoadTGA(TextureImage *texture, const char *filename){
|
|
|
|
byte TGAheader[12]={0,0,2,0,0,0,0,0,0,0,0,0}; // Uncompressed TGA Header
|
|
|
|
byte TGAcompare[12]; // Used To Compare TGA Header
|
|
|
|
byte header[6]; // First 6 Useful Bytes From The Header
|
|
|
|
int bytesPerPixel; // Holds Number Of Bytes Per Pixel Used In The TGA File
|
|
|
|
int imageSize; // Used To Store The Image Size When Setting Aside Ram
|
|
|
|
//int temp; // Temporary Variable
|
|
|
|
fileHandle_t imageFile;
|
|
|
|
//int i;
|
|
|
|
|
|
|
|
// Read tga file
|
|
|
|
|
|
|
|
trap_FS_FOpenFile( filename, &imageFile, FS_READ );
|
|
|
|
|
|
|
|
if ( !imageFile ){
|
|
|
|
Com_Printf( S_COLOR_YELLOW "Q3R Warning: Could not open %s for license plate.\n", filename);
|
|
|
|
return qfalse;
|
|
|
|
}
|
|
|
|
|
|
|
|
trap_FS_Read(TGAcompare, sizeof(TGAcompare), imageFile);
|
|
|
|
|
|
|
|
if ( memcmp(TGAheader, TGAcompare, sizeof(TGAheader)) != 0){ // Does The Header Match What We Want?
|
|
|
|
trap_FS_FCloseFile( imageFile );
|
2012-09-15 08:59:59 +00:00
|
|
|
if (TGAcompare[2] == 10) {
|
|
|
|
Com_Printf( S_COLOR_YELLOW "Q3R Warning: Cannot load %s, Run-Length Encoded TGAs are unsupported.\n", filename);
|
|
|
|
} else {
|
|
|
|
Com_Printf( S_COLOR_YELLOW "Q3R Warning: Header of %s does not match known header format.\n", filename);
|
|
|
|
}
|
2011-02-18 14:31:32 +00:00
|
|
|
return qfalse;
|
|
|
|
}
|
|
|
|
|
|
|
|
trap_FS_Read(header, sizeof(header), imageFile); // Read in the rest of the 6 bytes
|
|
|
|
|
|
|
|
texture->width = header[1] * 256 + header[0]; // Determine The TGA Width (highbyte*256+lowbyte)
|
|
|
|
texture->height = header[3] * 256 + header[2]; // Determine The TGA Height (highbyte*256+lowbyte)
|
|
|
|
|
|
|
|
if( texture->width <=0 || // Is The Width Less Than Or Equal To Zero
|
|
|
|
texture->height <=0 || // Is The Height Less Than Or Equal To Zero
|
|
|
|
(header[4]!=24 && header[4]!=32)) // Is The TGA 24 or 32 Bit?
|
|
|
|
{
|
|
|
|
trap_FS_FCloseFile( imageFile );
|
|
|
|
Com_Printf( S_COLOR_YELLOW "Q3R Warning: %s has invalid dimensions or bpps.\n", filename);
|
|
|
|
return qfalse; // Return False
|
|
|
|
}
|
|
|
|
|
|
|
|
texture->bpp = header[4]; // Grab The TGA's Bits Per Pixel (24 or 32)
|
|
|
|
bytesPerPixel = texture->bpp / 8; // Divide By 8 To Get The Bytes Per Pixel
|
|
|
|
imageSize = texture->width * texture->height * bytesPerPixel; // Calculate The Memory Required For The TGA Data
|
|
|
|
|
2011-07-26 08:52:24 +00:00
|
|
|
texture->imageData = (unsigned char*)CG_Alloc(imageSize); // Reserve Memory To Hold The TGA Data
|
2011-02-18 14:31:32 +00:00
|
|
|
|
|
|
|
trap_FS_Read(texture->imageData, imageSize, imageFile);
|
|
|
|
|
|
|
|
if( texture->imageData == NULL ) // Does The Storage Memory Exist?
|
|
|
|
{
|
|
|
|
trap_FS_FCloseFile( imageFile );
|
|
|
|
Com_Printf( S_COLOR_YELLOW "Q3R Warning: Not enough memory to load %s.\n", filename);
|
|
|
|
return qfalse; // Return False
|
|
|
|
}
|
|
|
|
/*
|
|
|
|
for(i = 0; i < imageSize; i += bytesPerPixel) // Loop Through The Image Data
|
|
|
|
{ // Swaps The 1st And 3rd Bytes ('R'ed and 'B'lue)
|
|
|
|
temp = texture->imageData[i]; // Temporarily Store The Value At Image Data 'i'
|
|
|
|
texture->imageData[i] = texture->imageData[i + 2]; // Set The 1st Byte To The Value Of The 3rd Byte
|
|
|
|
texture->imageData[i + 2] = temp; // Set The 3rd Byte To The Value In 'temp' (1st Byte Value)
|
|
|
|
}
|
|
|
|
*/
|
|
|
|
trap_FS_FCloseFile( imageFile );
|
|
|
|
|
|
|
|
return qtrue; // Texture Building Went Ok, Return True
|
|
|
|
}
|
|
|
|
|
|
|
|
static qboolean WriteNameOnTexture(TextureImage *texture, const char *name, int maxChars ) {
|
|
|
|
vec4_t color;
|
|
|
|
const char *s;
|
|
|
|
unsigned char ch;
|
|
|
|
float ax, ay, aw, ah;
|
|
|
|
float frow, fcol, fwidth, fheight;
|
|
|
|
int i, j;
|
|
|
|
int bytesPerPixelF, bytesPerPixelT;
|
|
|
|
float a;
|
|
|
|
int t, f, len, cnt;
|
|
|
|
TextureImage font;
|
|
|
|
|
|
|
|
if (!LoadTGA(&font, "gfx/2d/bigchars.tga"))
|
|
|
|
return qfalse;
|
|
|
|
// LoadTGA(&font, "menu/art/font1_prop.tga");
|
|
|
|
|
|
|
|
bytesPerPixelF = font.bpp / 8;
|
|
|
|
bytesPerPixelT = texture->bpp / 8;
|
|
|
|
|
|
|
|
// use image for maxChars
|
|
|
|
len = (int)(texture->width / SMALLCHAR_WIDTH) - 1;
|
|
|
|
if (len < maxChars)
|
|
|
|
maxChars = len;
|
|
|
|
|
|
|
|
len = strlen(name);
|
|
|
|
if (len > maxChars)
|
|
|
|
len = maxChars;
|
|
|
|
|
|
|
|
// ax = (int)((texture->width / 2.0f) - (len * 9 / 2.0f) - 3.5);
|
|
|
|
ax = (int)((texture->width / 2.0f) - (len * (SMALLCHAR_WIDTH+1) / 2.0f) - 3);
|
|
|
|
if (ax < -3)
|
|
|
|
ax = -3;
|
|
|
|
ay = 11;
|
|
|
|
ah = SMALLCHAR_HEIGHT;
|
|
|
|
|
|
|
|
color[0] = 0;
|
|
|
|
color[1] = 0;
|
|
|
|
color[2] = 0;
|
|
|
|
color[3] = 1.0f;
|
|
|
|
|
|
|
|
s = name;
|
|
|
|
cnt = 0;
|
|
|
|
while ( *s && cnt < maxChars)
|
|
|
|
{
|
|
|
|
if ( Q_IsColorString( s ) ) {
|
|
|
|
memcpy( color, g_color_table[ColorIndex(*(s+1))], sizeof( color ) );
|
|
|
|
|
|
|
|
s += 2;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
ch = *s & 127;
|
|
|
|
|
|
|
|
if ( ch == ' ' ) {
|
|
|
|
aw = SMALLCHAR_WIDTH + 8;
|
|
|
|
} else if ( propMap[ch][2] != -1 ) {
|
|
|
|
frow = (ch >> 4) * 16.0f;
|
|
|
|
fcol = (ch & 15) * 16.0f;
|
|
|
|
fwidth = 16.0f;
|
|
|
|
fheight = 16.0f;
|
|
|
|
// fcol = (float)propMap[ch][0];
|
|
|
|
// frow = (float)propMap[ch][1];
|
|
|
|
// fwidth = (float)propMap[ch][2];
|
|
|
|
// fheight = (float)PROP_HEIGHT;
|
|
|
|
aw = SMALLCHAR_WIDTH + 8;
|
|
|
|
|
|
|
|
for (i = 0; i < ah; i++){
|
|
|
|
t = (int)((texture->height - (ay + ah - i)) * texture->width + ax) * bytesPerPixelT;
|
|
|
|
f = (int)(font.height - (frow + fheight - fheight * (i / (float)ah))) * font.width;
|
|
|
|
|
|
|
|
for (j = 0; j < aw; j++){
|
|
|
|
a = font.imageData[(int)((f + (int)(fcol + fwidth * (j / (float)aw))) * bytesPerPixelF + 3)];
|
|
|
|
|
|
|
|
texture->imageData[t] = (byte)(texture->imageData[t] * (1.0f - (a / 255.0f)) + color[2] * a);
|
|
|
|
texture->imageData[t+1] = (byte)(texture->imageData[t+1] * (1.0f - (a / 255.0f)) + color[1] * a);
|
|
|
|
texture->imageData[t+2] = (byte)(texture->imageData[t+2] * (1.0f - (a / 255.0f)) + color[0] * a);
|
|
|
|
|
|
|
|
t += bytesPerPixelT;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
aw = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
ax += aw-7;
|
|
|
|
cnt++;
|
|
|
|
s++;
|
|
|
|
}
|
|
|
|
|
|
|
|
return qtrue;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
qboolean SaveTGA(TextureImage *texture, const char *filename){
|
|
|
|
byte TGAheader[12]={0,0,2,0,0,0,0,0,0,0,0,0}; // Uncompressed TGA Header
|
|
|
|
byte header[6]; // First 6 Useful Bytes From The Header
|
|
|
|
int bytesPerPixel; // Holds Number Of Bytes Per Pixel Used In The TGA File
|
|
|
|
int imageSize; // Used To Store The Image Size When Setting Aside Ram
|
|
|
|
//int temp; // Temporary Variable
|
|
|
|
fileHandle_t imageFile;
|
|
|
|
//int i;
|
|
|
|
|
|
|
|
// Write tga file
|
|
|
|
|
|
|
|
trap_FS_FOpenFile( filename, &imageFile, FS_WRITE );
|
|
|
|
|
|
|
|
if ( !imageFile ){
|
|
|
|
Com_Printf( S_COLOR_YELLOW "Q3R Warning: Could not open %s for texture output.\n", filename);
|
|
|
|
return qfalse;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
bytesPerPixel = texture->bpp / 8; // Divide By 8 To Get The Bytes Per Pixel
|
|
|
|
imageSize = texture->width * texture->height * bytesPerPixel; // Calculate The Memory Required For The TGA Data
|
|
|
|
|
|
|
|
header[0] = texture->width % 256;
|
|
|
|
header[1] = texture->width / 256;
|
|
|
|
header[2] = texture->height % 256;
|
|
|
|
header[3] = texture->height / 256;
|
|
|
|
header[4] = texture->bpp;
|
|
|
|
header[5] = 0;
|
|
|
|
|
|
|
|
trap_FS_Write(TGAheader, sizeof(TGAheader), imageFile);
|
|
|
|
trap_FS_Write(header, sizeof(header), imageFile);
|
|
|
|
/*
|
|
|
|
for(i = 0; i < imageSize; i += bytesPerPixel) // Loop Through The Image Data
|
|
|
|
{ // Swaps The 3st And 1rd Bytes ('R'ed and 'B'lue)
|
|
|
|
temp = texture->imageData[i]; // Temporarily Store The Value At Image Data 'i'
|
|
|
|
texture->imageData[i] = texture->imageData[i + 2]; // Set The 3st Byte To The Value Of The 1rd Byte
|
|
|
|
texture->imageData[i + 2] = temp; // Set The 1rd Byte To The Value In 'temp' (3st Byte Value)
|
|
|
|
}
|
|
|
|
*/
|
|
|
|
trap_FS_Write(texture->imageData, imageSize, imageFile);
|
|
|
|
|
|
|
|
trap_FS_FCloseFile( imageFile );
|
|
|
|
|
|
|
|
return qtrue;
|
|
|
|
}
|
|
|
|
|
|
|
|
void CreateLicensePlateImage(const char *input, const char *output, const char *name, int maxChars){
|
|
|
|
TextureImage tga;
|
|
|
|
|
|
|
|
allocPoint = 0;
|
|
|
|
|
|
|
|
if (!LoadTGA(&tga, input))
|
|
|
|
return;
|
|
|
|
if (!WriteNameOnTexture(&tga, name, maxChars))
|
|
|
|
return;
|
|
|
|
SaveTGA(&tga, output);
|
|
|
|
}
|