Merge pull request #759 from AnyOldName3/more-tga

Various TGA improvements
This commit is contained in:
OpenSceneGraph git repository 2019-06-14 15:01:15 +01:00 committed by GitHub
commit 68fc6ca610
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

View File

@ -76,6 +76,15 @@ simage_tga_error(char * buffer, int buflen)
return tgaerror; return tgaerror;
} }
enum AttributeType
{
NO_ALPHA = 0,
ALPHA_UNDEFINED_IGNORE = 1,
ALPHA_UNDEFINED_KEEP = 2,
ALPHA_PRESENT = 3,
ALPHA_PREMULTIPLIED = 4,
ATTRIBUTE_TYPE_UNSET = 256 // Out of range of values possible in file
};
/* TODO: */ /* TODO: */
/* - bottom-up images */ /* - bottom-up images */
@ -133,35 +142,69 @@ convert_32_to_32(const unsigned char * const src, unsigned char * const dest)
dest[3] = src[3]; dest[3] = src[3];
} }
static void
convert_32_to_24(const unsigned char * const src, unsigned char * const dest)
{
/* opengl image format is RGB */
/* TGA image format is BGRA (with A as attribute, not alpha) for 32 bit */
dest[0] = src[2];
dest[1] = src[1];
dest[2] = src[0];
}
static void static void
convert_data(const unsigned char * const src, unsigned char * const dest, convert_data(const unsigned char * const src, unsigned char * const dest,
const int x, const int srcformat, const int x, const int srcformat,
const int destformat) const int destformat)
{ {
if (srcformat == 2) if (destformat > 2) /* color */
{ {
if (destformat == 3) if (srcformat == 2)
convert_16_to_24(src+x*srcformat, {
dest+x*destformat); if (destformat == 3)
convert_16_to_24(src + x * srcformat,
dest + x * destformat);
else
{
assert(destformat == 4);
convert_16_to_32(src + x * srcformat,
dest + x * destformat);
}
}
else if (srcformat == 3)
{
assert(destformat == 3);
convert_24_to_24(src + x * srcformat,
dest + x * destformat);
}
else else
{ {
assert(destformat == 4); assert(srcformat == 4);
convert_16_to_32(src+x*srcformat, if (destformat == 3)
dest+x*destformat); convert_32_to_24(src + x * srcformat,
dest + x * destformat);
else
{
assert(destformat == 4);
convert_32_to_32(src + x * srcformat,
dest + x * destformat);
}
} }
} }
else if (srcformat == 3)
{
assert(destformat == 3);
convert_24_to_24(src+x*srcformat,
dest+x*destformat);
}
else else
{ {
assert(srcformat == 4 && destformat == 4); if (destformat == 1)
convert_32_to_32(src+x*srcformat, {
dest+x*destformat); assert(srcformat == 1 || srcformat == 2);
(dest + x * destformat)[0] = (src + x * srcformat)[0];
}
else
{
assert(srcformat == 2 && destformat == 2);
(dest + x * destformat)[0] = (src + x * srcformat)[0];
(dest + x * destformat)[1] = (src + x * srcformat)[1];
}
} }
} }
@ -258,6 +301,36 @@ const int rleEntrySize)
} }
} }
template<typename T>
struct SafeArray
{
SafeArray() { impl = NULL; }
SafeArray(size_t size) { impl = new T[size]; }
~SafeArray() { delete[] impl; }
T* release()
{
T* temp = impl;
impl = NULL;
return temp;
}
void reinitialise(size_t size)
{
delete[] impl;
impl = new T[size];
}
operator T*() { return impl; }
template<typename U>
explicit operator U() { return (U)impl; }
private:
T* impl;
};
unsigned char * unsigned char *
simage_tga_load(std::istream& fin, simage_tga_load(std::istream& fin,
@ -266,23 +339,26 @@ int *height_ret,
int *numComponents_ret) int *numComponents_ret)
{ {
unsigned char header[18]; unsigned char header[18];
unsigned char footer[26];
int type; int type;
int width; int width;
int height; int height;
int depth; int depth;
int flags; int flags;
int format; int format;
unsigned char *colormap; SafeArray<unsigned char> colormap;
int colormapFirst = 0;
int colormapLen = 0; int colormapLen = 0;
int indexsize = 0; int colormapDepth = 0;
int rleIsCompressed; int rleIsCompressed;
int rleRemaining; int rleRemaining;
int rleEntrySize; int rleEntrySize;
unsigned char rleCurrent[4]; unsigned char rleCurrent[4];
unsigned char *buffer;
unsigned char *dest; unsigned char *dest;
int bpr; int bpr;
unsigned char *linebuf; int alphaBPP;
AttributeType attributeType = ATTRIBUTE_TYPE_UNSET;
std::streampos endOfImage;
tgaerror = ERR_NO_ERROR; /* clear error */ tgaerror = ERR_NO_ERROR; /* clear error */
@ -296,11 +372,63 @@ int *numComponents_ret)
type = header[2]; type = header[2];
width = getInt16(&header[12]); width = getInt16(&header[12]);
height = getInt16(&header[14]); height = getInt16(&header[14]);
depth = header[16] >> 3; // Add 7 as R5G5B5 images with no alpha data take up two bytes per pixel, but only 15 bits
depth = (header[16] + 7) >> 3;
flags = header[17]; flags = header[17];
alphaBPP = flags & 0x0F;
fin.seekg(-26, std::ios::end);
endOfImage = fin.tellg() + (std::streamoff)26;
fin.read((char*)footer, 26);
if (fin.gcount() != 26)
{
tgaerror = ERR_READ;
return NULL;
}
// TGA footer signature is null-terminated, so works like a C string
if (strcmp((char*)&footer[8], "TRUEVISION-XFILE.") == 0)
{
endOfImage -= 26;
unsigned int extensionAreaOffset = getInt32(&footer[0]);
unsigned int developerAreaOffset = getInt32(&footer[4]);
if (extensionAreaOffset != 0)
{
endOfImage = std::min(endOfImage, (std::streampos)extensionAreaOffset);
// We only need the last few fields of the extension area
fin.seekg(extensionAreaOffset + 482);
unsigned char extensionAreaBuffer[13];
fin.read((char*)extensionAreaBuffer, 13);
if (fin.gcount() != 13)
{
tgaerror = ERR_READ;
return NULL;
}
unsigned int colorCorrectionOffset = getInt32(&extensionAreaBuffer[0]);
unsigned int postageStampOffset = getInt32(&extensionAreaBuffer[4]);
unsigned int scanLineOffset = getInt32(&extensionAreaBuffer[8]);
if (colorCorrectionOffset != 0)
endOfImage = std::min(endOfImage, (std::streampos)colorCorrectionOffset);
if (postageStampOffset != 0)
endOfImage = std::min(endOfImage, (std::streampos)postageStampOffset);
if (scanLineOffset != 0)
endOfImage = std::min(endOfImage, (std::streampos)scanLineOffset);
attributeType = (AttributeType) extensionAreaBuffer[12];
}
if (developerAreaOffset != 0)
endOfImage = std::min(endOfImage, (std::streampos)developerAreaOffset);
}
fin.seekg(18);
/* check for reasonable values in case this is not a tga file */ /* check for reasonable values in case this is not a tga file */
if ((type != 1 && type != 2 && type != 10) || if ((type != 1 && type != 2 && type != 3 && type != 9 && type != 10 && type != 11) ||
(width < 0 || width > 4096) || (width < 0 || width > 4096) ||
(height < 0 || height > 4096) || (height < 0 || height > 4096) ||
(depth < 1 || depth > 4)) (depth < 1 || depth > 4))
@ -312,30 +440,75 @@ int *numComponents_ret)
if (header[0]) /* skip identification field */ if (header[0]) /* skip identification field */
fin.seekg(header[0],std::ios::cur); fin.seekg(header[0],std::ios::cur);
colormap = NULL;
if (header[1] == 1) /* there is a colormap */ if (header[1] == 1) /* there is a colormap */
{ {
colormapLen = getInt16(&header[5]); colormapFirst = getInt16(&header[3]);
indexsize = header[7]>>3; if (colormapFirst != 0)
colormap = new unsigned char [colormapLen*indexsize];
fin.read((char*)colormap,colormapLen*indexsize);
if (indexsize == 2) /* 16 bits */
{ {
if (flags & 1) format = 4; // Error on non-zero colormapFirst as it's unclear from the specification what it actually does
else format = 3; tgaerror = ERR_UNSUPPORTED;
return NULL;
} }
colormapLen = getInt16(&header[5]);
colormapDepth = (header[7] + 7) >> 3;
colormap.reinitialise(colormapLen*colormapDepth);
if (colormap == NULL)
{
tgaerror = ERR_MEM;
return NULL;
}
fin.read((char*)colormap, colormapLen*colormapDepth);
if (colormapDepth == 2) /* 16 bits */
{
if (alphaBPP == 1 && (attributeType == ALPHA_PRESENT || attributeType == ALPHA_PREMULTIPLIED || attributeType == ATTRIBUTE_TYPE_UNSET))
format = 4;
else
format = 3;
}
else if (colormapDepth == 3)
format = 3;
else else
format = indexsize; {
assert(colormapDepth == 4);
if (attributeType == ALPHA_PRESENT || attributeType == ALPHA_PREMULTIPLIED || attributeType == ATTRIBUTE_TYPE_UNSET)
format = 4;
else
format = 3;
}
} }
else else if ((type & ~8) != 3) /* color image */
{ {
if (depth == 2) /* 16 bits */ if (depth == 2) /* 16 bits */
{ {
if (flags & 1) format = 4; if (alphaBPP == 1 && (attributeType == ALPHA_PRESENT || attributeType == ALPHA_PREMULTIPLIED || attributeType == ATTRIBUTE_TYPE_UNSET))
else format = 3; format = 4;
else
format = 3;
}
else if (depth == 3)
format = 3;
else
{
assert(depth == 4);
if (attributeType == ALPHA_PRESENT || attributeType == ALPHA_PREMULTIPLIED || attributeType == ATTRIBUTE_TYPE_UNSET)
format = 4;
else
format = 3;
}
}
else /* greyscale image */
{
if (depth == 1)
format = 1;
else
{
assert(depth == 2);
if (attributeType == ALPHA_PRESENT || attributeType == ALPHA_PREMULTIPLIED || attributeType == ATTRIBUTE_TYPE_UNSET)
format = 2;
else
format = 1;
} }
else format = depth;
} }
/* SoDebugError::postInfo("simage_tga_load", "TARGA file: %d %d %d %d %d\n", */ /* SoDebugError::postInfo("simage_tga_load", "TARGA file: %d %d %d %d %d\n", */
@ -344,10 +517,16 @@ int *numComponents_ret)
rleIsCompressed = 0; rleIsCompressed = 0;
rleRemaining = 0; rleRemaining = 0;
rleEntrySize = depth; rleEntrySize = depth;
buffer = new unsigned char [width*height*format]; SafeArray<unsigned char> buffer(width*height*format);
dest = buffer; dest = buffer;
bpr = format * width; bpr = format * width;
linebuf = new unsigned char [width*depth]; SafeArray<unsigned char> linebuf(width * depth);
if (buffer == NULL || linebuf == NULL)
{
tgaerror = ERR_MEM;
return NULL;
}
//check the intended image orientation //check the intended image orientation
bool bLeftToRight = (flags&0x10)==0; bool bLeftToRight = (flags&0x10)==0;
@ -360,20 +539,20 @@ int *numComponents_ret)
{ {
case 1: /* colormap, uncompressed */ case 1: /* colormap, uncompressed */
{ {
if (colormapLen == 0 || indexsize == 0) if (colormapLen == 0 || colormapDepth == 0)
{ {
tgaerror = ERR_UNSUPPORTED; /* colormap missing or empty */ tgaerror = ERR_UNSUPPORTED; /* colormap missing or empty */
if (colormap) delete [] colormap;
delete [] buffer;
delete [] linebuf;
return NULL; return NULL;
} }
unsigned char * formattedMap = new unsigned char[colormapLen * format]; SafeArray<unsigned char> formattedMap(colormapLen * format);
if (formattedMap == NULL)
{
tgaerror = ERR_MEM;
return NULL;
}
for (int i = 0; i < colormapLen; i++) for (int i = 0; i < colormapLen; i++)
{ {
convert_data(colormap, formattedMap, i, indexsize, format); convert_data(colormap, formattedMap, i, colormapDepth, format);
} }
int x, y; int x, y;
@ -383,7 +562,7 @@ int *numComponents_ret)
if (fin.gcount() != (std::streamsize) (width*depth)) if (fin.gcount() != (std::streamsize) (width*depth))
{ {
tgaerror = ERR_READ; tgaerror = ERR_READ;
break; return NULL;
} }
for (x = 0; x < width; x++) for (x = 0; x < width; x++)
@ -405,7 +584,6 @@ int *numComponents_ret)
break; break;
default: default:
tgaerror = ERR_UNSUPPORTED; tgaerror = ERR_UNSUPPORTED;
delete [] formattedMap;
return NULL; /* unreachable code - (depth < 1 || depth > 4) rejected by "check for reasonable values in case this is not a tga file" near the start of this function*/ return NULL; /* unreachable code - (depth < 1 || depth > 4) rejected by "check for reasonable values in case this is not a tga file" near the start of this function*/
} }
@ -415,11 +593,10 @@ int *numComponents_ret)
} }
dest += lineoffset; dest += lineoffset;
} }
delete [] formattedMap;
} }
break; break;
case 2: /* RGB, uncompressed */ case 2: /* RGB, uncompressed */
case 3: /* greyscale, uncompressed */
{ {
int x, y; int x, y;
for (y = 0; y < height; y++) for (y = 0; y < height; y++)
@ -428,7 +605,7 @@ int *numComponents_ret)
if (fin.gcount() != (std::streamsize) (width*depth)) if (fin.gcount() != (std::streamsize) (width*depth))
{ {
tgaerror = ERR_READ; tgaerror = ERR_READ;
break; return NULL;
} }
for (x = 0; x < width; x++) for (x = 0; x < width; x++)
{ {
@ -438,19 +615,100 @@ int *numComponents_ret)
} }
} }
break; break;
case 10: /* RGB, compressed */ case 9: /* colormap, compressed */
{ {
int size, x, y; if (colormapLen == 0 || colormapDepth == 0)
int pos = fin.tellg(); {
tgaerror = ERR_UNSUPPORTED; /* colormap missing or empty */
return NULL;
}
SafeArray<unsigned char> formattedMap(colormapLen * format);
if (formattedMap == NULL)
{
tgaerror = ERR_MEM;
return NULL;
}
for (int i = 0; i < colormapLen; i++)
{
convert_data(colormap, formattedMap, i, colormapDepth, format);
}
fin.seekg(0,std::ios::end); int size, x, y;
size = (int)fin.tellg() - pos; std::streampos pos = fin.tellg();
fin.seekg(pos,std::ios::beg);
unsigned char* buf = new unsigned char [size]; size = (int)(endOfImage - pos);
SafeArray<unsigned char> buf(size);
if (buf == NULL) if (buf == NULL)
{ {
tgaerror = ERR_MEM; tgaerror = ERR_MEM;
break; return NULL;
}
unsigned char* src = buf;
fin.read((char*)buf, size);
if (fin.gcount() == (std::streamsize)size)
{
for (y = 0; y < height; y++)
{
rle_decode(&src, linebuf, width*depth, &rleRemaining,
&rleIsCompressed, rleCurrent, rleEntrySize);
assert(src <= buf + size);
for (x = 0; x < width; x++)
{
int index;
switch (depth)
{
case 1:
index = linebuf[x];
break;
case 2:
index = getInt16(linebuf + x * 2);
break;
case 3:
index = getInt24(linebuf + x * 3);
break;
case 4:
index = getInt32(linebuf + x * 4);
break;
default:
tgaerror = ERR_UNSUPPORTED;
return NULL; /* unreachable code - (depth < 1 || depth > 4) rejected by "check for reasonable values in case this is not a tga file" near the start of this function*/
}
if (index >= colormapLen)
{
tgaerror = ERR_UNSUPPORTED;
return NULL;
}
int adjustedX = bLeftToRight ? x : (width - 1) - x;
for (int i = 0; i < format; i++)
(dest + adjustedX * format)[i] = (formattedMap + index * format)[i];
}
dest += lineoffset;
}
}
else
{
tgaerror = ERR_READ;
return NULL;
}
}
break;
case 10: /* RGB, compressed */
case 11: /* greyscale, compressed */
{
int size, x, y;
std::streampos pos = fin.tellg();
size = (int)(endOfImage - pos);
SafeArray<unsigned char> buf(size);
if (buf == NULL)
{
tgaerror = ERR_MEM;
return NULL;
} }
unsigned char* src = buf; unsigned char* src = buf;
@ -472,27 +730,21 @@ int *numComponents_ret)
else else
{ {
tgaerror = ERR_READ; tgaerror = ERR_READ;
return NULL;
} }
if (buf) delete [] buf;
} }
break; break;
default: default:
{
tgaerror = ERR_UNSUPPORTED; tgaerror = ERR_UNSUPPORTED;
} return NULL;
}
if (linebuf) delete [] linebuf;
if (colormap) delete [] colormap;
if (tgaerror)
{
if (buffer) delete [] buffer;
return NULL;
} }
*width_ret = width; *width_ret = width;
*height_ret = height; *height_ret = height;
*numComponents_ret = format; *numComponents_ret = format;
return buffer; return buffer.release();
} }