diff options
Diffstat (limited to 'indra/llimage/llimagetga.cpp')
-rw-r--r-- | indra/llimage/llimagetga.cpp | 2456 |
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; +} + + |