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 0201cc6413..1fa5ce5a84 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;
+}
+
+
|