Files
SingularityViewer/indra/llimage/llimagetga.cpp
2010-04-02 02:48:44 -03:00

1190 lines
28 KiB
C++

/**
* @file llimagetga.cpp
*
* $LicenseInfo:firstyear=2001&license=viewergpl$
*
* Copyright (c) 2001-2009, Linden Research, Inc.
*
* Second Life Viewer Source Code
* The source code in this file ("Source Code") is provided by Linden Lab
* to you under the terms of the GNU General Public License, version 2.0
* ("GPL"), unless you have obtained a separate licensing agreement
* ("Other License"), formally executed by you and Linden Lab. Terms of
* the GPL can be found in doc/GPL-license.txt in this distribution, or
* online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
*
* There are special exceptions to the terms and conditions of the GPL as
* it is applied to this Source Code. View the full text of the exception
* in the file doc/FLOSS-exception.txt in this software distribution, or
* online at
* http://secondlifegrid.net/programs/open_source/licensing/flossexception
*
* By copying, modifying or distributing this software, you acknowledge
* that you have read and understood your obligations described above,
* and agree to abide by those obligations.
*
* ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
* WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
* COMPLETENESS OR PERFORMANCE.
* $/LicenseInfo$
*/
#include "linden_common.h"
#include "lldir.h"
#include "llimagetga.h"
#include "llerror.h"
#include "llmath.h"
// For expanding 5-bit pixel values to 8-bit with best rounding
// static
const U8 LLImageTGA::s5to8bits[32] =
{
0, 8, 16, 25, 33, 41, 49, 58,
66, 74, 82, 90, 99, 107, 115, 123,
132, 140, 148, 156, 165, 173, 181, 189,
197, 206, 214, 222, 230, 239, 247, 255
};
inline void LLImageTGA::decodeTruecolorPixel15( U8* dst, const U8* src )
{
// We expand 5 bit data to 8 bit sample width.
// The format of the 16-bit (LSB first) input word is
// xRRRRRGGGGGBBBBB
U32 t = U32(src[0]) + (U32(src[1]) << 8);
dst[2] = s5to8bits[t & 0x1F]; // blue
t >>= 5;
dst[1] = s5to8bits[t & 0x1F]; // green
t >>= 5;
dst[0] = s5to8bits[t & 0x1F]; // red
}
LLImageTGA::LLImageTGA()
: LLImageFormatted(IMG_CODEC_TGA),
mColorMap( NULL ),
mColorMapStart( 0 ),
mColorMapLength( 0 ),
mColorMapBytesPerEntry( 0 ),
mIs15Bit( FALSE ),
mAttributeBits(0),
mColorMapDepth(0),
mColorMapIndexHi(0),
mColorMapIndexLo(0),
mColorMapLengthHi(0),
mColorMapLengthLo(0),
mColorMapType(0),
mDataOffset(0),
mHeightHi(0),
mHeightLo(0),
mIDLength(0),
mImageType(0),
mInterleave(0),
mOriginRightBit(0),
mOriginTopBit(0),
mPixelSize(0),
mWidthHi(0),
mWidthLo(0),
mXOffsetHi(0),
mXOffsetLo(0),
mYOffsetHi(0),
mYOffsetLo(0)
{
}
LLImageTGA::LLImageTGA(const std::string& file_name)
: LLImageFormatted(IMG_CODEC_TGA),
mColorMap( NULL ),
mColorMapStart( 0 ),
mColorMapLength( 0 ),
mColorMapBytesPerEntry( 0 ),
mIs15Bit( FALSE )
{
loadFile(file_name);
}
LLImageTGA::~LLImageTGA()
{
delete [] mColorMap;
}
BOOL LLImageTGA::updateData()
{
resetLastError();
// Check to make sure that this instance has been initialized with data
if (!getData() || (0 == getDataSize()))
{
setLastError("LLImageTGA uninitialized");
return FALSE;
}
// Pull image information from the header...
U8 flags;
U8 junk[256];
/****************************************************************************
**
** For more information about the original Truevision TGA(tm) file format,
** or for additional information about the new extensions to the
** Truevision TGA file, refer to the "Truevision TGA File Format
** Specification Version 2.0" available from Truevision or your
** Truevision dealer.
**
** FILE STRUCTURE FOR THE ORIGINAL TRUEVISION TGA FILE
** FIELD 1 : NUMBER OF CHARACTERS IN ID FIELD (1 BYTES)
** FIELD 2 : COLOR MAP TYPE (1 BYTES)
** FIELD 3 : IMAGE TYPE CODE (1 BYTES)
** = 0 NO IMAGE DATA INCLUDED
** = 1 UNCOMPRESSED, COLOR-MAPPED IMAGE
** = 2 UNCOMPRESSED, TRUE-COLOR IMAGE
** = 3 UNCOMPRESSED, BLACK AND WHITE IMAGE
** = 9 RUN-LENGTH ENCODED COLOR-MAPPED IMAGE
** = 10 RUN-LENGTH ENCODED TRUE-COLOR IMAGE
** = 11 RUN-LENGTH ENCODED BLACK AND WHITE IMAGE
** FIELD 4 : COLOR MAP SPECIFICATION (5 BYTES)
** 4.1 : COLOR MAP ORIGIN (2 BYTES)
** 4.2 : COLOR MAP LENGTH (2 BYTES)
** 4.3 : COLOR MAP ENTRY SIZE (2 BYTES)
** FIELD 5 : IMAGE SPECIFICATION (10 BYTES)
** 5.1 : X-ORIGIN OF IMAGE (2 BYTES)
** 5.2 : Y-ORIGIN OF IMAGE (2 BYTES)
** 5.3 : WIDTH OF IMAGE (2 BYTES)
** 5.4 : HEIGHT OF IMAGE (2 BYTES)
** 5.5 : IMAGE PIXEL SIZE (1 BYTE)
** 5.6 : IMAGE DESCRIPTOR BYTE (1 BYTE)
** FIELD 6 : IMAGE ID FIELD (LENGTH SPECIFIED BY FIELD 1)
** FIELD 7 : COLOR MAP DATA (BIT WIDTH SPECIFIED BY FIELD 4.3 AND
** NUMBER OF COLOR MAP ENTRIES SPECIFIED IN FIELD 4.2)
** FIELD 8 : IMAGE DATA FIELD (WIDTH AND HEIGHT SPECIFIED IN
** FIELD 5.3 AND 5.4)
****************************************************************************/
mDataOffset = 0;
mIDLength = *(getData()+mDataOffset++);
mColorMapType = *(getData()+mDataOffset++);
mImageType = *(getData()+mDataOffset++);
mColorMapIndexLo = *(getData()+mDataOffset++);
mColorMapIndexHi = *(getData()+mDataOffset++);
mColorMapLengthLo = *(getData()+mDataOffset++);
mColorMapLengthHi = *(getData()+mDataOffset++);
mColorMapDepth = *(getData()+mDataOffset++);
mXOffsetLo = *(getData()+mDataOffset++);
mXOffsetHi = *(getData()+mDataOffset++);
mYOffsetLo = *(getData()+mDataOffset++);
mYOffsetHi = *(getData()+mDataOffset++);
mWidthLo = *(getData()+mDataOffset++);
mWidthHi = *(getData()+mDataOffset++);
mHeightLo = *(getData()+mDataOffset++);
mHeightHi = *(getData()+mDataOffset++);
mPixelSize = *(getData()+mDataOffset++);
flags = *(getData()+mDataOffset++);
mAttributeBits = flags & 0xf;
mOriginRightBit = (flags & 0x10) >> 4;
mOriginTopBit = (flags & 0x20) >> 5;
mInterleave = (flags & 0xc0) >> 6;
switch( mImageType )
{
case 0:
// No image data included in file
setLastError("Unable to load file. TGA file contains no image data.");
return FALSE;
case 1:
// Colormapped uncompressed
if( 8 != mPixelSize )
{
setLastError("Unable to load file. Colormapped images must have 8 bits per pixel.");
return FALSE;
}
break;
case 2:
// Truecolor uncompressed
break;
case 3:
// Monochrome uncompressed
if( 8 != mPixelSize )
{
setLastError("Unable to load file. Monochrome images must have 8 bits per pixel.");
return FALSE;
}
break;
case 9:
// Colormapped, RLE
break;
case 10:
// Truecolor, RLE
break;
case 11:
// Monochrome, RLE
if( 8 != mPixelSize )
{
setLastError("Unable to load file. Monochrome images must have 8 bits per pixel.");
return FALSE;
}
break;
default:
setLastError("Unable to load file. Unrecoginzed TGA image type.");
return FALSE;
}
// discard the ID field, if any
if (mIDLength)
{
memcpy(junk, getData()+mDataOffset, mIDLength); /* Flawfinder: ignore */
mDataOffset += mIDLength;
}
// check to see if there's a colormap since even rgb files can have them
S32 color_map_bytes = 0;
if( (1 == mColorMapType) && (mColorMapDepth > 0) )
{
mColorMapStart = (S32(mColorMapIndexHi) << 8) + mColorMapIndexLo;
mColorMapLength = (S32(mColorMapLengthHi) << 8) + mColorMapLengthLo;
if( mColorMapDepth > 24 )
{
mColorMapBytesPerEntry = 4;
}
else
if( mColorMapDepth > 16 )
{
mColorMapBytesPerEntry = 3;
}
else
if( mColorMapDepth > 8 )
{
mColorMapBytesPerEntry = 2;
}
else
{
mColorMapBytesPerEntry = 1;
}
color_map_bytes = mColorMapLength * mColorMapBytesPerEntry;
// Note: although it's legal for TGA files to have color maps and not use them
// (some programs actually do this and use the color map for other ends), we'll
// only allocate memory for one if _we_ intend to use it.
if ( (1 == mImageType) || (9 == mImageType) )
{
mColorMap = new U8[ color_map_bytes ];
if (!mColorMap)
{
llerrs << "Out of Memory in BOOL LLImageTGA::updateData()" << llendl;
return FALSE;
}
memcpy( mColorMap, getData() + mDataOffset, color_map_bytes ); /* Flawfinder: ignore */
}
mDataOffset += color_map_bytes;
}
// heights are read as bytes to prevent endian problems
S32 height = (S32(mHeightHi) << 8) + mHeightLo;
S32 width = (S32(mWidthHi) << 8) + mWidthLo;
// make sure that it's a pixel format that we understand
S32 bits_per_pixel;
if( mColorMap )
{
bits_per_pixel = mColorMapDepth;
}
else
{
bits_per_pixel = mPixelSize;
}
S32 components;
switch(bits_per_pixel)
{
case 24:
components = 3;
break;
case 32:
components = 4;
// Don't enforce this. ACDSee doesn't bother to set the attributes bits correctly. Arrgh!
// if( mAttributeBits != 8 )
// {
// setLastError("Unable to load file. 32 bit TGA image does not have 8 bits of alpha.");
// return FALSE;
// }
mAttributeBits = 8;
break;
case 15:
case 16:
components = 3;
mIs15Bit = TRUE; // 16th bit is used for Targa hardware interupts and is ignored.
break;
case 8:
components = 1;
break;
default:
setLastError("Unable to load file. Unknown pixel size.");
return FALSE;
}
setSize(width, height, components);
return TRUE;
}
BOOL LLImageTGA::decode(LLImageRaw* raw_image, F32 decode_time)
{
llassert_always(raw_image);
// Check to make sure that this instance has been initialized with data
if (!getData() || (0 == getDataSize()))
{
setLastError("LLImageTGA trying to decode an image with no data!");
return FALSE;
}
// Copy everything after the header.
raw_image->resize(getWidth(), getHeight(), getComponents());
if( (getComponents() != 1) &&
(getComponents() != 3) &&
(getComponents() != 4) )
{
setLastError("TGA images with a number of components other than 1, 3, and 4 are not supported.");
return FALSE;
}
if( mOriginRightBit )
{
setLastError("TGA images with origin on right side are not supported.");
return FALSE;
}
BOOL flipped = (mOriginTopBit != 0);
BOOL rle_compressed = ((mImageType & 0x08) != 0);
if( mColorMap )
{
return decodeColorMap( raw_image, rle_compressed, flipped );
}
else
{
return decodeTruecolor( raw_image, rle_compressed, flipped );
}
}
BOOL LLImageTGA::decodeTruecolor( LLImageRaw* raw_image, BOOL rle, BOOL flipped )
{
BOOL success = FALSE;
BOOL alpha_opaque = FALSE;
if( rle )
{
switch( getComponents() )
{
case 1:
success = decodeTruecolorRle8( raw_image );
break;
case 3:
if( mIs15Bit )
{
success = decodeTruecolorRle15( raw_image );
}
else
{
success = decodeTruecolorRle24( raw_image );
}
break;
case 4:
success = decodeTruecolorRle32( raw_image, alpha_opaque );
if (alpha_opaque)
{
// alpha was entirely opaque
// convert to 24 bit image
LLPointer<LLImageRaw> compacted_image = new LLImageRaw(raw_image->getWidth(), raw_image->getHeight(), 3);
compacted_image->copy(raw_image);
raw_image->resize(raw_image->getWidth(), raw_image->getHeight(), 3);
raw_image->copy(compacted_image);
}
break;
}
}
else
{
BOOL alpha_opaque;
success = decodeTruecolorNonRle( raw_image, alpha_opaque );
if (alpha_opaque && raw_image->getComponents() == 4)
{
// alpha was entirely opaque
// convert to 24 bit image
LLPointer<LLImageRaw> compacted_image = new LLImageRaw(raw_image->getWidth(), raw_image->getHeight(), 3);
compacted_image->copy(raw_image);
raw_image->resize(raw_image->getWidth(), raw_image->getHeight(), 3);
raw_image->copy(compacted_image);
}
}
if( success && flipped )
{
// This works because the Targa definition requires that RLE blocks never
// encode pixels from more than one scanline.
// (On the other hand, it's not as fast as writing separate flipped versions as
// we did with TruecolorNonRle.)
raw_image->verticalFlip();
}
return success;
}
BOOL LLImageTGA::decodeTruecolorNonRle( LLImageRaw* raw_image, BOOL &alpha_opaque )
{
alpha_opaque = TRUE;
// Origin is the bottom left
U8* dst = raw_image->getData();
U8* src = getData() + mDataOffset;
S32 pixels = getWidth() * getHeight();
if (getComponents() == 4)
{
while( pixels-- )
{
// Our data is stored in RGBA. TGA stores them as BGRA (little-endian ARGB)
dst[0] = src[2]; // Red
dst[1] = src[1]; // Green
dst[2] = src[0]; // Blue
dst[3] = src[3]; // Alpha
if (dst[3] != 255)
{
alpha_opaque = FALSE;
}
dst += 4;
src += 4;
}
}
else if (getComponents() == 3)
{
if( mIs15Bit )
{
while( pixels-- )
{
decodeTruecolorPixel15( dst, src );
dst += 3;
src += 2;
}
}
else
{
while( pixels-- )
{
dst[0] = src[2]; // Red
dst[1] = src[1]; // Green
dst[2] = src[0]; // Blue
dst += 3;
src += 3;
}
}
}
else if (getComponents() == 1)
{
memcpy(dst, src, pixels); /* Flawfinder: ignore */
}
return TRUE;
}
void LLImageTGA::decodeColorMapPixel8( U8* dst, const U8* src )
{
S32 index = llclamp( *src - mColorMapStart, 0, mColorMapLength - 1 );
dst[0] = mColorMap[ index ];
}
void LLImageTGA::decodeColorMapPixel15( U8* dst, const U8* src )
{
S32 index = llclamp( *src - mColorMapStart, 0, mColorMapLength - 1 );
decodeTruecolorPixel15( dst, mColorMap + 2 * index );
}
void LLImageTGA::decodeColorMapPixel24( U8* dst, const U8* src )
{
S32 index = 3 * llclamp( *src - mColorMapStart, 0, mColorMapLength - 1 );
dst[0] = mColorMap[ index + 2 ]; // Red
dst[1] = mColorMap[ index + 1 ]; // Green
dst[2] = mColorMap[ index + 0 ]; // Blue
}
void LLImageTGA::decodeColorMapPixel32( U8* dst, const U8* src )
{
S32 index = 4 * llclamp( *src - mColorMapStart, 0, mColorMapLength - 1 );
dst[0] = mColorMap[ index + 2 ]; // Red
dst[1] = mColorMap[ index + 1 ]; // Green
dst[2] = mColorMap[ index + 0 ]; // Blue
dst[3] = mColorMap[ index + 3 ]; // Alpha
}
BOOL LLImageTGA::decodeColorMap( LLImageRaw* raw_image, BOOL rle, BOOL flipped )
{
// If flipped, origin is the top left. Need to reverse the order of the rows.
// Otherwise the origin is the bottom left.
if( 8 != mPixelSize )
{
return FALSE;
}
U8* src = getData() + mDataOffset;
U8* dst = raw_image->getData(); // start from the top
void (LLImageTGA::*pixel_decoder)( U8*, const U8* );
switch( mColorMapBytesPerEntry )
{
case 1: pixel_decoder = &LLImageTGA::decodeColorMapPixel8; break;
case 2: pixel_decoder = &LLImageTGA::decodeColorMapPixel15; break;
case 3: pixel_decoder = &LLImageTGA::decodeColorMapPixel24; break;
case 4: pixel_decoder = &LLImageTGA::decodeColorMapPixel32; break;
default: llassert(0); return FALSE;
}
if( rle )
{
U8* last_dst = dst + getComponents() * (getHeight() * getWidth() - 1);
while( dst <= last_dst )
{
// Read RLE block header
U8 block_header_byte = *src;
src++;
U8 block_pixel_count = (block_header_byte & 0x7F) + 1;
if( block_header_byte & 0x80 )
{
// Encoded (duplicate-pixel) block
do
{
(this->*pixel_decoder)( dst, src );
dst += getComponents();
block_pixel_count--;
}
while( block_pixel_count > 0 );
src++;
}
else
{
// Unencoded block
do
{
(this->*pixel_decoder)( dst, src );
dst += getComponents();
src++;
block_pixel_count--;
}
while( block_pixel_count > 0 );
}
}
raw_image->verticalFlip();
}
else
{
S32 src_row_bytes = getWidth();
S32 dst_row_bytes = getWidth() * getComponents();
if( flipped )
{
U8* src_last_row_start = src + (getHeight() - 1) * src_row_bytes;
src = src_last_row_start; // start from the bottom
src_row_bytes *= -1;
}
S32 i;
S32 j;
for( S32 row = 0; row < getHeight(); row++ )
{
for( i = 0, j = 0; j < getWidth(); i += getComponents(), j++ )
{
(this->*pixel_decoder)( dst + i, src + j );
}
dst += dst_row_bytes;
src += src_row_bytes;
}
}
return TRUE;
}
BOOL LLImageTGA::encode(const LLImageRaw* raw_image, F32 encode_time)
{
llassert_always(raw_image);
deleteData();
setSize(raw_image->getWidth(), raw_image->getHeight(), raw_image->getComponents());
// Data from header
mIDLength = 0; // Length of identifier string
mColorMapType = 0; // 0 = No Map
// Supported: 2 = Uncompressed true color, 3 = uncompressed monochrome without colormap
switch( getComponents() )
{
case 1:
mImageType = 3;
break;
case 2: // Interpret as intensity plus alpha
case 3:
case 4:
mImageType = 2;
break;
default:
return FALSE;
}
// Color map stuff (unsupported)
mColorMapIndexLo = 0; // First color map entry (low order byte)
mColorMapIndexHi = 0; // First color map entry (high order byte)
mColorMapLengthLo = 0; // Color map length (low order byte)
mColorMapLengthHi = 0; // Color map length (high order byte)
mColorMapDepth = 0; // Size of color map entry (15, 16, 24, or 32 bits)
// Image offset relative to origin.
mXOffsetLo = 0; // X offset from origin (low order byte)
mXOffsetHi = 0; // X offset from origin (hi order byte)
mYOffsetLo = 0; // Y offset from origin (low order byte)
mYOffsetHi = 0; // Y offset from origin (hi order byte)
// Height and width
mWidthLo = U8(getWidth() & 0xFF); // Width (low order byte)
mWidthHi = U8((getWidth() >> 8) & 0xFF); // Width (hi order byte)
mHeightLo = U8(getHeight() & 0xFF); // Height (low order byte)
mHeightHi = U8((getHeight() >> 8) & 0xFF); // Height (hi order byte)
S32 bytes_per_pixel;
switch( getComponents() )
{
case 1:
bytes_per_pixel = 1;
break;
case 3:
bytes_per_pixel = 3;
break;
case 2: // Interpret as intensity plus alpha. Store as RGBA.
case 4:
bytes_per_pixel = 4;
break;
default:
return FALSE;
}
mPixelSize = U8(bytes_per_pixel * 8); // 8, 16, 24, 32 bits per pixel
mAttributeBits = (4 == bytes_per_pixel) ? 8 : 0; // 4 bits: number of attribute bits (alpha) per pixel
mOriginRightBit = 0; // 1 bit: origin, 0 = left, 1 = right
mOriginTopBit = 0; // 1 bit: origin, 0 = bottom, 1 = top
mInterleave = 0; // 2 bits: interleaved flag, 0 = none, 1 = interleaved 2, 2 = interleaved 4
const S32 TGA_HEADER_SIZE = 18;
const S32 COLOR_MAP_SIZE = 0;
mDataOffset = TGA_HEADER_SIZE + mIDLength + COLOR_MAP_SIZE; // Offset from start of data to the actual header.
S32 pixels = getWidth() * getHeight();
S32 datasize = mDataOffset + bytes_per_pixel * pixels;
U8* dst = allocateData(datasize);
// Write header
*(dst++) = mIDLength;
*(dst++) = mColorMapType;
*(dst++) = mImageType;
*(dst++) = mColorMapIndexLo;
*(dst++) = mColorMapIndexHi;
*(dst++) = mColorMapLengthLo;
*(dst++) = mColorMapLengthHi;
*(dst++) = mColorMapDepth;
*(dst++) = mXOffsetLo;
*(dst++) = mXOffsetHi;
*(dst++) = mYOffsetLo;
*(dst++) = mYOffsetHi;
*(dst++) = mWidthLo;
*(dst++) = mWidthHi;
*(dst++) = mHeightLo;
*(dst++) = mHeightHi;
*(dst++) = mPixelSize;
*(dst++) =
((mInterleave & 3) << 5) |
((mOriginTopBit & 1) << 4) |
((mOriginRightBit & 1) << 3) |
((mAttributeBits & 0xF) << 0);
// Write pixels
const U8* src = raw_image->getData();
llassert( dst == getData() + mDataOffset );
S32 i = 0;
S32 j = 0;
switch( getComponents() )
{
case 1:
memcpy( dst, src, bytes_per_pixel * pixels ); /* Flawfinder: ignore */
break;
case 2:
while( pixels-- )
{
dst[i + 0] = src[j + 0]; // intensity
dst[i + 1] = src[j + 0]; // intensity
dst[i + 2] = src[j + 0]; // intensity
dst[i + 3] = src[j + 1]; // alpha
i += 4;
j += 2;
}
break;
case 3:
while( pixels-- )
{
dst[i + 0] = src[i + 2]; // blue
dst[i + 1] = src[i + 1]; // green
dst[i + 2] = src[i + 0]; // red
i += 3;
}
break;
case 4:
while( pixels-- )
{
dst[i + 0] = src[i + 2]; // blue
dst[i + 1] = src[i + 1]; // green
dst[i + 2] = src[i + 0]; // red
dst[i + 3] = src[i + 3]; // alpha
i += 4;
}
break;
}
return TRUE;
}
BOOL LLImageTGA::decodeTruecolorRle32( LLImageRaw* raw_image, BOOL &alpha_opaque )
{
llassert( getComponents() == 4 );
alpha_opaque = TRUE;
U8* dst = raw_image->getData();
U32* dst_pixels = (U32*) dst;
U8* src = getData() + mDataOffset;
U8* last_src = src + getDataSize();
U32 rgba;
U8* rgba_byte_p = (U8*) &rgba;
U32* last_dst_pixel = dst_pixels + getHeight() * getWidth() - 1;
while( dst_pixels <= last_dst_pixel )
{
// Read RLE block header
if (src >= last_src)
return FALSE;
U8 block_header_byte = *src;
src++;
U32 block_pixel_count = (block_header_byte & 0x7F) + 1;
if( block_header_byte & 0x80 )
{
// Encoded (duplicate-pixel) block
if (src + 3 >= last_src)
return FALSE;
rgba_byte_p[0] = src[2];
rgba_byte_p[1] = src[1];
rgba_byte_p[2] = src[0];
rgba_byte_p[3] = src[3];
if (rgba_byte_p[3] != 255)
{
alpha_opaque = FALSE;
}
src += 4;
register U32 value = rgba;
do
{
*dst_pixels = value;
dst_pixels++;
block_pixel_count--;
}
while( block_pixel_count > 0 );
}
else
{
// Unencoded block
do
{
if (src + 3 >= last_src)
return FALSE;
((U8*)dst_pixels)[0] = src[2];
((U8*)dst_pixels)[1] = src[1];
((U8*)dst_pixels)[2] = src[0];
((U8*)dst_pixels)[3] = src[3];
if (src[3] != 255)
{
alpha_opaque = FALSE;
}
src += 4;
dst_pixels++;
block_pixel_count--;
}
while( block_pixel_count > 0 );
}
}
return TRUE;
}
BOOL LLImageTGA::decodeTruecolorRle15( LLImageRaw* raw_image )
{
llassert( getComponents() == 3 );
llassert( mIs15Bit );
U8* dst = raw_image->getData();
U8* src = getData() + mDataOffset;
U8* last_src = src + getDataSize();
U8* last_dst = dst + getComponents() * (getHeight() * getWidth() - 1);
while( dst <= last_dst )
{
// Read RLE block header
if (src >= last_src)
return FALSE;
U8 block_header_byte = *src;
src++;
U8 block_pixel_count = (block_header_byte & 0x7F) + 1;
if( block_header_byte & 0x80 )
{
// Encoded (duplicate-pixel) block
do
{
if (src + 2 >= last_src)
return FALSE;
decodeTruecolorPixel15( dst, src ); // slow
dst += 3;
block_pixel_count--;
}
while( block_pixel_count > 0 );
src += 2;
}
else
{
// Unencoded block
do
{
if (src + 2 >= last_src)
return FALSE;
decodeTruecolorPixel15( dst, src );
dst += 3;
src += 2;
block_pixel_count--;
}
while( block_pixel_count > 0 );
}
}
return TRUE;
}
BOOL LLImageTGA::decodeTruecolorRle24( LLImageRaw* raw_image )
{
llassert( getComponents() == 3 );
U8* dst = raw_image->getData();
U8* src = getData() + mDataOffset;
U8* last_src = src + getDataSize();
U8* last_dst = dst + getComponents() * (getHeight() * getWidth() - 1);
while( dst <= last_dst )
{
// Read RLE block header
if (src >= last_src)
return FALSE;
U8 block_header_byte = *src;
src++;
U8 block_pixel_count = (block_header_byte & 0x7F) + 1;
if( block_header_byte & 0x80 )
{
// Encoded (duplicate-pixel) block
do
{
if (src + 2 >= last_src)
return FALSE;
dst[0] = src[2];
dst[1] = src[1];
dst[2] = src[0];
dst += 3;
block_pixel_count--;
}
while( block_pixel_count > 0 );
src += 3;
}
else
{
// Unencoded block
do
{
if (src + 2 >= last_src)
return FALSE;
dst[0] = src[2];
dst[1] = src[1];
dst[2] = src[0];
dst += 3;
src += 3;
block_pixel_count--;
}
while( block_pixel_count > 0 );
}
}
return TRUE;
}
BOOL LLImageTGA::decodeTruecolorRle8( LLImageRaw* raw_image )
{
llassert( getComponents() == 1 );
U8* dst = raw_image->getData();
U8* src = getData() + mDataOffset;
U8* last_src = src + getDataSize();
U8* last_dst = dst + getHeight() * getWidth() - 1;
while( dst <= last_dst )
{
// Read RLE block header
if (src >= last_src)
return FALSE;
U8 block_header_byte = *src;
src++;
U8 block_pixel_count = (block_header_byte & 0x7F) + 1;
if( block_header_byte & 0x80 )
{
if (src >= last_src)
return FALSE;
// Encoded (duplicate-pixel) block
memset( dst, *src, block_pixel_count );
dst += block_pixel_count;
src++;
}
else
{
// Unencoded block
do
{
if (src >= last_src)
return FALSE;
*dst = *src;
dst++;
src++;
block_pixel_count--;
}
while( block_pixel_count > 0 );
}
}
return TRUE;
}
// Decoded and process the image for use in avatar gradient masks.
// Processing happens during the decode for speed.
BOOL LLImageTGA::decodeAndProcess( LLImageRaw* raw_image, F32 domain, F32 weight )
{
llassert_always(raw_image);
// "Domain" isn't really the right word. It refers to the width of the
// ramp portion of the function that relates input and output pixel values.
// A domain of 0 gives a step function.
//
// | /----------------
// O| / |
// u| / |
// t| / |
// p|------------------/ |
// u| | |
// t|<---------------->|<-->|
// | "offset" "domain"
// |
// --+---Input--------------------------------
// |
if (!getData() || (0 == getDataSize()))
{
setLastError("LLImageTGA trying to decode an image with no data!");
return FALSE;
}
// Only works for unflipped monochrome RLE images
if( (getComponents() != 1) || (mImageType != 11) || mOriginTopBit || mOriginRightBit )
{
llerrs << "LLImageTGA trying to alpha-gradient process an image that's not a standard RLE, one component image" << llendl;
return FALSE;
}
raw_image->resize(getWidth(), getHeight(), getComponents());
U8* dst = raw_image->getData();
U8* src = getData() + mDataOffset;
U8* last_dst = dst + getHeight() * getWidth() - 1;
if( domain > 0 )
{
// Process using a look-up table (lut)
const S32 LUT_LEN = 256;
U8 lut[LUT_LEN];
S32 i;
F32 scale = 1.f / domain;
F32 offset = (1.f - domain) * llclampf( 1.f - weight );
F32 bias = -(scale * offset);
for( i = 0; i < LUT_LEN; i++ )
{
lut[i] = (U8)llclampb( 255.f * ( i/255.f * scale + bias ) );
}
while( dst <= last_dst )
{
// Read RLE block header
U8 block_header_byte = *src;
src++;
U8 block_pixel_count = (block_header_byte & 0x7F) + 1;
if( block_header_byte & 0x80 )
{
// Encoded (duplicate-pixel) block
memset( dst, lut[ *src ], block_pixel_count );
dst += block_pixel_count;
src++;
}
else
{
// Unencoded block
do
{
*dst = lut[ *src ];
dst++;
src++;
block_pixel_count--;
}
while( block_pixel_count > 0 );
}
}
}
else
{
// Process using a simple comparison agains a threshold
const U8 threshold = (U8)(0xFF * llclampf( 1.f - weight ));
while( dst <= last_dst )
{
// Read RLE block header
U8 block_header_byte = *src;
src++;
U8 block_pixel_count = (block_header_byte & 0x7F) + 1;
if( block_header_byte & 0x80 )
{
// Encoded (duplicate-pixel) block
memset( dst, ((*src >= threshold) ? 0xFF : 0), block_pixel_count );
dst += block_pixel_count;
src++;
}
else
{
// Unencoded block
do
{
*dst = (*src >= threshold) ? 0xFF : 0;
dst++;
src++;
block_pixel_count--;
}
while( block_pixel_count > 0 );
}
}
}
return TRUE;
}
// Reads a .tga file and creates an LLImageTGA with its data.
bool LLImageTGA::loadFile( const std::string& path )
{
S32 len = path.size();
if( len < 5 )
{
return false;
}
std::string extension = gDirUtilp->getExtension(path);
if( "tga" != extension )
{
return false;
}
LLFILE* file = LLFile::fopen(path, "rb"); /* Flawfinder: ignore */
if( !file )
{
llwarns << "Couldn't open file " << path << llendl;
return false;
}
S32 file_size = 0;
if (!fseek(file, 0, SEEK_END))
{
file_size = ftell(file);
fseek(file, 0, SEEK_SET);
}
U8* buffer = allocateData(file_size);
S32 bytes_read = fread(buffer, 1, file_size, file);
if( bytes_read != file_size )
{
deleteData();
llwarns << "Couldn't read file " << path << llendl;
return false;
}
fclose( file );
if( !updateData() )
{
llwarns << "Couldn't decode file " << path << llendl;
deleteData();
return false;
}
return true;
}