mirror of
https://github.com/AquariaOSE/Aquaria.git
synced 2024-11-15 22:19:07 +00:00
874 lines
21 KiB
C++
874 lines
21 KiB
C++
/*
|
|
Copyright (C) 2007, 2010 - Bit-Blot
|
|
|
|
This file is part of Aquaria.
|
|
|
|
Aquaria 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.
|
|
|
|
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, write to the Free Software
|
|
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
|
*/
|
|
#include "Texture.h"
|
|
#include "Core.h"
|
|
#include "../ExternalLibs/glpng.h"
|
|
#include "ByteBuffer.h"
|
|
|
|
#include <assert.h>
|
|
|
|
#if defined(BBGE_BUILD_UNIX)
|
|
#include <stdint.h>
|
|
#endif
|
|
|
|
//#include "pngLoad.h"
|
|
//#include "jpeg/jpeglib.h"
|
|
/*
|
|
#include <il/il.h>
|
|
#include <il/ilu.h>
|
|
#include <il/ilut.h>
|
|
*/
|
|
#ifdef Z2D_J2K
|
|
//..\j2k-codec\j2k-codec.lib
|
|
#include "..\j2k-codec\j2k-codec.h"
|
|
#endif
|
|
|
|
#ifdef BBGE_BUILD_OPENGL
|
|
GLint Texture::filter = GL_LINEAR;
|
|
|
|
GLint Texture::format = 0;
|
|
#endif
|
|
bool Texture::useMipMaps = true;
|
|
|
|
/*
|
|
#ifdef BBGE_BUILD_OPENGL
|
|
#include "glext/glext.h"
|
|
#endif
|
|
*/
|
|
|
|
TexErr Texture::textureError = TEXERR_OK;
|
|
|
|
Texture::Texture() : Resource()
|
|
{
|
|
#ifdef BBGE_BUILD_OPENGL
|
|
textures[0] = 0;
|
|
#endif
|
|
#ifdef BBGE_BUILD_DIRECTX
|
|
d3dTexture = 0;
|
|
#endif
|
|
width = height = 0;
|
|
|
|
repeat = false;
|
|
repeating = false;
|
|
pngSetStandardOrientation(0);
|
|
|
|
ow = oh = -1;
|
|
}
|
|
|
|
Texture::~Texture()
|
|
{
|
|
destroy();
|
|
}
|
|
|
|
void Texture::read(int tx, int ty, int w, int h, unsigned char *pixels)
|
|
{
|
|
#ifdef BBGE_BUILD_OPENGL
|
|
if (tx == 0 && ty == 0 && w == this->width && h == this->height)
|
|
{
|
|
glBindTexture(GL_TEXTURE_2D, textures[0]);
|
|
glGetTexImage(GL_TEXTURE_2D, 0, GL_RGBA, GL_UNSIGNED_BYTE, pixels);
|
|
glBindTexture(GL_TEXTURE_2D, 0);
|
|
}
|
|
else
|
|
{
|
|
std::ostringstream os;
|
|
os << "Unable to read a texture subimage (size = "
|
|
<< this->width << "x" << this->height << ", requested = "
|
|
<< tx << "," << ty << "+" << w << "x" << h << ")";
|
|
debugLog(os.str());
|
|
}
|
|
#endif
|
|
}
|
|
|
|
void Texture::write(int tx, int ty, int w, int h, const unsigned char *pixels)
|
|
{
|
|
#ifdef BBGE_BUILD_OPENGL
|
|
glBindTexture(GL_TEXTURE_2D, textures[0]);
|
|
|
|
glTexSubImage2D(GL_TEXTURE_2D, 0,
|
|
tx,
|
|
ty,
|
|
w,
|
|
h,
|
|
GL_RGBA,
|
|
GL_UNSIGNED_BYTE,
|
|
pixels
|
|
);
|
|
|
|
glBindTexture(GL_TEXTURE_2D, 0);
|
|
/*
|
|
target Specifies the target texture. Must be
|
|
GL_TEXTURE_2D.
|
|
|
|
level Specifies the level-of-detail number. Level 0 is
|
|
the base image level. Level n is the nth mipmap
|
|
reduction image.
|
|
|
|
xoffset Specifies a texel offset in the x direction within
|
|
the texture array.
|
|
|
|
yoffset Specifies a texel offset in the y direction within
|
|
the texture array.
|
|
|
|
width Specifies the width of the texture subimage.
|
|
|
|
height Specifies the height of the texture subimage.
|
|
|
|
format Specifies the format of the pixel data. The
|
|
following symbolic values are accepted:
|
|
GL_COLOR_INDEX, GL_RED, GL_GREEN, GL_BLUE,
|
|
GL_ALPHA, GL_RGB, GL_RGBA, GL_LUMINANCE, and
|
|
GL_LUMINANCE_ALPHA.
|
|
|
|
type Specifies the data type of the pixel data. The
|
|
following symbolic values are accepted:
|
|
GL_UNSIGNED_BYTE, GL_BYTE, GL_BITMAP,
|
|
GL_UNSIGNED_SHORT, GL_SHORT, GL_UNSIGNED_INT,
|
|
GL_INT, and GL_FLOAT.
|
|
|
|
pixels Specifies a pointer to the image data in memory.
|
|
*/
|
|
#endif
|
|
}
|
|
|
|
void Texture::unload()
|
|
{
|
|
Resource::unload();
|
|
#ifdef BBGE_BUILD_OPENGL
|
|
if (textures[0])
|
|
{
|
|
ow = width;
|
|
oh = height;
|
|
|
|
if (core->debugLogTextures)
|
|
{
|
|
debugLog("UNLOADING TEXTURE: " + name);
|
|
}
|
|
|
|
|
|
glDeleteTextures(1, &textures[0]);
|
|
textures[0] = 0;
|
|
|
|
//removeRef();
|
|
}
|
|
#endif
|
|
}
|
|
|
|
void Texture::destroy()
|
|
{
|
|
#ifdef BBGE_BUILD_OPENGL
|
|
unload();
|
|
#endif
|
|
#ifdef BBGE_BUILD_DIRECTX
|
|
if (d3dTexture)
|
|
{
|
|
d3dTexture->Release();
|
|
d3dTexture = 0;
|
|
}
|
|
#endif
|
|
|
|
if (!core->isShuttingDown())
|
|
core->removeTexture(this->name);
|
|
|
|
// Resource::destroy();
|
|
}
|
|
|
|
int Texture::getPixelWidth()
|
|
{
|
|
#ifdef BBGE_BUILD_OPENGL
|
|
int w = 0, h = 0;
|
|
unsigned int size = 0;
|
|
unsigned char *data = getBufferAndSize(&w, &h, &size);
|
|
if (!data)
|
|
return 0;
|
|
|
|
int smallestx = -1, largestx = -1;
|
|
for (unsigned int x = 0; x < unsigned(w); x++)
|
|
{
|
|
for (unsigned int y = 0; y < unsigned(h); y++)
|
|
{
|
|
unsigned int p = (y*unsigned(w)*4) + (x*4) + 3;
|
|
if (p < size && data[p] >= 254)
|
|
{
|
|
if (smallestx == -1 || x < smallestx)
|
|
smallestx = x;
|
|
if (largestx == -1 || x > largestx)
|
|
largestx = x;
|
|
}
|
|
}
|
|
}
|
|
free(data);
|
|
return largestx - smallestx;
|
|
#elif defined(BBGE_BUILD_DIRECTX)
|
|
return 0;
|
|
#endif
|
|
}
|
|
|
|
int Texture::getPixelHeight()
|
|
{
|
|
#ifdef BBGE_BUILD_OPENGL
|
|
int w = 0, h = 0;
|
|
unsigned int size = 0;
|
|
unsigned char *data = getBufferAndSize(&w, &h, &size);
|
|
if (!data)
|
|
return 0;
|
|
|
|
int smallesty = -1, largesty = -1;
|
|
for (unsigned int x = 0; x < unsigned(w); x++)
|
|
{
|
|
for (unsigned int y = 0; y < unsigned(h); y++)
|
|
{
|
|
int p = (y*unsigned(w)*4) + (x*4) + 3;
|
|
if (p < size && data[p] >= 254)
|
|
{
|
|
if (smallesty == -1 || y < smallesty)
|
|
smallesty = y;
|
|
if (largesty == -1 || y > largesty)
|
|
largesty = y;
|
|
}
|
|
}
|
|
}
|
|
free(data);
|
|
return largesty - smallesty;
|
|
#elif defined(BBGE_BUILD_DIRECTX)
|
|
return 0;
|
|
#endif
|
|
}
|
|
|
|
void Texture::reload()
|
|
{
|
|
Resource::reload();
|
|
|
|
debugLog("RELOADING TEXTURE: " + name + " with loadName " + loadName + "...");
|
|
|
|
unload();
|
|
load(loadName);
|
|
|
|
/*if (ow != -1 && oh != -1)
|
|
{
|
|
width = ow;
|
|
height = oh;
|
|
}*/
|
|
debugLog("DONE");
|
|
}
|
|
|
|
void Texture::load(std::string file)
|
|
{
|
|
Texture::textureError = TEXERR_OK;
|
|
|
|
if (file.size()<4)
|
|
{
|
|
errorLog("Texture Name is Empty or Too Short");
|
|
Texture::textureError = TEXERR_FILENOTFOUND;
|
|
return;
|
|
}
|
|
|
|
stringToLowerUserData(file);
|
|
file = core->adjustFilenameCase(file);
|
|
|
|
loadName = file;
|
|
repeating = false;
|
|
|
|
size_t pos = file.find_last_of('.');
|
|
|
|
if ((pos != std::string::npos) && (pos >= 0))
|
|
{
|
|
// make sure this didn't catch the '.' in /home/username/.Aquaria/* --ryan.
|
|
const std::string userdata = core->getUserDataFolder();
|
|
const size_t len = userdata.length();
|
|
if (pos < len)
|
|
pos = std::string::npos;
|
|
}
|
|
|
|
/*if (core->debugLogTextures)
|
|
{
|
|
std::ostringstream os;
|
|
os << "pos [" << pos << "], file :" << file;
|
|
debugLog(os.str());
|
|
}*/
|
|
|
|
bool found = exists(file);
|
|
|
|
if(!found && exists(file + ".png"))
|
|
{
|
|
found = true;
|
|
file += ".png";
|
|
}
|
|
|
|
// .tga/.zga are never used as game graphics anywhere except save slot thumbnails.
|
|
// if so, their file names are passed exact, not with a missing extension
|
|
|
|
if (found)
|
|
{
|
|
file = localisePathInternalModpath(file);
|
|
file = core->adjustFilenameCase(file);
|
|
|
|
/*
|
|
std::ostringstream os;
|
|
os << "Loading texture [" << file << "]";
|
|
debugLog(os.str());
|
|
*/
|
|
std::string post = file.substr(file.size()-3, 3);
|
|
stringToLower(post);
|
|
if (post == "png")
|
|
{
|
|
|
|
#ifdef BBGE_BUILD_OPENGL
|
|
loadPNG(file);
|
|
#endif
|
|
|
|
#ifdef BBGE_BUILD_DIRECTX
|
|
D3DXCreateTextureFromFile(core->getD3DDevice(), file.c_str(), &this->d3dTexture);
|
|
if (!d3dTexture)
|
|
{
|
|
errorLog ("failed to load texture");
|
|
}
|
|
else
|
|
{
|
|
D3DSURFACE_DESC desc;
|
|
this->d3dTexture->GetLevelDesc(0,&desc);
|
|
|
|
width = desc.Width;
|
|
height = desc.Height;
|
|
}
|
|
#endif
|
|
}
|
|
else if (post == "zga")
|
|
{
|
|
loadZGA(file);
|
|
}
|
|
else if (post == "tga")
|
|
{
|
|
loadTGA(file);
|
|
}
|
|
else
|
|
{
|
|
debugLog("unknown image file type: " + file);
|
|
Texture::textureError = TEXERR_FILENOTFOUND;
|
|
width = 64;
|
|
height = 64;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// load default image / leave white
|
|
if (core->debugLogTextures)
|
|
debugLog("***Could not find texture: " + file);
|
|
Texture::textureError = TEXERR_FILENOTFOUND;
|
|
width = 64;
|
|
height = 64;
|
|
}
|
|
}
|
|
|
|
void Texture::apply(bool repeatOverride)
|
|
{
|
|
#ifdef BBGE_BUILD_OPENGL
|
|
glBindTexture(GL_TEXTURE_2D, textures[0]);
|
|
if (repeat || repeatOverride)
|
|
{
|
|
if (!repeating)
|
|
{
|
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
|
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
|
|
repeating = true;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (repeating)
|
|
{
|
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
|
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
|
|
repeating = false;
|
|
}
|
|
}
|
|
#endif
|
|
#ifdef BBGE_BUILD_DIRECTX
|
|
core->getD3DDevice()->SetTexture(0, d3dTexture);
|
|
|
|
#endif
|
|
}
|
|
|
|
void Texture::unbind()
|
|
{
|
|
}
|
|
|
|
#ifdef BBGE_BUILD_OPENGL
|
|
|
|
void Texture::setID(int id)
|
|
{
|
|
textures[0] = id;
|
|
}
|
|
|
|
#endif
|
|
|
|
void Texture::loadPNG(const std::string &file)
|
|
{
|
|
if (file.empty()) return;
|
|
|
|
#ifdef BBGE_BUILD_OPENGL
|
|
|
|
|
|
pngInfo info;
|
|
|
|
int pngType = PNG_ALPHA;
|
|
|
|
if (format != 0)
|
|
{
|
|
if (format == GL_LUMINANCE_ALPHA)
|
|
pngType = PNG_LUMINANCEALPHA;
|
|
}
|
|
|
|
unsigned long memsize = 0;
|
|
const char *memptr = readFile(file, &memsize);
|
|
if(!memptr || !memsize)
|
|
goto fail;
|
|
|
|
if (filter == GL_NEAREST)
|
|
{
|
|
textures[0] = pngBindMem(memptr, memsize, PNG_NOMIPMAPS, pngType, &info, GL_CLAMP_TO_EDGE, filter, filter);
|
|
}
|
|
else
|
|
{
|
|
textures[0] = pngBindMem(memptr, memsize, PNG_BUILDMIPMAPS, pngType, &info, GL_CLAMP_TO_EDGE, GL_LINEAR_MIPMAP_LINEAR, filter);
|
|
}
|
|
|
|
if (textures[0] != 0)
|
|
{
|
|
width = info.Width;
|
|
height = info.Height;
|
|
}
|
|
else
|
|
{
|
|
fail:
|
|
|
|
debugLog("Can't load PNG file: " + file);
|
|
width = 64;
|
|
height = 64;
|
|
Texture::textureError = TEXERR_FILENOTFOUND;
|
|
}
|
|
|
|
if(memptr)
|
|
delete [] memptr;
|
|
|
|
#endif
|
|
}
|
|
|
|
// internal load functions
|
|
void Texture::loadTGA(const std::string &file)
|
|
{
|
|
loadTGA(TGAload(file.c_str()));
|
|
}
|
|
|
|
void Texture::loadZGA(const std::string &file)
|
|
{
|
|
unsigned long size = 0;
|
|
char *buf = readCompressedFile(file, &size);
|
|
ImageTGA *tga = TGAloadMem(buf, size);
|
|
if (!tga)
|
|
{
|
|
debugLog("Can't load ZGA File: " + file);
|
|
return;
|
|
}
|
|
loadTGA(tga);
|
|
}
|
|
|
|
void Texture::loadTGA(ImageTGA *imageTGA)
|
|
{
|
|
if (!imageTGA)
|
|
return;
|
|
|
|
glGenTextures(1, &textures[0]);
|
|
glBindTexture(GL_TEXTURE_2D, textures[0]);
|
|
glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,filter); // Linear Filtering
|
|
glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,filter); // Linear Filtering
|
|
|
|
if (imageTGA->channels==3)
|
|
glTexImage2D(GL_TEXTURE_2D, 0, 3, imageTGA->sizeX, imageTGA->sizeY, 0, GL_RGB, GL_UNSIGNED_BYTE, imageTGA->data);
|
|
else if (imageTGA->channels==4)
|
|
glTexImage2D(GL_TEXTURE_2D, 0, 4,imageTGA->sizeX, imageTGA->sizeY, 0, GL_RGBA, GL_UNSIGNED_BYTE, imageTGA->data);
|
|
|
|
width = imageTGA->sizeX;
|
|
height = imageTGA->sizeY;
|
|
|
|
if (imageTGA->data)
|
|
delete[] (imageTGA->data);
|
|
free (imageTGA);
|
|
}
|
|
|
|
|
|
#define TGA_RGB 2 // This tells us it's a normal RGB (really BGR) file
|
|
#define TGA_A 3 // This tells us it's a ALPHA file
|
|
#define TGA_RLE 10 // This tells us that the targa is Run-Length Encoded (RLE)
|
|
|
|
#if defined(BBGE_BUILD_UNIX)
|
|
typedef uint8_t byte;
|
|
typedef uint16_t WORD;
|
|
#endif
|
|
|
|
|
|
#ifdef BBGE_BUILD_WINDOWS
|
|
#define byte char
|
|
#endif
|
|
|
|
ImageTGA *Texture::TGAload(const char *filename)
|
|
{
|
|
unsigned long size = 0;
|
|
char *rawbuf = readFile(filename, &size);
|
|
ImageTGA *tga = TGAloadMem(rawbuf, size);
|
|
if (rawbuf)
|
|
delete [] rawbuf;
|
|
if (!tga)
|
|
{
|
|
debugLog("Can't load TGA File!");
|
|
return NULL;
|
|
}
|
|
return tga;
|
|
}
|
|
|
|
ImageTGA *Texture::TGAloadMem(void *mem, int size)
|
|
{
|
|
if (!mem || size < 20)
|
|
return NULL;
|
|
|
|
ByteBuffer bb(mem, size, ByteBuffer::REUSE);
|
|
|
|
ImageTGA *pImageData = NULL; // This stores our important image data
|
|
WORD width = 0, height = 0; // The dimensions of the image
|
|
byte length = 0; // The length in bytes to the pixels
|
|
byte imageType = 0; // The image type (RLE, RGB, Alpha...)
|
|
byte bits = 0; // The bits per pixel for the image (16, 24, 32)
|
|
int channels = 0; // The channels of the image (3 = RGA : 4 = RGBA)
|
|
int stride = 0; // The stride (channels * width)
|
|
int i = 0; // A counter
|
|
|
|
// This function loads in a TARGA (.TGA) file and returns its data to be
|
|
// used as a texture or what have you. This currently loads in a 16, 24
|
|
// and 32-bit targa file, along with RLE compressed files. Eventually you
|
|
// will want to do more error checking to make it more robust. This is
|
|
// also a perfect start to go into a modular class for an engine.
|
|
// Basically, how it works is, you read in the header information, then
|
|
// move your file pointer to the pixel data. Before reading in the pixel
|
|
// data, we check to see the if it's an RLE compressed image. This is because
|
|
// we will handle it different. If it isn't compressed, then we need another
|
|
// check to see if we need to convert it from 16-bit to 24 bit. 24-bit and
|
|
// 32-bit textures are very similar, so there's no need to do anything special.
|
|
// We do, however, read in an extra bit for each color.
|
|
|
|
|
|
// Allocate the structure that will hold our eventual image data (must free it!)
|
|
pImageData = (ImageTGA*)malloc(sizeof(ImageTGA));
|
|
|
|
// Read in the length in bytes from the header to the pixel data
|
|
bb >> length;
|
|
|
|
// Jump over one byte
|
|
bb.skipRead(1);
|
|
|
|
// Read in the imageType (RLE, RGB, etc...)
|
|
//fread(&imageType, sizeof(byte), 1, pFile);
|
|
bb >> imageType;
|
|
|
|
// Skip past general information we don't care about
|
|
bb.skipRead(9);
|
|
|
|
// Read the width, height and bits per pixel (16, 24 or 32)
|
|
bb >> width >> height >> bits;
|
|
|
|
/*
|
|
std::ostringstream os;
|
|
os << "TGALoad: width: " << width << " height: " << height << " bits: " << bits;
|
|
debugLog(os.str());
|
|
*/
|
|
|
|
// Now we move the file pointer to the pixel data
|
|
bb.skipRead(length + 1);
|
|
|
|
// Check if the image is RLE compressed or not
|
|
if(imageType != TGA_RLE)
|
|
{
|
|
// Check if the image is a 24 or 32-bit image
|
|
if(bits == 24 || bits == 32)
|
|
{
|
|
// Calculate the channels (3 or 4) - (use bits >> 3 for more speed).
|
|
// Next, we calculate the stride and allocate enough memory for the pixels.
|
|
channels = bits / 8;
|
|
stride = channels * width;
|
|
pImageData->data = new unsigned char[stride * height];
|
|
|
|
// Load in all the pixel data line by line
|
|
for(int y = 0; y < height; y++)
|
|
{
|
|
// Store a pointer to the current line of pixels
|
|
unsigned char *pLine = &(pImageData->data[stride * y]);
|
|
|
|
// Read in the current line of pixels
|
|
if (bb.readable() < stride)
|
|
break;
|
|
bb.read(pLine, stride);
|
|
|
|
// Go through all of the pixels and swap the B and R values since TGA
|
|
// files are stored as BGR instead of RGB (or use GL_BGR_EXT verses GL_RGB)
|
|
for(i = 0; i < stride; i += channels)
|
|
{
|
|
int temp = pLine[i];
|
|
pLine[i] = pLine[i + 2];
|
|
pLine[i + 2] = temp;
|
|
}
|
|
}
|
|
}
|
|
// Check if the image is a 16 bit image (RGB stored in 1 unsigned short)
|
|
else if(bits == 16)
|
|
{
|
|
unsigned short pixels = 0;
|
|
int r=0, g=0, b=0;
|
|
|
|
// Since we convert 16-bit images to 24 bit, we hardcode the channels to 3.
|
|
// We then calculate the stride and allocate memory for the pixels.
|
|
channels = 3;
|
|
stride = channels * width;
|
|
pImageData->data = new unsigned char[stride * height];
|
|
|
|
// Load in all the pixel data pixel by pixel
|
|
for(int i = 0; i < width*height; i++)
|
|
{
|
|
// Read in the current pixel
|
|
if (bb.readable() < sizeof(unsigned char))
|
|
break;
|
|
bb >> pixels;
|
|
|
|
// To convert a 16-bit pixel into an R, G, B, we need to
|
|
// do some masking and such to isolate each color value.
|
|
// 0x1f = 11111 in binary, so since 5 bits are reserved in
|
|
// each unsigned short for the R, G and B, we bit shift and mask
|
|
// to find each value. We then bit shift up by 3 to get the full color.
|
|
b = (pixels & 0x1f) << 3;
|
|
g = ((pixels >> 5) & 0x1f) << 3;
|
|
r = ((pixels >> 10) & 0x1f) << 3;
|
|
|
|
// This essentially assigns the color to our array and swaps the
|
|
// B and R values at the same time.
|
|
pImageData->data[i * 3 + 0] = r;
|
|
pImageData->data[i * 3 + 1] = g;
|
|
pImageData->data[i * 3 + 2] = b;
|
|
}
|
|
}
|
|
// Else return a NULL for a bad or unsupported pixel format
|
|
else
|
|
return NULL;
|
|
}
|
|
// Else, it must be Run-Length Encoded (RLE)
|
|
else
|
|
{
|
|
// First, let me explain real quickly what RLE is.
|
|
// For further information, check out Paul Bourke's intro article at:
|
|
// http://astronomy.swin.edu.au/~pbourke/dataformats/rle/
|
|
//
|
|
// Anyway, we know that RLE is a basic type compression. It takes
|
|
// colors that are next to each other and then shrinks that info down
|
|
// into the color and a integer that tells how much of that color is used.
|
|
// For instance:
|
|
// aaaaabbcccccccc would turn into a5b2c8
|
|
// Well, that's fine and dandy and all, but how is it down with RGB colors?
|
|
// Simple, you read in an color count (rleID), and if that number is less than 128,
|
|
// it does NOT have any optimization for those colors, so we just read the next
|
|
// pixels normally. Say, the color count was 28, we read in 28 colors like normal.
|
|
// If the color count is over 128, that means that the next color is optimized and
|
|
// we want to read in the same pixel color for a count of (colorCount - 127).
|
|
// It's 127 because we add 1 to the color count, as you'll notice in the code.
|
|
|
|
// Create some variables to hold the rleID, current colors read, channels, & stride.
|
|
byte rleID = 0;
|
|
int colorsRead = 0;
|
|
channels = bits / 8;
|
|
stride = channels * width;
|
|
|
|
// Next we want to allocate the memory for the pixels and create an array,
|
|
// depending on the channel count, to read in for each pixel.
|
|
pImageData->data = new unsigned char[stride * height];
|
|
byte *pColors = new byte [channels];
|
|
|
|
// Load in all the pixel data
|
|
while(i < width*height)
|
|
{
|
|
// Read in the current color count + 1
|
|
bb >> rleID;
|
|
|
|
// Check if we don't have an encoded string of colors
|
|
if(rleID < 128)
|
|
{
|
|
// Increase the count by 1
|
|
rleID++;
|
|
|
|
// Go through and read all the unique colors found
|
|
while(rleID)
|
|
{
|
|
// Read in the current color
|
|
if (bb.readable() < channels)
|
|
break;
|
|
bb.read(pColors, channels);
|
|
|
|
// Store the current pixel in our image array
|
|
pImageData->data[colorsRead + 0] = pColors[2];
|
|
pImageData->data[colorsRead + 1] = pColors[1];
|
|
pImageData->data[colorsRead + 2] = pColors[0];
|
|
|
|
// If we have a 4 channel 32-bit image, assign one more for the alpha
|
|
if(bits == 32)
|
|
pImageData->data[colorsRead + 3] = pColors[3];
|
|
|
|
// Increase the current pixels read, decrease the amount
|
|
// of pixels left, and increase the starting index for the next pixel.
|
|
i++;
|
|
rleID--;
|
|
colorsRead += channels;
|
|
}
|
|
}
|
|
// Else, let's read in a string of the same character
|
|
else
|
|
{
|
|
// Minus the 128 ID + 1 (127) to get the color count that needs to be read
|
|
rleID -= 127;
|
|
|
|
// Read in the current color, which is the same for a while
|
|
if (bb.readable() < channels)
|
|
break;
|
|
bb.read(pColors, channels);
|
|
|
|
// Go and read as many pixels as are the same
|
|
while(rleID)
|
|
{
|
|
// Assign the current pixel to the current index in our pixel array
|
|
pImageData->data[colorsRead + 0] = pColors[2];
|
|
pImageData->data[colorsRead + 1] = pColors[1];
|
|
pImageData->data[colorsRead + 2] = pColors[0];
|
|
|
|
// If we have a 4 channel 32-bit image, assign one more for the alpha
|
|
if(bits == 32)
|
|
pImageData->data[colorsRead + 3] = pColors[3];
|
|
|
|
// Increase the current pixels read, decrease the amount
|
|
// of pixels left, and increase the starting index for the next pixel.
|
|
i++;
|
|
rleID--;
|
|
colorsRead += channels;
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
// Free up pColors
|
|
delete[] pColors;
|
|
}
|
|
|
|
// Fill in our tImageTGA structure to pass back
|
|
pImageData->channels = channels;
|
|
pImageData->sizeX = width;
|
|
pImageData->sizeY = height;
|
|
|
|
// Return the TGA data (remember, you must free this data after you are done)
|
|
return pImageData;
|
|
}
|
|
|
|
// ceil to next power of 2
|
|
static unsigned int clp2(unsigned int x)
|
|
{
|
|
--x;
|
|
x |= (x >> 1);
|
|
x |= (x >> 2);
|
|
x |= (x >> 4);
|
|
x |= (x >> 8);
|
|
x |= (x >> 16);
|
|
return x + 1;
|
|
}
|
|
|
|
unsigned char * Texture::getBufferAndSize(int *wparam, int *hparam, unsigned int *sizeparam)
|
|
{
|
|
unsigned char *data = NULL;
|
|
unsigned int size = 0;
|
|
int tw = 0, th = 0;
|
|
int w = 0, h = 0;
|
|
|
|
// This can't happen. If it does we're doomed.
|
|
if(width <= 0 || height <= 0)
|
|
goto fail;
|
|
|
|
glBindTexture(GL_TEXTURE_2D, textures[0]);
|
|
|
|
// As returned by graphics driver
|
|
|
|
glGetTexLevelParameteriv(GL_TEXTURE_2D, 0, GL_TEXTURE_WIDTH, &w);
|
|
glGetTexLevelParameteriv(GL_TEXTURE_2D, 0, GL_TEXTURE_HEIGHT, &h);
|
|
|
|
// As we know it - but round to nearest power of 2 - OpenGL does this internally anyways.
|
|
tw = clp2(width); // known to be > 0.
|
|
th = clp2(height);
|
|
|
|
if (w != tw || h != th)
|
|
{
|
|
std::ostringstream os;
|
|
os << "Texture::getBufferAndSize() WARNING: width/height disagree: ";
|
|
os << "Driver says (" << w << ", " << h << "); ";
|
|
os << "Texture says (" << width << ", " << height << "); ";
|
|
os << "Rounded to (" << tw << ", " << th << ")";
|
|
debugLog(os.str());
|
|
// choose max. for size calculation
|
|
w = w > tw ? w : tw;
|
|
h = h > th ? h : th;
|
|
}
|
|
|
|
size = w * h * 4;
|
|
if (!size)
|
|
goto fail;
|
|
|
|
data = (unsigned char*)malloc(size + 32);
|
|
if (!data)
|
|
{
|
|
std::ostringstream os;
|
|
os << "Game::fillGridFromQuad allocation failure, size = " << size;
|
|
errorLog(os.str());
|
|
goto fail;
|
|
}
|
|
memcpy(data + size, "SAFE", 5);
|
|
glGetTexImage(GL_TEXTURE_2D, 0, GL_RGBA, GL_UNSIGNED_BYTE, data);
|
|
glBindTexture(GL_TEXTURE_2D, 0);
|
|
|
|
// Not sure but this might be the case with nouveau drivers on linux... still investigating. -- fg
|
|
if(memcmp(data + size, "SAFE", 5))
|
|
{
|
|
errorLog("Texture::getBufferAndSize(): Broken graphics driver! Wrote past end of buffer!");
|
|
free(data); // in case we are here, this will most likely cause a crash.
|
|
goto fail;
|
|
}
|
|
|
|
*wparam = w;
|
|
*hparam = h;
|
|
*sizeparam = size;
|
|
return data;
|
|
|
|
|
|
fail:
|
|
*wparam = 0;
|
|
*hparam = 0;
|
|
*sizeparam = 0;
|
|
return NULL;
|
|
}
|