summaryrefslogtreecommitdiff
path: root/indra/llimage/llimagetga.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'indra/llimage/llimagetga.cpp')
-rw-r--r--indra/llimage/llimagetga.cpp2456
1 files changed, 1228 insertions, 1228 deletions
diff --git a/indra/llimage/llimagetga.cpp b/indra/llimage/llimagetga.cpp
index 1fa5ce5a84..b168f343e7 100644
--- a/indra/llimage/llimagetga.cpp
+++ b/indra/llimage/llimagetga.cpp
@@ -1,1228 +1,1228 @@
-/**
- * @file llimagetga.cpp
- *
- * $LicenseInfo:firstyear=2001&license=viewerlgpl$
- * Second Life Viewer Source Code
- * Copyright (C) 2010, Linden Research, Inc.
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation;
- * version 2.1 of the License only.
- *
- * This library 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
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
- *
- * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
- * $/LicenseInfo$
- */
-
-#include "linden_common.h"
-
-#include "llimagetga.h"
-
-#include "lldir.h"
-#include "llerror.h"
-#include "llmath.h"
-#include "llpointer.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();
-
- LLImageDataLock lock(this);
-
- // 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
- ** = (0001) 1 UNCOMPRESSED, COLOR-MAPPED IMAGE
- ** = (0010) 2 UNCOMPRESSED, TRUE-COLOR IMAGE
- ** = (0011) 3 UNCOMPRESSED, BLACK AND WHITE IMAGE
- ** = (1001) 9 RUN-LENGTH ENCODED COLOR-MAPPED IMAGE
- ** = (1010) 10 RUN-LENGTH ENCODED TRUE-COLOR IMAGE
- ** = (1011) 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(std::nothrow) U8[ color_map_bytes ];
- if (!mColorMap)
- {
- LLError::LLUserWarningMsg::showOutOfMemory();
- LL_ERRS() << "Out of Memory in bool LLImageTGA::updateData()" << LL_ENDL;
- 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);
-
- LLImageDataSharedLock lockIn(this);
- LLImageDataLock lockOut(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.
-
- if( !raw_image->resize(getWidth(), getHeight(), getComponents()))
- {
- setLastError("LLImageTGA::out of memory");
- return false;
- }
-
- 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( raw_image->isBufferInvalid())
- {
- setLastError("LLImageTGA::out of memory");
- 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);
- if (compacted_image->isBufferInvalid())
- {
- success = false;
- break;
- }
- 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);
- if (compacted_image->isBufferInvalid())
- {
- success = false;
- }
- else
- {
- 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 (pixels * (mIs15Bit ? 2 : getComponents()) > getDataSize() - mDataOffset)
- { //here we have situation when data size in src less than actually needed
- return false;
- }
-
- 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);
-
- LLImageDataSharedLock lockIn(raw_image);
- LLImageDataLock lockOut(this);
-
- 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;
- 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--------------------------------
- // |
-
- LLImageDataSharedLock lockIn(this);
- LLImageDataLock lockOut(raw_image);
-
- 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 )
- {
- LL_ERRS() << "LLImageTGA trying to alpha-gradient process an image that's not a standard RLE, one component image" << LL_ENDL;
- return false;
- }
-
- if( !raw_image->resize(getWidth(), getHeight(), getComponents()) )
- {
- LL_ERRS() << "LLImageTGA: Failed to resize image" << LL_ENDL;
- return false;
- }
-
- 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 )
- {
- LL_WARNS() << "Couldn't open file " << path << LL_ENDL;
- 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();
- LL_WARNS() << "Couldn't read file " << path << LL_ENDL;
- return false;
- }
-
- fclose( file );
-
- if( !updateData() )
- {
- LL_WARNS() << "Couldn't decode file " << path << LL_ENDL;
- deleteData();
- return false;
- }
- return true;
-}
-
-
+/**
+ * @file llimagetga.cpp
+ *
+ * $LicenseInfo:firstyear=2001&license=viewerlgpl$
+ * Second Life Viewer Source Code
+ * Copyright (C) 2010, Linden Research, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
+ * $/LicenseInfo$
+ */
+
+#include "linden_common.h"
+
+#include "llimagetga.h"
+
+#include "lldir.h"
+#include "llerror.h"
+#include "llmath.h"
+#include "llpointer.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();
+
+ LLImageDataLock lock(this);
+
+ // 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
+ ** = (0001) 1 UNCOMPRESSED, COLOR-MAPPED IMAGE
+ ** = (0010) 2 UNCOMPRESSED, TRUE-COLOR IMAGE
+ ** = (0011) 3 UNCOMPRESSED, BLACK AND WHITE IMAGE
+ ** = (1001) 9 RUN-LENGTH ENCODED COLOR-MAPPED IMAGE
+ ** = (1010) 10 RUN-LENGTH ENCODED TRUE-COLOR IMAGE
+ ** = (1011) 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(std::nothrow) U8[ color_map_bytes ];
+ if (!mColorMap)
+ {
+ LLError::LLUserWarningMsg::showOutOfMemory();
+ LL_ERRS() << "Out of Memory in bool LLImageTGA::updateData()" << LL_ENDL;
+ 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);
+
+ LLImageDataSharedLock lockIn(this);
+ LLImageDataLock lockOut(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.
+
+ if( !raw_image->resize(getWidth(), getHeight(), getComponents()))
+ {
+ setLastError("LLImageTGA::out of memory");
+ return false;
+ }
+
+ 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( raw_image->isBufferInvalid())
+ {
+ setLastError("LLImageTGA::out of memory");
+ 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);
+ if (compacted_image->isBufferInvalid())
+ {
+ success = false;
+ break;
+ }
+ 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);
+ if (compacted_image->isBufferInvalid())
+ {
+ success = false;
+ }
+ else
+ {
+ 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 (pixels * (mIs15Bit ? 2 : getComponents()) > getDataSize() - mDataOffset)
+ { //here we have situation when data size in src less than actually needed
+ return false;
+ }
+
+ 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);
+
+ LLImageDataSharedLock lockIn(raw_image);
+ LLImageDataLock lockOut(this);
+
+ 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;
+ 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--------------------------------
+ // |
+
+ LLImageDataSharedLock lockIn(this);
+ LLImageDataLock lockOut(raw_image);
+
+ 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 )
+ {
+ LL_ERRS() << "LLImageTGA trying to alpha-gradient process an image that's not a standard RLE, one component image" << LL_ENDL;
+ return false;
+ }
+
+ if( !raw_image->resize(getWidth(), getHeight(), getComponents()) )
+ {
+ LL_ERRS() << "LLImageTGA: Failed to resize image" << LL_ENDL;
+ return false;
+ }
+
+ 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 )
+ {
+ LL_WARNS() << "Couldn't open file " << path << LL_ENDL;
+ 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();
+ LL_WARNS() << "Couldn't read file " << path << LL_ENDL;
+ return false;
+ }
+
+ fclose( file );
+
+ if( !updateData() )
+ {
+ LL_WARNS() << "Couldn't decode file " << path << LL_ENDL;
+ deleteData();
+ return false;
+ }
+ return true;
+}
+
+