Imported existing code

This commit is contained in:
Hazim Gazov
2010-04-02 02:48:44 -03:00
parent 48fbc5ae91
commit 7a86d01598
13996 changed files with 2468699 additions and 0 deletions

View File

@@ -0,0 +1,65 @@
# -*- cmake -*-
project(llimage)
include(00-Common)
include(LLAddBuildTest)
include(LLCommon)
include(LLImage)
include(LLMath)
include(LLVFS)
include(ZLIB)
include_directories(
${LLCOMMON_INCLUDE_DIRS}
${LLMATH_INCLUDE_DIRS}
${LLVFS_INCLUDE_DIRS}
${PNG_INCLUDE_DIRS}
${ZLIB_INCLUDE_DIRS}
)
set(llimage_SOURCE_FILES
llimagebmp.cpp
llimage.cpp
llimagedxt.cpp
llimagej2c.cpp
llimagejpeg.cpp
llimagepng.cpp
llimagetga.cpp
llimageworker.cpp
llpngwrapper.cpp
)
set(llimage_HEADER_FILES
CMakeLists.txt
llimage.h
llimagebmp.h
llimagedxt.h
llimagej2c.h
llimagejpeg.h
llimagepng.h
llimagetga.h
llimageworker.h
llmapimagetype.h
llpngwrapper.h
)
set_source_files_properties(${llimage_HEADER_FILES}
PROPERTIES HEADER_FILE_ONLY TRUE)
list(APPEND llimage_SOURCE_FILES ${llimage_HEADER_FILES})
add_library (llimage ${llimage_SOURCE_FILES})
add_dependencies(llimage prepare)
target_link_libraries(
llimage
${JPEG_LIBRARIES}
${PNG_LIBRARIES}
${ZLIB_LIBRARIES}
)
# Add tests
if (NOT STANDALONE)
ADD_BUILD_TEST(llimageworker llimage)
endif (NOT STANDALONE)

1703
indra/llimage/llimage.cpp Normal file

File diff suppressed because it is too large Load Diff

319
indra/llimage/llimage.h Normal file
View File

@@ -0,0 +1,319 @@
/**
* @file llimage.h
* @brief Object for managing images and their textures.
*
* $LicenseInfo:firstyear=2000&license=viewergpl$
*
* Copyright (c) 2000-2009, Linden Research, Inc.
*
* Second Life Viewer Source Code
* The source code in this file ("Source Code") is provided by Linden Lab
* to you under the terms of the GNU General Public License, version 2.0
* ("GPL"), unless you have obtained a separate licensing agreement
* ("Other License"), formally executed by you and Linden Lab. Terms of
* the GPL can be found in doc/GPL-license.txt in this distribution, or
* online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
*
* There are special exceptions to the terms and conditions of the GPL as
* it is applied to this Source Code. View the full text of the exception
* in the file doc/FLOSS-exception.txt in this software distribution, or
* online at
* http://secondlifegrid.net/programs/open_source/licensing/flossexception
*
* By copying, modifying or distributing this software, you acknowledge
* that you have read and understood your obligations described above,
* and agree to abide by those obligations.
*
* ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
* WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
* COMPLETENESS OR PERFORMANCE.
* $/LicenseInfo$
*/
#ifndef LL_LLIMAGE_H
#define LL_LLIMAGE_H
#include "lluuid.h"
#include "llstring.h"
#include "llmemory.h"
#include "llthread.h"
const S32 MIN_IMAGE_MIP = 2; // 4x4, only used for expand/contract power of 2
const S32 MAX_IMAGE_MIP = 11; // 2048x2048
const S32 MAX_DISCARD_LEVEL = 5;
const S32 MIN_IMAGE_SIZE = (1<<MIN_IMAGE_MIP); // 4, only used for expand/contract power of 2
const S32 MAX_IMAGE_SIZE = (1<<MAX_IMAGE_MIP); // 2048
const S32 MIN_IMAGE_AREA = MIN_IMAGE_SIZE * MIN_IMAGE_SIZE;
const S32 MAX_IMAGE_AREA = MAX_IMAGE_SIZE * MAX_IMAGE_SIZE;
const S32 MAX_IMAGE_COMPONENTS = 8;
const S32 MAX_IMAGE_DATA_SIZE = MAX_IMAGE_AREA * MAX_IMAGE_COMPONENTS;
// Note! These CANNOT be changed without modifying simulator code
// *TODO: change both to 1024 when SIM texture fetching is deprecated
const S32 FIRST_PACKET_SIZE = 600;
const S32 MAX_IMG_PACKET_SIZE = 1000;
// Base classes for images.
// There are two major parts for the image:
// The compressed representation, and the decompressed representation.
class LLImageFormatted;
class LLImageRaw;
class LLColor4U;
typedef enum e_image_codec
{
IMG_CODEC_INVALID = 0,
IMG_CODEC_RGB = 1,
IMG_CODEC_J2C = 2,
IMG_CODEC_BMP = 3,
IMG_CODEC_TGA = 4,
IMG_CODEC_JPEG = 5,
IMG_CODEC_DXT = 6,
IMG_CODEC_PNG = 7,
IMG_CODEC_EOF = 8
} EImageCodec;
//============================================================================
// library initialization class
class LLImage
{
public:
static void initClass();
static void cleanupClass();
static const std::string& getLastError();
static void setLastError(const std::string& message);
protected:
static LLMutex* sMutex;
static std::string sLastErrorMessage;
};
//============================================================================
// Image base class
class LLImageBase : public LLThreadSafeRefCount
{
protected:
virtual ~LLImageBase();
public:
LLImageBase();
enum
{
TYPE_NORMAL = 0,
TYPE_AVATAR_BAKE = 1,
};
virtual void deleteData();
virtual U8* allocateData(S32 size = -1);
virtual U8* reallocateData(S32 size = -1);
virtual void dump();
virtual void sanityCheck();
U16 getWidth() const { return mWidth; }
U16 getHeight() const { return mHeight; }
S8 getComponents() const { return mComponents; }
S32 getDataSize() const { return mDataSize; }
const U8 *getData() const ;
U8 *getData() ;
BOOL isBufferInvalid() ;
void setSize(S32 width, S32 height, S32 ncomponents);
U8* allocateDataSize(S32 width, S32 height, S32 ncomponents, S32 size = -1); // setSize() + allocateData()
protected:
// special accessor to allow direct setting of mData and mDataSize by LLImageFormatted
void setDataAndSize(U8 *data, S32 size) { mData = data; mDataSize = size; };
public:
static void generateMip(const U8 *indata, U8* mipdata, int width, int height, S32 nchannels);
// Function for calculating the download priority for textures
// <= 0 priority means that there's no need for more data.
static F32 calc_download_priority(F32 virtual_size, F32 visible_area, S32 bytes_sent);
static void setSizeOverride(BOOL enabled) { sSizeOverride = enabled; }
static EImageCodec getCodecFromExtension(const std::string& exten);
private:
U8 *mData;
S32 mDataSize;
U16 mWidth;
U16 mHeight;
S8 mComponents;
BOOL mBadBufferAllocation ;
public:
S16 mMemType; // debug
static BOOL sSizeOverride;
};
// Raw representation of an image (used for textures, and other uncompressed formats
class LLImageRaw : public LLImageBase
{
protected:
/*virtual*/ ~LLImageRaw();
public:
LLImageRaw();
LLImageRaw(U16 width, U16 height, S8 components);
LLImageRaw(U8 *data, U16 width, U16 height, S8 components);
// Construct using createFromFile (used by tools)
LLImageRaw(const std::string& filename, bool j2c_lowest_mip_only = false);
/*virtual*/ void deleteData();
/*virtual*/ U8* allocateData(S32 size = -1);
/*virtual*/ U8* reallocateData(S32 size);
BOOL resize(U16 width, U16 height, S8 components);
U8 * getSubImage(U32 x_pos, U32 y_pos, U32 width, U32 height) const;
BOOL setSubImage(U32 x_pos, U32 y_pos, U32 width, U32 height,
const U8 *data, U32 stride = 0, BOOL reverse_y = FALSE);
void clear(U8 r=0, U8 g=0, U8 b=0, U8 a=255);
void verticalFlip();
void expandToPowerOfTwo(S32 max_dim = MAX_IMAGE_SIZE, BOOL scale_image = TRUE);
void contractToPowerOfTwo(S32 max_dim = MAX_IMAGE_SIZE, BOOL scale_image = TRUE);
void biasedScaleToPowerOfTwo(S32 max_dim = MAX_IMAGE_SIZE);
BOOL scale( S32 new_width, S32 new_height, BOOL scale_image = TRUE );
// Fill the buffer with a constant color
void fill( const LLColor4U& color );
// Copy operations
// Src and dst can be any size. Src and dst can each have 3 or 4 components.
void copy( LLImageRaw* src );
// Src and dst are same size. Src and dst have same number of components.
void copyUnscaled( LLImageRaw* src );
// Src and dst are same size. Src has 4 components. Dst has 3 components.
void copyUnscaled4onto3( LLImageRaw* src );
// Src and dst are same size. Src has 3 components. Dst has 4 components.
void copyUnscaled3onto4( LLImageRaw* src );
// Src and dst can be any size. Src and dst have same number of components.
void copyScaled( LLImageRaw* src );
// Src and dst can be any size. Src has 3 components. Dst has 4 components.
void copyScaled3onto4( LLImageRaw* src );
// Src and dst can be any size. Src has 4 components. Dst has 3 components.
void copyScaled4onto3( LLImageRaw* src );
// Composite operations
// Src and dst can be any size. Src and dst can each have 3 or 4 components.
void composite( LLImageRaw* src );
// Src and dst can be any size. Src has 4 components. Dst has 3 components.
void compositeScaled4onto3( LLImageRaw* src );
// Src and dst are same size. Src has 4 components. Dst has 3 components.
void compositeUnscaled4onto3( LLImageRaw* src );
protected:
// Create an image from a local file (generally used in tools)
bool createFromFile(const std::string& filename, bool j2c_lowest_mip_only = false);
void copyLineScaled( U8* in, U8* out, S32 in_pixel_len, S32 out_pixel_len, S32 in_pixel_step, S32 out_pixel_step );
void compositeRowScaled4onto3( U8* in, U8* out, S32 in_pixel_len, S32 out_pixel_len );
U8 fastFractionalMult(U8 a,U8 b);
public:
static S32 sGlobalRawMemory;
static S32 sRawImageCount;
};
// Compressed representation of image.
// Subclass from this class for the different representations (J2C, bmp)
class LLImageFormatted : public LLImageBase
{
public:
static LLImageFormatted* createFromType(S8 codec);
static LLImageFormatted* createFromExtension(const std::string& instring);
protected:
/*virtual*/ ~LLImageFormatted();
public:
LLImageFormatted(S8 codec);
// LLImageBase
/*virtual*/ void deleteData();
/*virtual*/ U8* allocateData(S32 size = -1);
/*virtual*/ U8* reallocateData(S32 size);
/*virtual*/ void dump();
/*virtual*/ void sanityCheck();
// New methods
// subclasses must return a prefered file extension (lowercase without a leading dot)
virtual std::string getExtension() = 0;
// calcHeaderSize() returns the maximum size of header;
// 0 indicates we don't know have a header and have to lead the entire file
virtual S32 calcHeaderSize() { return 0; };
// calcDataSize() returns how many bytes to read to load discard_level (including header)
virtual S32 calcDataSize(S32 discard_level);
// calcDiscardLevelBytes() returns the smallest valid discard level based on the number of input bytes
virtual S32 calcDiscardLevelBytes(S32 bytes);
// getRawDiscardLevel()by default returns mDiscardLevel, but may be overridden (LLImageJ2C)
virtual S8 getRawDiscardLevel() { return mDiscardLevel; }
BOOL load(const std::string& filename);
BOOL save(const std::string& filename);
virtual BOOL updateData() = 0; // pure virtual
void setData(U8 *data, S32 size);
void appendData(U8 *data, S32 size);
// Loads first 4 channels.
virtual BOOL decode(LLImageRaw* raw_image, F32 decode_time) = 0;
// Subclasses that can handle more than 4 channels should override this function.
virtual BOOL decodeChannels(LLImageRaw* raw_image, F32 decode_time, S32 first_channel, S32 max_channel);
virtual BOOL encode(const LLImageRaw* raw_image, F32 encode_time) = 0;
S8 getCodec() const;
BOOL isDecoding() const { return mDecoding ? TRUE : FALSE; }
BOOL isDecoded() const { return mDecoded ? TRUE : FALSE; }
void setDiscardLevel(S8 discard_level) { mDiscardLevel = discard_level; }
S8 getDiscardLevel() const { return mDiscardLevel; }
// setLastError needs to be deferred for J2C images since it may be called from a DLL
virtual void resetLastError();
virtual void setLastError(const std::string& message, const std::string& filename = std::string());
protected:
BOOL copyData(U8 *data, S32 size); // calls updateData()
protected:
S8 mCodec;
S8 mDecoding;
S8 mDecoded; // unused, but changing LLImage layout requires recompiling static Mac/Linux libs. 2009-01-30 JC
S8 mDiscardLevel;
public:
static S32 sGlobalFormattedMemory;
};
#endif

View File

@@ -0,0 +1,659 @@
/**
* @file llimagebmp.cpp
*
* $LicenseInfo:firstyear=2001&license=viewergpl$
*
* Copyright (c) 2001-2009, Linden Research, Inc.
*
* Second Life Viewer Source Code
* The source code in this file ("Source Code") is provided by Linden Lab
* to you under the terms of the GNU General Public License, version 2.0
* ("GPL"), unless you have obtained a separate licensing agreement
* ("Other License"), formally executed by you and Linden Lab. Terms of
* the GPL can be found in doc/GPL-license.txt in this distribution, or
* online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
*
* There are special exceptions to the terms and conditions of the GPL as
* it is applied to this Source Code. View the full text of the exception
* in the file doc/FLOSS-exception.txt in this software distribution, or
* online at
* http://secondlifegrid.net/programs/open_source/licensing/flossexception
*
* By copying, modifying or distributing this software, you acknowledge
* that you have read and understood your obligations described above,
* and agree to abide by those obligations.
*
* ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
* WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
* COMPLETENESS OR PERFORMANCE.
* $/LicenseInfo$
*/
#include "linden_common.h"
#include "llimagebmp.h"
#include "llerror.h"
#include "llendianswizzle.h"
/**
* @struct LLBMPHeader
*
* This struct helps deal with bmp files.
*/
struct LLBMPHeader
{
S32 mSize;
S32 mWidth;
S32 mHeight;
S16 mPlanes;
S16 mBitsPerPixel;
S16 mCompression;
S16 mAlignmentPadding; // pads out to next word boundary
S32 mImageSize;
S32 mHorzPelsPerMeter;
S32 mVertPelsPerMeter;
S32 mNumColors;
S32 mNumColorsImportant;
};
/**
* @struct Win95BmpHeaderExtension
*/
struct Win95BmpHeaderExtension
{
U32 mReadMask;
U32 mGreenMask;
U32 mBlueMask;
U32 mAlphaMask;
U32 mColorSpaceType;
U16 mRed[3]; // Red CIE endpoint
U16 mGreen[3]; // Green CIE endpoint
U16 mBlue[3]; // Blue CIE endpoint
U32 mGamma[3]; // Gamma scale for r g and b
};
/**
* LLImageBMP
*/
LLImageBMP::LLImageBMP()
:
LLImageFormatted(IMG_CODEC_BMP),
mColorPaletteColors( 0 ),
mColorPalette( NULL ),
mBitmapOffset( 0 ),
mBitsPerPixel( 0 ),
mOriginAtTop( FALSE )
{
mBitfieldMask[0] = 0;
mBitfieldMask[1] = 0;
mBitfieldMask[2] = 0;
mBitfieldMask[3] = 0;
}
LLImageBMP::~LLImageBMP()
{
delete[] mColorPalette;
}
BOOL LLImageBMP::updateData()
{
resetLastError();
// Check to make sure that this instance has been initialized with data
U8* mdata = getData();
if (!mdata || (0 == getDataSize()))
{
setLastError("Uninitialized instance of LLImageBMP");
return FALSE;
}
// Read the bitmap headers in order to get all the useful info
// about this image
////////////////////////////////////////////////////////////////////
// Part 1: "File Header"
// 14 bytes consisting of
// 2 bytes: either BM or BA
// 4 bytes: file size in bytes
// 4 bytes: reserved (always 0)
// 4 bytes: bitmap offset (starting position of image data in bytes)
const S32 FILE_HEADER_SIZE = 14;
if ((mdata[0] != 'B') || (mdata[1] != 'M'))
{
if ((mdata[0] != 'B') || (mdata[1] != 'A'))
{
setLastError("OS/2 bitmap array BMP files are not supported");
return FALSE;
}
else
{
setLastError("Does not appear to be a bitmap file");
return FALSE;
}
}
mBitmapOffset = mdata[13];
mBitmapOffset <<= 8; mBitmapOffset += mdata[12];
mBitmapOffset <<= 8; mBitmapOffset += mdata[11];
mBitmapOffset <<= 8; mBitmapOffset += mdata[10];
////////////////////////////////////////////////////////////////////
// Part 2: "Bitmap Header"
const S32 BITMAP_HEADER_SIZE = 40;
LLBMPHeader header;
llassert( sizeof( header ) == BITMAP_HEADER_SIZE );
memcpy( /* Flawfinder: ignore */
(void*)&header,
mdata + FILE_HEADER_SIZE,
BITMAP_HEADER_SIZE);
// convert BMP header from little endian (no-op on little endian builds)
llendianswizzleone(header.mSize);
llendianswizzleone(header.mWidth);
llendianswizzleone(header.mHeight);
llendianswizzleone(header.mPlanes);
llendianswizzleone(header.mBitsPerPixel);
llendianswizzleone(header.mCompression);
llendianswizzleone(header.mAlignmentPadding);
llendianswizzleone(header.mImageSize);
llendianswizzleone(header.mHorzPelsPerMeter);
llendianswizzleone(header.mVertPelsPerMeter);
llendianswizzleone(header.mNumColors);
llendianswizzleone(header.mNumColorsImportant);
BOOL windows_nt_version = FALSE;
BOOL windows_95_version = FALSE;
if( 12 == header.mSize )
{
setLastError("Windows 2.x and OS/2 1.x BMP files are not supported");
return FALSE;
}
else
if( 40 == header.mSize )
{
if( 3 == header.mCompression )
{
// Windows NT
windows_nt_version = TRUE;
}
else
{
// Windows 3.x
}
}
else
if( 12 <= header.mSize && 64 <= header.mSize )
{
setLastError("OS/2 2.x BMP files are not supported");
return FALSE;
}
else
if( 108 == header.mSize )
{
// BITMAPV4HEADER
windows_95_version = TRUE;
}
else
if( 108 < header.mSize )
{
// BITMAPV5HEADER or greater
// Should work as long at Microsoft maintained backwards compatibility (which they did in V4 and V5)
windows_95_version = TRUE;
}
S32 width = header.mWidth;
S32 height = header.mHeight;
if (height < 0)
{
mOriginAtTop = TRUE;
height = -height;
}
else
{
mOriginAtTop = FALSE;
}
mBitsPerPixel = header.mBitsPerPixel;
S32 components;
switch( mBitsPerPixel )
{
case 8:
components = 1;
break;
case 24:
case 32:
components = 3;
break;
case 1:
case 4:
case 16: // Started work on 16, but doesn't work yet
// These are legal, but we don't support them yet.
setLastError("Unsupported bit depth");
return FALSE;
default:
setLastError("Unrecognized bit depth");
return FALSE;
}
setSize(width, height, components);
switch( header.mCompression )
{
case 0:
// Uncompressed
break;
case 1:
setLastError("8 bit RLE compression not supported.");
return FALSE;
case 2:
setLastError("4 bit RLE compression not supported.");
return FALSE;
case 3:
// Windows NT or Windows 95
break;
default:
setLastError("Unsupported compression format.");
return FALSE;
}
////////////////////////////////////////////////////////////////////
// Part 3: Bitfield Masks and other color data
S32 extension_size = 0;
if( windows_nt_version )
{
if( (16 != header.mBitsPerPixel) && (32 != header.mBitsPerPixel) )
{
setLastError("Bitfield encoding requires 16 or 32 bits per pixel.");
return FALSE;
}
if( 0 != header.mNumColors )
{
setLastError("Bitfield encoding is not compatible with a color table.");
return FALSE;
}
extension_size = 4 * 3;
memcpy( mBitfieldMask, mdata + FILE_HEADER_SIZE + BITMAP_HEADER_SIZE, extension_size); /* Flawfinder: ignore */
}
else
if( windows_95_version )
{
Win95BmpHeaderExtension win_95_extension;
extension_size = sizeof( win_95_extension );
llassert( sizeof( win_95_extension ) + BITMAP_HEADER_SIZE == 108 );
memcpy( &win_95_extension, mdata + FILE_HEADER_SIZE + BITMAP_HEADER_SIZE, sizeof( win_95_extension ) ); /* Flawfinder: ignore */
if( 3 == header.mCompression )
{
memcpy( mBitfieldMask, mdata + FILE_HEADER_SIZE + BITMAP_HEADER_SIZE, 4 * 4); /* Flawfinder: ignore */
}
// Color correction ignored for now
}
////////////////////////////////////////////////////////////////////
// Part 4: Color Palette (optional)
// Note: There's no color palette if there are 16 or more bits per pixel
S32 color_palette_size = 0;
mColorPaletteColors = 0;
if( header.mBitsPerPixel < 16 )
{
if( 0 == header.mNumColors )
{
mColorPaletteColors = (1 << header.mBitsPerPixel);
}
else
{
mColorPaletteColors = header.mNumColors;
}
}
color_palette_size = mColorPaletteColors * 4;
if( 0 != mColorPaletteColors )
{
mColorPalette = new U8[color_palette_size];
if (!mColorPalette)
{
llerrs << "Out of memory in LLImageBMP::updateData()" << llendl;
return FALSE;
}
memcpy( mColorPalette, mdata + FILE_HEADER_SIZE + BITMAP_HEADER_SIZE + extension_size, color_palette_size ); /* Flawfinder: ignore */
}
return TRUE;
}
BOOL LLImageBMP::decode(LLImageRaw* raw_image, F32 decode_time)
{
llassert_always(raw_image);
resetLastError();
// Check to make sure that this instance has been initialized with data
U8* mdata = getData();
if (!mdata || (0 == getDataSize()))
{
setLastError("llimagebmp trying to decode an image with no data!");
return FALSE;
}
raw_image->resize(getWidth(), getHeight(), 3);
U8* src = mdata + mBitmapOffset;
U8* dst = raw_image->getData();
BOOL success = FALSE;
switch( mBitsPerPixel )
{
case 8:
if( mColorPaletteColors >= 256 )
{
success = decodeColorTable8( dst, src );
}
break;
case 16:
success = decodeColorMask16( dst, src );
break;
case 24:
success = decodeTruecolor24( dst, src );
break;
case 32:
success = decodeColorMask32( dst, src );
break;
}
if( success && mOriginAtTop )
{
raw_image->verticalFlip();
}
return success;
}
U32 LLImageBMP::countTrailingZeros( U32 m )
{
U32 shift_count = 0;
while( !(m & 1) )
{
shift_count++;
m >>= 1;
}
return shift_count;
}
BOOL LLImageBMP::decodeColorMask16( U8* dst, U8* src )
{
llassert( 16 == mBitsPerPixel );
if( !mBitfieldMask[0] && !mBitfieldMask[1] && !mBitfieldMask[2] )
{
// Use default values
mBitfieldMask[0] = 0x00007C00;
mBitfieldMask[1] = 0x000003E0;
mBitfieldMask[2] = 0x0000001F;
}
S32 src_row_span = getWidth() * 2;
S32 alignment_bytes = (3 * src_row_span) % 4; // round up to nearest multiple of 4
U32 r_shift = countTrailingZeros( mBitfieldMask[2] );
U32 g_shift = countTrailingZeros( mBitfieldMask[1] );
U32 b_shift = countTrailingZeros( mBitfieldMask[0] );
for( S32 row = 0; row < getHeight(); row++ )
{
for( S32 col = 0; col < getWidth(); col++ )
{
U32 value = *((U16*)src);
dst[0] = U8((value & mBitfieldMask[2]) >> r_shift); // Red
dst[1] = U8((value & mBitfieldMask[1]) >> g_shift); // Green
dst[2] = U8((value & mBitfieldMask[0]) >> b_shift); // Blue
src += 2;
dst += 3;
}
src += alignment_bytes;
}
return TRUE;
}
BOOL LLImageBMP::decodeColorMask32( U8* dst, U8* src )
{
// Note: alpha is not supported
llassert( 32 == mBitsPerPixel );
if( !mBitfieldMask[0] && !mBitfieldMask[1] && !mBitfieldMask[2] )
{
// Use default values
mBitfieldMask[0] = 0x00FF0000;
mBitfieldMask[1] = 0x0000FF00;
mBitfieldMask[2] = 0x000000FF;
}
S32 src_row_span = getWidth() * 4;
S32 alignment_bytes = (3 * src_row_span) % 4; // round up to nearest multiple of 4
U32 r_shift = countTrailingZeros( mBitfieldMask[0] );
U32 g_shift = countTrailingZeros( mBitfieldMask[1] );
U32 b_shift = countTrailingZeros( mBitfieldMask[2] );
for( S32 row = 0; row < getHeight(); row++ )
{
for( S32 col = 0; col < getWidth(); col++ )
{
U32 value = *((U32*)src);
dst[0] = U8((value & mBitfieldMask[0]) >> r_shift); // Red
dst[1] = U8((value & mBitfieldMask[1]) >> g_shift); // Green
dst[2] = U8((value & mBitfieldMask[2]) >> b_shift); // Blue
src += 4;
dst += 3;
}
src += alignment_bytes;
}
return TRUE;
}
BOOL LLImageBMP::decodeColorTable8( U8* dst, U8* src )
{
llassert( (8 == mBitsPerPixel) && (mColorPaletteColors >= 256) );
S32 src_row_span = getWidth() * 1;
S32 alignment_bytes = (3 * src_row_span) % 4; // round up to nearest multiple of 4
for( S32 row = 0; row < getHeight(); row++ )
{
for( S32 col = 0; col < getWidth(); col++ )
{
S32 index = 4 * src[0];
dst[0] = mColorPalette[index + 2]; // Red
dst[1] = mColorPalette[index + 1]; // Green
dst[2] = mColorPalette[index + 0]; // Blue
src++;
dst += 3;
}
src += alignment_bytes;
}
return TRUE;
}
BOOL LLImageBMP::decodeTruecolor24( U8* dst, U8* src )
{
llassert( 24 == mBitsPerPixel );
llassert( 3 == getComponents() );
S32 src_row_span = getWidth() * 3;
S32 alignment_bytes = (3 * src_row_span) % 4; // round up to nearest multiple of 4
for( S32 row = 0; row < getHeight(); row++ )
{
for( S32 col = 0; col < getWidth(); col++ )
{
dst[0] = src[2]; // Red
dst[1] = src[1]; // Green
dst[2] = src[0]; // Blue
src += 3;
dst += 3;
}
src += alignment_bytes;
}
return TRUE;
}
BOOL LLImageBMP::encode(const LLImageRaw* raw_image, F32 encode_time)
{
llassert_always(raw_image);
resetLastError();
S32 src_components = raw_image->getComponents();
S32 dst_components = ( src_components < 3 ) ? 1 : 3;
if( (2 == src_components) || (4 == src_components) )
{
llinfos << "Dropping alpha information during BMP encoding" << llendl;
}
setSize(raw_image->getWidth(), raw_image->getHeight(), dst_components);
U8 magic[14];
LLBMPHeader header;
int header_bytes = 14+sizeof(header);
llassert(header_bytes == 54);
if (getComponents() == 1)
{
header_bytes += 1024; // Need colour LUT.
}
int line_bytes = getComponents() * getWidth();
int alignment_bytes = (3 * line_bytes) % 4;
line_bytes += alignment_bytes;
int file_bytes = line_bytes*getHeight() + header_bytes;
// Allocate the new buffer for the data.
if(!allocateData(file_bytes)) //memory allocation failed
{
return FALSE ;
}
magic[0] = 'B'; magic[1] = 'M';
magic[2] = (U8) file_bytes;
magic[3] = (U8)(file_bytes>>8);
magic[4] = (U8)(file_bytes>>16);
magic[5] = (U8)(file_bytes>>24);
magic[6] = magic[7] = magic[8] = magic[9] = 0;
magic[10] = (U8) header_bytes;
magic[11] = (U8)(header_bytes>>8);
magic[12] = (U8)(header_bytes>>16);
magic[13] = (U8)(header_bytes>>24);
header.mSize = 40;
header.mWidth = getWidth();
header.mHeight = getHeight();
header.mPlanes = 1;
header.mBitsPerPixel = (getComponents()==1)?8:24;
header.mCompression = 0;
header.mAlignmentPadding = 0;
header.mImageSize = 0;
#if LL_DARWIN
header.mHorzPelsPerMeter = header.mVertPelsPerMeter = 2834; // 72dpi
#else
header.mHorzPelsPerMeter = header.mVertPelsPerMeter = 0;
#endif
header.mNumColors = header.mNumColorsImportant = 0;
// convert BMP header to little endian (no-op on little endian builds)
llendianswizzleone(header.mSize);
llendianswizzleone(header.mWidth);
llendianswizzleone(header.mHeight);
llendianswizzleone(header.mPlanes);
llendianswizzleone(header.mBitsPerPixel);
llendianswizzleone(header.mCompression);
llendianswizzleone(header.mAlignmentPadding);
llendianswizzleone(header.mImageSize);
llendianswizzleone(header.mHorzPelsPerMeter);
llendianswizzleone(header.mVertPelsPerMeter);
llendianswizzleone(header.mNumColors);
llendianswizzleone(header.mNumColorsImportant);
U8* mdata = getData();
// Output magic, then header, then the palette table, then the data.
U32 cur_pos = 0;
memcpy(mdata, magic, 14);
cur_pos += 14;
memcpy(mdata+cur_pos, &header, 40); /* Flawfinder: ignore */
cur_pos += 40;
if (getComponents() == 1)
{
S32 n;
for (n=0; n < 256; n++)
{
mdata[cur_pos++] = (U8)n;
mdata[cur_pos++] = (U8)n;
mdata[cur_pos++] = (U8)n;
mdata[cur_pos++] = 0;
}
}
// Need to iterate through, because we need to flip the RGB.
const U8* src = raw_image->getData();
U8* dst = mdata + cur_pos;
for( S32 row = 0; row < getHeight(); row++ )
{
for( S32 col = 0; col < getWidth(); col++ )
{
switch( src_components )
{
case 1:
*dst++ = *src++;
break;
case 2:
{
U32 lum = src[0];
U32 alpha = src[1];
*dst++ = (U8)(lum * alpha / 255);
src += 2;
break;
}
case 3:
case 4:
dst[0] = src[2];
dst[1] = src[1];
dst[2] = src[0];
src += src_components;
dst += 3;
break;
}
}
for( S32 i = 0; i < alignment_bytes; i++ )
{
*dst++ = 0;
}
}
return TRUE;
}

View File

@@ -0,0 +1,70 @@
/**
* @file llimagebmp.h
* @brief Image implementation for BMP.
*
* $LicenseInfo:firstyear=2001&license=viewergpl$
*
* Copyright (c) 2001-2009, Linden Research, Inc.
*
* Second Life Viewer Source Code
* The source code in this file ("Source Code") is provided by Linden Lab
* to you under the terms of the GNU General Public License, version 2.0
* ("GPL"), unless you have obtained a separate licensing agreement
* ("Other License"), formally executed by you and Linden Lab. Terms of
* the GPL can be found in doc/GPL-license.txt in this distribution, or
* online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
*
* There are special exceptions to the terms and conditions of the GPL as
* it is applied to this Source Code. View the full text of the exception
* in the file doc/FLOSS-exception.txt in this software distribution, or
* online at
* http://secondlifegrid.net/programs/open_source/licensing/flossexception
*
* By copying, modifying or distributing this software, you acknowledge
* that you have read and understood your obligations described above,
* and agree to abide by those obligations.
*
* ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
* WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
* COMPLETENESS OR PERFORMANCE.
* $/LicenseInfo$
*/
#ifndef LL_LLIMAGEBMP_H
#define LL_LLIMAGEBMP_H
#include "llimage.h"
// This class compresses and decompressed BMP files
class LLImageBMP : public LLImageFormatted
{
protected:
virtual ~LLImageBMP();
public:
LLImageBMP();
/*virtual*/ std::string getExtension() { return std::string("bmp"); }
/*virtual*/ BOOL updateData();
/*virtual*/ BOOL decode(LLImageRaw* raw_image, F32 decode_time);
/*virtual*/ BOOL encode(const LLImageRaw* raw_image, F32 encode_time);
protected:
BOOL decodeColorTable8( U8* dst, U8* src );
BOOL decodeColorMask16( U8* dst, U8* src );
BOOL decodeTruecolor24( U8* dst, U8* src );
BOOL decodeColorMask32( U8* dst, U8* src );
U32 countTrailingZeros( U32 m );
protected:
S32 mColorPaletteColors;
U8* mColorPalette;
S32 mBitmapOffset;
S32 mBitsPerPixel;
U32 mBitfieldMask[4]; // rgba
BOOL mOriginAtTop;
};
#endif

View File

@@ -0,0 +1,511 @@
/**
* @file llimagedxt.cpp
*
* $LicenseInfo:firstyear=2001&license=viewergpl$
*
* Copyright (c) 2001-2009, Linden Research, Inc.
*
* Second Life Viewer Source Code
* The source code in this file ("Source Code") is provided by Linden Lab
* to you under the terms of the GNU General Public License, version 2.0
* ("GPL"), unless you have obtained a separate licensing agreement
* ("Other License"), formally executed by you and Linden Lab. Terms of
* the GPL can be found in doc/GPL-license.txt in this distribution, or
* online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
*
* There are special exceptions to the terms and conditions of the GPL as
* it is applied to this Source Code. View the full text of the exception
* in the file doc/FLOSS-exception.txt in this software distribution, or
* online at
* http://secondlifegrid.net/programs/open_source/licensing/flossexception
*
* By copying, modifying or distributing this software, you acknowledge
* that you have read and understood your obligations described above,
* and agree to abide by those obligations.
*
* ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
* WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
* COMPLETENESS OR PERFORMANCE.
* $/LicenseInfo$
*/
#include "linden_common.h"
#include "llimagedxt.h"
//static
void LLImageDXT::checkMinWidthHeight(EFileFormat format, S32& width, S32& height)
{
S32 mindim = (format >= FORMAT_DXT1 && format <= FORMAT_DXR5) ? 4 : 1;
width = llmax(width, mindim);
height = llmax(height, mindim);
}
//static
S32 LLImageDXT::formatBits(EFileFormat format)
{
switch (format)
{
case FORMAT_DXT1: return 4;
case FORMAT_DXR1: return 4;
case FORMAT_I8: return 8;
case FORMAT_A8: return 8;
case FORMAT_DXT3: return 8;
case FORMAT_DXR3: return 8;
case FORMAT_DXR5: return 8;
case FORMAT_DXT5: return 8;
case FORMAT_RGB8: return 24;
case FORMAT_RGBA8: return 32;
default:
llerrs << "LLImageDXT::Unknown format: " << format << llendl;
return 0;
}
};
//static
S32 LLImageDXT::formatBytes(EFileFormat format, S32 width, S32 height)
{
checkMinWidthHeight(format, width, height);
S32 bytes = ((width*height*formatBits(format)+7)>>3);
S32 aligned = (bytes+3)&~3;
return aligned;
}
//static
S32 LLImageDXT::formatComponents(EFileFormat format)
{
switch (format)
{
case FORMAT_DXT1: return 3;
case FORMAT_DXR1: return 3;
case FORMAT_I8: return 1;
case FORMAT_A8: return 1;
case FORMAT_DXT3: return 4;
case FORMAT_DXR3: return 4;
case FORMAT_DXT5: return 4;
case FORMAT_DXR5: return 4;
case FORMAT_RGB8: return 3;
case FORMAT_RGBA8: return 4;
default:
llerrs << "LLImageDXT::Unknown format: " << format << llendl;
return 0;
}
};
// static
LLImageDXT::EFileFormat LLImageDXT::getFormat(S32 fourcc)
{
switch(fourcc)
{
case 0x20203849: return FORMAT_I8;
case 0x20203841: return FORMAT_A8;
case 0x20424752: return FORMAT_RGB8;
case 0x41424752: return FORMAT_RGBA8;
case 0x31525844: return FORMAT_DXR1;
case 0x32525844: return FORMAT_DXR2;
case 0x33525844: return FORMAT_DXR3;
case 0x34525844: return FORMAT_DXR4;
case 0x35525844: return FORMAT_DXR5;
case 0x31545844: return FORMAT_DXT1;
case 0x32545844: return FORMAT_DXT2;
case 0x33545844: return FORMAT_DXT3;
case 0x34545844: return FORMAT_DXT4;
case 0x35545844: return FORMAT_DXT5;
default: return FORMAT_UNKNOWN;
}
}
//static
S32 LLImageDXT::getFourCC(EFileFormat format)
{
switch(format)
{
case FORMAT_I8: return 0x20203849;
case FORMAT_A8: return 0x20203841;
case FORMAT_RGB8: return 0x20424752;
case FORMAT_RGBA8: return 0x41424752;
case FORMAT_DXR1: return 0x31525844;
case FORMAT_DXR2: return 0x32525844;
case FORMAT_DXR3: return 0x33525844;
case FORMAT_DXR4: return 0x34525844;
case FORMAT_DXR5: return 0x35525844;
case FORMAT_DXT1: return 0x31545844;
case FORMAT_DXT2: return 0x32545844;
case FORMAT_DXT3: return 0x33545844;
case FORMAT_DXT4: return 0x34545844;
case FORMAT_DXT5: return 0x35545844;
default: return 0x00000000;
}
}
//static
void LLImageDXT::calcDiscardWidthHeight(S32 discard_level, EFileFormat format, S32& width, S32& height)
{
while (discard_level > 0 && width > 1 && height > 1)
{
discard_level--;
width >>= 1;
height >>= 1;
}
checkMinWidthHeight(format, width, height);
}
//static
S32 LLImageDXT::calcNumMips(S32 width, S32 height)
{
S32 nmips = 0;
while (width > 0 && height > 0)
{
width >>= 1;
height >>= 1;
nmips++;
}
return nmips;
}
//============================================================================
LLImageDXT::LLImageDXT()
: LLImageFormatted(IMG_CODEC_DXT),
mFileFormat(FORMAT_UNKNOWN),
mHeaderSize(0)
{
}
LLImageDXT::~LLImageDXT()
{
}
// virtual
BOOL LLImageDXT::updateData()
{
resetLastError();
U8* data = getData();
S32 data_size = getDataSize();
if (!data || !data_size)
{
setLastError("LLImageDXT uninitialized");
return FALSE;
}
S32 width, height, miplevelmax;
dxtfile_header_t* header = (dxtfile_header_t*)data;
if (header->fourcc != 0x20534444)
{
dxtfile_header_old_t* oldheader = (dxtfile_header_old_t*)header;
mHeaderSize = sizeof(dxtfile_header_old_t);
mFileFormat = EFileFormat(oldheader->format);
miplevelmax = llmin(oldheader->maxlevel,MAX_IMAGE_MIP);
width = oldheader->maxwidth;
height = oldheader->maxheight;
}
else
{
mHeaderSize = sizeof(dxtfile_header_t);
mFileFormat = getFormat(header->pixel_fmt.fourcc);
miplevelmax = llmin(header->num_mips-1,MAX_IMAGE_MIP);
width = header->maxwidth;
height = header->maxheight;
}
if (data_size < mHeaderSize)
{
llerrs << "LLImageDXT: not enough data" << llendl;
}
S32 ncomponents = formatComponents(mFileFormat);
setSize(width, height, ncomponents);
S32 discard = calcDiscardLevelBytes(data_size);
discard = llmin(discard, miplevelmax);
setDiscardLevel(discard);
return TRUE;
}
// discard: 0 = largest (last) mip
S32 LLImageDXT::getMipOffset(S32 discard)
{
if (mFileFormat >= FORMAT_DXT1 && mFileFormat <= FORMAT_DXT5)
{
llerrs << "getMipOffset called with old (unsupported) format" << llendl;
}
S32 width = getWidth(), height = getHeight();
S32 num_mips = calcNumMips(width, height);
discard = llclamp(discard, 0, num_mips-1);
S32 last_mip = num_mips-1-discard;
llassert(mHeaderSize > 0);
S32 offset = mHeaderSize;
for (S32 mipidx = num_mips-1; mipidx >= 0; mipidx--)
{
if (mipidx < last_mip)
{
offset += formatBytes(mFileFormat, width, height);
}
width >>= 1;
height >>= 1;
}
return offset;
}
void LLImageDXT::setFormat()
{
S32 ncomponents = getComponents();
switch (ncomponents)
{
case 3: mFileFormat = FORMAT_DXR1; break;
case 4: mFileFormat = FORMAT_DXR3; break;
default: llerrs << "LLImageDXT::setFormat called with ncomponents = " << ncomponents << llendl;
}
mHeaderSize = calcHeaderSize();
}
// virtual
BOOL LLImageDXT::decode(LLImageRaw* raw_image, F32 time)
{
// *TODO: Test! This has been tweaked since its intial inception,
// but we don't use it any more!
llassert_always(raw_image);
if (mFileFormat >= FORMAT_DXT1 && mFileFormat <= FORMAT_DXR5)
{
llwarns << "Attempt to decode compressed LLImageDXT to Raw (unsupported)" << llendl;
return FALSE;
}
S32 width = getWidth(), height = getHeight();
S32 ncomponents = getComponents();
U8* data = NULL;
if (mDiscardLevel >= 0)
{
data = getData() + getMipOffset(mDiscardLevel);
calcDiscardWidthHeight(mDiscardLevel, mFileFormat, width, height);
}
else
{
data = getData() + getMipOffset(0);
}
S32 image_size = formatBytes(mFileFormat, width, height);
if ((!getData()) || (data + image_size > getData() + getDataSize()))
{
setLastError("LLImageDXT trying to decode an image with not enough data!");
return FALSE;
}
raw_image->resize(width, height, ncomponents);
memcpy(raw_image->getData(), data, image_size); /* Flawfinder: ignore */
return TRUE;
}
BOOL LLImageDXT::getMipData(LLPointer<LLImageRaw>& raw, S32 discard)
{
if (discard < 0)
{
discard = mDiscardLevel;
}
else if (discard < mDiscardLevel)
{
llerrs << "Request for invalid discard level" << llendl;
}
U8* data = getData() + getMipOffset(discard);
S32 width = 0;
S32 height = 0;
calcDiscardWidthHeight(discard, mFileFormat, width, height);
raw = new LLImageRaw(data, width, height, getComponents());
return TRUE;
}
BOOL LLImageDXT::encodeDXT(const LLImageRaw* raw_image, F32 time, bool explicit_mips)
{
llassert_always(raw_image);
S32 ncomponents = raw_image->getComponents();
EFileFormat format;
switch (ncomponents)
{
case 1:
format = FORMAT_A8;
break;
case 3:
format = FORMAT_RGB8;
break;
case 4:
format = FORMAT_RGBA8;
break;
default:
llerrs << "LLImageDXT::encode: Unhandled channel number: " << ncomponents << llendl;
return 0;
}
S32 width = raw_image->getWidth();
S32 height = raw_image->getHeight();
if (explicit_mips)
{
height = (height/3)*2;
}
setSize(width, height, ncomponents);
mHeaderSize = sizeof(dxtfile_header_t);
mFileFormat = format;
S32 nmips = calcNumMips(width, height);
S32 w = width;
S32 h = height;
S32 totbytes = mHeaderSize;
for (S32 mip=0; mip<nmips; mip++)
{
totbytes += formatBytes(format,w,h);
w >>= 1;
h >>= 1;
}
allocateData(totbytes);
U8* data = getData();
dxtfile_header_t* header = (dxtfile_header_t*)data;
llassert(mHeaderSize > 0);
memset(header, 0, mHeaderSize);
header->fourcc = 0x20534444;
header->pixel_fmt.fourcc = getFourCC(format);
header->num_mips = nmips;
header->maxwidth = width;
header->maxheight = height;
U8* prev_mipdata = 0;
w = width, h = height;
for (S32 mip=0; mip<nmips; mip++)
{
U8* mipdata = data + getMipOffset(mip);
S32 bytes = formatBytes(format, w, h);
if (mip==0)
{
memcpy(mipdata, raw_image->getData(), bytes); /* Flawfinder: ignore */
}
else if (explicit_mips)
{
extractMip(raw_image->getData(), mipdata, width, height, w, h, format);
}
else
{
generateMip(prev_mipdata, mipdata, w, h, ncomponents);
}
w >>= 1;
h >>= 1;
checkMinWidthHeight(format, w, h);
prev_mipdata = mipdata;
}
return TRUE;
}
// virtual
BOOL LLImageDXT::encode(const LLImageRaw* raw_image, F32 time)
{
return encodeDXT(raw_image, time, false);
}
// virtual
bool LLImageDXT::convertToDXR()
{
EFileFormat newformat = FORMAT_UNKNOWN;
switch (mFileFormat)
{
case FORMAT_DXR1:
case FORMAT_DXR2:
case FORMAT_DXR3:
case FORMAT_DXR4:
case FORMAT_DXR5:
return false; // nothing to do
case FORMAT_DXT1: newformat = FORMAT_DXR1; break;
case FORMAT_DXT2: newformat = FORMAT_DXR2; break;
case FORMAT_DXT3: newformat = FORMAT_DXR3; break;
case FORMAT_DXT4: newformat = FORMAT_DXR4; break;
case FORMAT_DXT5: newformat = FORMAT_DXR5; break;
default:
llwarns << "convertToDXR: can not convert format: " << llformat("0x%08x",getFourCC(mFileFormat)) << llendl;
return false;
}
mFileFormat = newformat;
S32 width = getWidth(), height = getHeight();
S32 nmips = calcNumMips(width,height);
S32 total_bytes = getDataSize();
U8* olddata = getData();
U8* newdata = new U8[total_bytes];
if (!newdata)
{
llerrs << "Out of memory in LLImageDXT::convertToDXR()" << llendl;
return false;
}
llassert(total_bytes > 0);
memset(newdata, 0, total_bytes);
memcpy(newdata, olddata, mHeaderSize); /* Flawfinder: ignore */
for (S32 mip=0; mip<nmips; mip++)
{
S32 bytes = formatBytes(mFileFormat, width, height);
S32 newoffset = getMipOffset(mip);
S32 oldoffset = mHeaderSize + (total_bytes - newoffset - bytes);
memcpy(newdata + newoffset, olddata + oldoffset, bytes); /* Flawfinder: ignore */
width >>= 1;
height >>= 1;
}
dxtfile_header_t* header = (dxtfile_header_t*)newdata;
header->pixel_fmt.fourcc = getFourCC(newformat);
setData(newdata, total_bytes);
updateData();
return true;
}
// virtual
S32 LLImageDXT::calcHeaderSize()
{
return llmax(sizeof(dxtfile_header_old_t), sizeof(dxtfile_header_t));
}
// virtual
S32 LLImageDXT::calcDataSize(S32 discard_level)
{
if (mFileFormat == FORMAT_UNKNOWN)
{
llerrs << "calcDataSize called with unloaded LLImageDXT" << llendl;
return 0;
}
if (discard_level < 0)
{
discard_level = mDiscardLevel;
}
S32 bytes = getMipOffset(discard_level); // size of header + previous mips
S32 w = getWidth() >> discard_level;
S32 h = getHeight() >> discard_level;
bytes += formatBytes(mFileFormat,w,h);
return bytes;
}
//============================================================================
//static
void LLImageDXT::extractMip(const U8 *indata, U8* mipdata, int width, int height,
int mip_width, int mip_height, EFileFormat format)
{
int initial_offset = formatBytes(format, width, height);
int line_width = formatBytes(format, width, 1);
int mip_line_width = formatBytes(format, mip_width, 1);
int line_offset = 0;
for (int ww=width>>1; ww>mip_width; ww>>=1)
{
line_offset += formatBytes(format, ww, 1);
}
for (int h=0;h<mip_height;++h)
{
int start_offset = initial_offset + line_width * h + line_offset;
memcpy(mipdata + mip_line_width*h, indata + start_offset, mip_line_width); /* Flawfinder: ignore */
}
}
//============================================================================

146
indra/llimage/llimagedxt.h Normal file
View File

@@ -0,0 +1,146 @@
/**
* @file llimagedxt.h
*
* $LicenseInfo:firstyear=2001&license=viewergpl$
*
* Copyright (c) 2001-2009, Linden Research, Inc.
*
* Second Life Viewer Source Code
* The source code in this file ("Source Code") is provided by Linden Lab
* to you under the terms of the GNU General Public License, version 2.0
* ("GPL"), unless you have obtained a separate licensing agreement
* ("Other License"), formally executed by you and Linden Lab. Terms of
* the GPL can be found in doc/GPL-license.txt in this distribution, or
* online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
*
* There are special exceptions to the terms and conditions of the GPL as
* it is applied to this Source Code. View the full text of the exception
* in the file doc/FLOSS-exception.txt in this software distribution, or
* online at
* http://secondlifegrid.net/programs/open_source/licensing/flossexception
*
* By copying, modifying or distributing this software, you acknowledge
* that you have read and understood your obligations described above,
* and agree to abide by those obligations.
*
* ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
* WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
* COMPLETENESS OR PERFORMANCE.
* $/LicenseInfo$
*/
#ifndef LL_LLIMAGEDXT_H
#define LL_LLIMAGEDXT_H
#include "llimage.h"
// This class decodes and encodes LL DXT files (which may unclude uncompressed RGB or RGBA mipped data)
class LLImageDXT : public LLImageFormatted
{
public:
enum EFileFormat
{
FORMAT_UNKNOWN = 0,
FORMAT_I8 = 1,
FORMAT_A8,
FORMAT_RGB8,
FORMAT_RGBA8,
FORMAT_DXT1,
FORMAT_DXT2,
FORMAT_DXT3,
FORMAT_DXT4,
FORMAT_DXT5,
FORMAT_DXR1,
FORMAT_DXR2,
FORMAT_DXR3,
FORMAT_DXR4,
FORMAT_DXR5,
FORMAT_NOFILE = 0xff,
};
struct dxtfile_header_old_t
{
S32 format;
S32 maxlevel;
S32 maxwidth;
S32 maxheight;
};
struct dxtfile_header_t
{
S32 fourcc;
// begin DDSURFACEDESC2 struct
S32 header_size; // size of the header
S32 flags; // flags - unused
S32 maxheight;
S32 maxwidth;
S32 image_size; // size of the compressed image
S32 depth;
S32 num_mips;
S32 reserved[11];
struct pixel_format
{
S32 struct_size; // size of this structure
S32 flags;
S32 fourcc;
S32 bit_count;
S32 r_mask;
S32 g_mask;
S32 b_mask;
S32 a_mask;
} pixel_fmt;
S32 caps[4];
S32 reserved2;
};
protected:
/*virtual*/ ~LLImageDXT();
private:
BOOL encodeDXT(const LLImageRaw* raw_image, F32 decode_time, bool explicit_mips);
public:
LLImageDXT();
/*virtual*/ std::string getExtension() { return std::string("dxt"); }
/*virtual*/ BOOL updateData();
/*virtual*/ BOOL decode(LLImageRaw* raw_image, F32 decode_time);
/*virtual*/ BOOL encode(const LLImageRaw* raw_image, F32 encode_time);
/*virtual*/ S32 calcHeaderSize();
/*virtual*/ S32 calcDataSize(S32 discard_level = 0);
BOOL getMipData(LLPointer<LLImageRaw>& raw, S32 discard=-1);
void setFormat();
S32 getMipOffset(S32 discard);
EFileFormat getFileFormat() { return mFileFormat; }
bool isCompressed() { return (mFileFormat >= FORMAT_DXT1 && mFileFormat <= FORMAT_DXR5); }
bool convertToDXR(); // convert from DXT to DXR
static void checkMinWidthHeight(EFileFormat format, S32& width, S32& height);
static S32 formatBits(EFileFormat format);
static S32 formatBytes(EFileFormat format, S32 width, S32 height);
static S32 formatOffset(EFileFormat format, S32 width, S32 height, S32 max_width, S32 max_height);
static S32 formatComponents(EFileFormat format);
static EFileFormat getFormat(S32 fourcc);
static S32 getFourCC(EFileFormat format);
static void calcDiscardWidthHeight(S32 discard_level, EFileFormat format, S32& width, S32& height);
static S32 calcNumMips(S32 width, S32 height);
private:
static void extractMip(const U8 *indata, U8* mipdata, int width, int height,
int mip_width, int mip_height, EFileFormat format);
private:
EFileFormat mFileFormat;
S32 mHeaderSize;
};
#endif

View File

@@ -0,0 +1,506 @@
/**
* @file llimagej2c.cpp
*
* $LicenseInfo:firstyear=2001&license=viewergpl$
*
* Copyright (c) 2001-2009, Linden Research, Inc.
*
* Second Life Viewer Source Code
* The source code in this file ("Source Code") is provided by Linden Lab
* to you under the terms of the GNU General Public License, version 2.0
* ("GPL"), unless you have obtained a separate licensing agreement
* ("Other License"), formally executed by you and Linden Lab. Terms of
* the GPL can be found in doc/GPL-license.txt in this distribution, or
* online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
*
* There are special exceptions to the terms and conditions of the GPL as
* it is applied to this Source Code. View the full text of the exception
* in the file doc/FLOSS-exception.txt in this software distribution, or
* online at
* http://secondlifegrid.net/programs/open_source/licensing/flossexception
*
* By copying, modifying or distributing this software, you acknowledge
* that you have read and understood your obligations described above,
* and agree to abide by those obligations.
*
* ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
* WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
* COMPLETENESS OR PERFORMANCE.
* $/LicenseInfo$
*/
#include "linden_common.h"
#include "apr_pools.h"
#include "apr_dso.h"
#include "lldir.h"
#include "llimagej2c.h"
#include "llmemtype.h"
typedef LLImageJ2CImpl* (*CreateLLImageJ2CFunction)();
typedef void (*DestroyLLImageJ2CFunction)(LLImageJ2CImpl*);
typedef const char* (*EngineInfoLLImageJ2CFunction)();
//some "private static" variables so we only attempt to load
//dynamic libaries once
CreateLLImageJ2CFunction j2cimpl_create_func;
DestroyLLImageJ2CFunction j2cimpl_destroy_func;
EngineInfoLLImageJ2CFunction j2cimpl_engineinfo_func;
apr_pool_t *j2cimpl_dso_memory_pool;
apr_dso_handle_t *j2cimpl_dso_handle;
//Declare the prototype for theses functions here, their functionality
//will be implemented in other files which define a derived LLImageJ2CImpl
//but only ONE static library which has the implementation for this
//function should ever be included
LLImageJ2CImpl* fallbackCreateLLImageJ2CImpl();
void fallbackDestroyLLImageJ2CImpl(LLImageJ2CImpl* impl);
const char* fallbackEngineInfoLLImageJ2CImpl();
//static
//Loads the required "create", "destroy" and "engineinfo" functions needed
void LLImageJ2C::openDSO()
{
//attempt to load a DSO and get some functions from it
std::string dso_name;
std::string dso_path;
bool all_functions_loaded = false;
apr_status_t rv;
#if LL_WINDOWS
dso_name = "llkdu.dll";
#elif LL_DARWIN
dso_name = "libllkdu.dylib";
#else
dso_name = "libllkdu.so";
#endif
dso_path = gDirUtilp->findFile(dso_name,
gDirUtilp->getAppRODataDir(),
gDirUtilp->getExecutableDir());
j2cimpl_dso_handle = NULL;
j2cimpl_dso_memory_pool = NULL;
//attempt to load the shared library
apr_pool_create(&j2cimpl_dso_memory_pool, NULL);
rv = apr_dso_load(&j2cimpl_dso_handle,
dso_path.c_str(),
j2cimpl_dso_memory_pool);
//now, check for success
if ( rv == APR_SUCCESS )
{
//found the dynamic library
//now we want to load the functions we're interested in
CreateLLImageJ2CFunction create_func = NULL;
DestroyLLImageJ2CFunction dest_func = NULL;
EngineInfoLLImageJ2CFunction engineinfo_func = NULL;
rv = apr_dso_sym((apr_dso_handle_sym_t*)&create_func,
j2cimpl_dso_handle,
"createLLImageJ2CKDU");
if ( rv == APR_SUCCESS )
{
//we've loaded the create function ok
//we need to delete via the DSO too
//so lets check for a destruction function
rv = apr_dso_sym((apr_dso_handle_sym_t*)&dest_func,
j2cimpl_dso_handle,
"destroyLLImageJ2CKDU");
if ( rv == APR_SUCCESS )
{
//we've loaded the destroy function ok
rv = apr_dso_sym((apr_dso_handle_sym_t*)&engineinfo_func,
j2cimpl_dso_handle,
"engineInfoLLImageJ2CKDU");
if ( rv == APR_SUCCESS )
{
//ok, everything is loaded alright
j2cimpl_create_func = create_func;
j2cimpl_destroy_func = dest_func;
j2cimpl_engineinfo_func = engineinfo_func;
all_functions_loaded = true;
}
}
}
}
if ( !all_functions_loaded )
{
//something went wrong with the DSO or function loading..
//fall back onto our satefy impl creation function
#if 0
// precious verbose debugging, sadly we can't use our
// 'llinfos' stream etc. this early in the initialisation seq.
char errbuf[256];
fprintf(stderr, "failed to load syms from DSO %s (%s)\n",
dso_name.c_str(), dso_path.c_str());
apr_strerror(rv, errbuf, sizeof(errbuf));
fprintf(stderr, "error: %d, %s\n", rv, errbuf);
apr_dso_error(j2cimpl_dso_handle, errbuf, sizeof(errbuf));
fprintf(stderr, "dso-error: %d, %s\n", rv, errbuf);
#endif
if ( j2cimpl_dso_handle )
{
apr_dso_unload(j2cimpl_dso_handle);
j2cimpl_dso_handle = NULL;
}
if ( j2cimpl_dso_memory_pool )
{
apr_pool_destroy(j2cimpl_dso_memory_pool);
j2cimpl_dso_memory_pool = NULL;
}
}
}
//static
void LLImageJ2C::closeDSO()
{
if ( j2cimpl_dso_handle ) apr_dso_unload(j2cimpl_dso_handle);
if (j2cimpl_dso_memory_pool) apr_pool_destroy(j2cimpl_dso_memory_pool);
}
//static
std::string LLImageJ2C::getEngineInfo()
{
if (!j2cimpl_engineinfo_func)
j2cimpl_engineinfo_func = fallbackEngineInfoLLImageJ2CImpl;
return j2cimpl_engineinfo_func();
}
LLImageJ2C::LLImageJ2C() : LLImageFormatted(IMG_CODEC_J2C),
mMaxBytes(0),
mRawDiscardLevel(-1),
mRate(0.0f),
mReversible(FALSE)
{
//We assume here that if we wanted to create via
//a dynamic library that the approriate open calls were made
//before any calls to this constructor.
//Therefore, a NULL creation function pointer here means
//we either did not want to create using functions from the dynamic
//library or there were issues loading it, either way
//use our fall back
if ( !j2cimpl_create_func )
{
j2cimpl_create_func = fallbackCreateLLImageJ2CImpl;
}
mImpl = j2cimpl_create_func();
}
// virtual
LLImageJ2C::~LLImageJ2C()
{
//We assume here that if we wanted to destroy via
//a dynamic library that the approriate open calls were made
//before any calls to this destructor.
//Therefore, a NULL creation function pointer here means
//we either did not want to destroy using functions from the dynamic
//library or there were issues loading it, either way
//use our fall back
if ( !j2cimpl_destroy_func )
{
j2cimpl_destroy_func = fallbackDestroyLLImageJ2CImpl;
}
if ( mImpl )
{
j2cimpl_destroy_func(mImpl);
}
}
// virtual
void LLImageJ2C::resetLastError()
{
mLastError.clear();
}
//virtual
void LLImageJ2C::setLastError(const std::string& message, const std::string& filename)
{
mLastError = message;
if (!filename.empty())
mLastError += std::string(" FILE: ") + filename;
}
// virtual
S8 LLImageJ2C::getRawDiscardLevel()
{
return mRawDiscardLevel;
}
BOOL LLImageJ2C::updateData()
{
BOOL res = TRUE;
resetLastError();
// Check to make sure that this instance has been initialized with data
if (!getData() || (getDataSize() < 16))
{
setLastError("LLImageJ2C uninitialized");
res = FALSE;
}
else
{
res = mImpl->getMetadata(*this);
}
if (res)
{
// SJB: override discard based on mMaxBytes elsewhere
S32 max_bytes = getDataSize(); // mMaxBytes ? mMaxBytes : getDataSize();
S32 discard = calcDiscardLevelBytes(max_bytes);
setDiscardLevel(discard);
}
if (!mLastError.empty())
{
LLImage::setLastError(mLastError);
}
return res;
}
BOOL LLImageJ2C::decode(LLImageRaw *raw_imagep, F32 decode_time)
{
return decodeChannels(raw_imagep, decode_time, 0, 4);
}
// Returns TRUE to mean done, whether successful or not.
BOOL LLImageJ2C::decodeChannels(LLImageRaw *raw_imagep, F32 decode_time, S32 first_channel, S32 max_channel_count )
{
LLMemType mt1((LLMemType::EMemType)mMemType);
BOOL res = TRUE;
resetLastError();
// Check to make sure that this instance has been initialized with data
if (!getData() || (getDataSize() < 16))
{
setLastError("LLImageJ2C uninitialized");
res = TRUE; // done
}
else
{
// Update the raw discard level
updateRawDiscardLevel();
mDecoding = TRUE;
res = mImpl->decodeImpl(*this, *raw_imagep, decode_time, first_channel, max_channel_count);
}
if (res)
{
if (!mDecoding)
{
// Failed
raw_imagep->deleteData();
}
else
{
mDecoding = FALSE;
}
}
if (!mLastError.empty())
{
LLImage::setLastError(mLastError);
}
return res;
}
BOOL LLImageJ2C::encode(const LLImageRaw *raw_imagep, F32 encode_time)
{
return encode(raw_imagep, NULL, encode_time);
}
BOOL LLImageJ2C::encode(const LLImageRaw *raw_imagep, const char* comment_text, F32 encode_time)
{
LLMemType mt1((LLMemType::EMemType)mMemType);
resetLastError();
BOOL res = mImpl->encodeImpl(*this, *raw_imagep, comment_text, encode_time, mReversible);
if (!mLastError.empty())
{
LLImage::setLastError(mLastError);
}
return res;
}
//static
S32 LLImageJ2C::calcHeaderSizeJ2C()
{
return FIRST_PACKET_SIZE; // Hack. just needs to be >= actual header size...
}
//static
S32 LLImageJ2C::calcDataSizeJ2C(S32 w, S32 h, S32 comp, S32 discard_level, F32 rate)
{
if (rate <= 0.f) rate = .125f;
while (discard_level > 0)
{
if (w < 1 || h < 1)
break;
w >>= 1;
h >>= 1;
discard_level--;
}
S32 bytes = (S32)((F32)(w*h*comp)*rate);
bytes = llmax(bytes, calcHeaderSizeJ2C());
return bytes;
}
S32 LLImageJ2C::calcHeaderSize()
{
return calcHeaderSizeJ2C();
}
S32 LLImageJ2C::calcDataSize(S32 discard_level)
{
return calcDataSizeJ2C(getWidth(), getHeight(), getComponents(), discard_level, mRate);
}
S32 LLImageJ2C::calcDiscardLevelBytes(S32 bytes)
{
llassert(bytes >= 0);
S32 discard_level = 0;
if (bytes == 0)
{
return MAX_DISCARD_LEVEL;
}
while (1)
{
S32 bytes_needed = calcDataSize(discard_level); // virtual
if (bytes >= bytes_needed - (bytes_needed>>2)) // For J2c, up the res at 75% of the optimal number of bytes
{
break;
}
discard_level++;
if (discard_level >= MAX_DISCARD_LEVEL)
{
break;
}
}
return discard_level;
}
void LLImageJ2C::setRate(F32 rate)
{
mRate = rate;
}
void LLImageJ2C::setMaxBytes(S32 max_bytes)
{
mMaxBytes = max_bytes;
}
void LLImageJ2C::setReversible(const BOOL reversible)
{
mReversible = reversible;
}
BOOL LLImageJ2C::loadAndValidate(const std::string &filename)
{
BOOL res = TRUE;
resetLastError();
S32 file_size = 0;
LLAPRFile infile ;
infile.open(filename, LL_APR_RB, LLAPRFile::global, &file_size);
apr_file_t* apr_file = infile.getFileHandle() ;
if (!apr_file)
{
setLastError("Unable to open file for reading", filename);
res = FALSE;
}
else if (file_size == 0)
{
setLastError("File is empty",filename);
res = FALSE;
}
else
{
U8 *data = new U8[file_size];
apr_size_t bytes_read = file_size;
apr_status_t s = apr_file_read(apr_file, data, &bytes_read); // modifies bytes_read
infile.close() ;
if (s != APR_SUCCESS || (S32)bytes_read != file_size)
{
delete[] data;
setLastError("Unable to read entire file");
res = FALSE;
}
else
{
res = validate(data, file_size);
}
}
if (!mLastError.empty())
{
LLImage::setLastError(mLastError);
}
return res;
}
BOOL LLImageJ2C::validate(U8 *data, U32 file_size)
{
LLMemType mt1((LLMemType::EMemType)mMemType);
resetLastError();
setData(data, file_size);
BOOL res = updateData();
if ( res )
{
// Check to make sure that this instance has been initialized with data
if (!getData() || (0 == getDataSize()))
{
setLastError("LLImageJ2C uninitialized");
res = FALSE;
}
else
{
res = mImpl->getMetadata(*this);
}
}
if (!mLastError.empty())
{
LLImage::setLastError(mLastError);
}
return res;
}
void LLImageJ2C::decodeFailed()
{
mDecoding = FALSE;
}
void LLImageJ2C::updateRawDiscardLevel()
{
mRawDiscardLevel = mMaxBytes ? calcDiscardLevelBytes(mMaxBytes) : mDiscardLevel;
}
LLImageJ2CImpl::~LLImageJ2CImpl()
{
}

123
indra/llimage/llimagej2c.h Normal file
View File

@@ -0,0 +1,123 @@
/**
* @file llimagej2c.h
* @brief Image implmenation for jpeg2000.
*
* $LicenseInfo:firstyear=2001&license=viewergpl$
*
* Copyright (c) 2001-2009, Linden Research, Inc.
*
* Second Life Viewer Source Code
* The source code in this file ("Source Code") is provided by Linden Lab
* to you under the terms of the GNU General Public License, version 2.0
* ("GPL"), unless you have obtained a separate licensing agreement
* ("Other License"), formally executed by you and Linden Lab. Terms of
* the GPL can be found in doc/GPL-license.txt in this distribution, or
* online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
*
* There are special exceptions to the terms and conditions of the GPL as
* it is applied to this Source Code. View the full text of the exception
* in the file doc/FLOSS-exception.txt in this software distribution, or
* online at
* http://secondlifegrid.net/programs/open_source/licensing/flossexception
*
* By copying, modifying or distributing this software, you acknowledge
* that you have read and understood your obligations described above,
* and agree to abide by those obligations.
*
* ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
* WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
* COMPLETENESS OR PERFORMANCE.
* $/LicenseInfo$
*/
#ifndef LL_LLIMAGEJ2C_H
#define LL_LLIMAGEJ2C_H
#include "llimage.h"
#include "llassettype.h"
class LLImageJ2CImpl;
class LLImageJ2C : public LLImageFormatted
{
protected:
virtual ~LLImageJ2C();
public:
LLImageJ2C();
// Base class overrides
/*virtual*/ std::string getExtension() { return std::string("j2c"); }
/*virtual*/ BOOL updateData();
/*virtual*/ BOOL decode(LLImageRaw *raw_imagep, F32 decode_time);
/*virtual*/ BOOL decodeChannels(LLImageRaw *raw_imagep, F32 decode_time, S32 first_channel, S32 max_channel_count);
/*virtual*/ BOOL encode(const LLImageRaw *raw_imagep, F32 encode_time);
/*virtual*/ S32 calcHeaderSize();
/*virtual*/ S32 calcDataSize(S32 discard_level = 0);
/*virtual*/ S32 calcDiscardLevelBytes(S32 bytes);
/*virtual*/ S8 getRawDiscardLevel();
// Override these so that we don't try to set a global variable from a DLL
/*virtual*/ void resetLastError();
/*virtual*/ void setLastError(const std::string& message, const std::string& filename = std::string());
// Encode with comment text
BOOL encode(const LLImageRaw *raw_imagep, const char* comment_text, F32 encode_time=0.0);
BOOL validate(U8 *data, U32 file_size);
BOOL loadAndValidate(const std::string &filename);
// Encode accessors
void setReversible(const BOOL reversible); // Use non-lossy?
void setRate(F32 rate);
void setMaxBytes(S32 max_bytes);
S32 getMaxBytes() const { return mMaxBytes; }
static S32 calcHeaderSizeJ2C();
static S32 calcDataSizeJ2C(S32 w, S32 h, S32 comp, S32 discard_level, F32 rate = 0.f);
static void openDSO();
static void closeDSO();
static std::string getEngineInfo();
protected:
friend class LLImageJ2CImpl;
friend class LLImageJ2COJ;
friend class LLImageJ2CKDU;
void decodeFailed();
void updateRawDiscardLevel();
S32 mMaxBytes; // Maximum number of bytes of data to use...
S8 mRawDiscardLevel;
F32 mRate;
BOOL mReversible;
LLImageJ2CImpl *mImpl;
std::string mLastError;
};
// Derive from this class to implement JPEG2000 decoding
class LLImageJ2CImpl
{
public:
virtual ~LLImageJ2CImpl();
protected:
// Find out the image size and number of channels.
// Return value:
// true: image size and number of channels was determined
// false: error on decode
virtual BOOL getMetadata(LLImageJ2C &base) = 0;
// Decode the raw image optionally aborting (to continue later) after
// decode_time seconds. Decode at most max_channel_count and start
// decoding channel first_channel.
// Return value:
// true: decoding complete (even if it failed)
// false: time expired while decoding
virtual BOOL decodeImpl(LLImageJ2C &base, LLImageRaw &raw_image, F32 decode_time, S32 first_channel, S32 max_channel_count) = 0;
virtual BOOL encodeImpl(LLImageJ2C &base, const LLImageRaw &raw_image, const char* comment_text, F32 encode_time=0.0,
BOOL reversible=FALSE) = 0;
friend class LLImageJ2C;
};
#define LINDEN_J2C_COMMENT_PREFIX "LL_"
#endif

View File

@@ -0,0 +1,654 @@
/**
* @file llimagejpeg.cpp
*
* $LicenseInfo:firstyear=2002&license=viewergpl$
*
* Copyright (c) 2002-2009, Linden Research, Inc.
*
* Second Life Viewer Source Code
* The source code in this file ("Source Code") is provided by Linden Lab
* to you under the terms of the GNU General Public License, version 2.0
* ("GPL"), unless you have obtained a separate licensing agreement
* ("Other License"), formally executed by you and Linden Lab. Terms of
* the GPL can be found in doc/GPL-license.txt in this distribution, or
* online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
*
* There are special exceptions to the terms and conditions of the GPL as
* it is applied to this Source Code. View the full text of the exception
* in the file doc/FLOSS-exception.txt in this software distribution, or
* online at
* http://secondlifegrid.net/programs/open_source/licensing/flossexception
*
* By copying, modifying or distributing this software, you acknowledge
* that you have read and understood your obligations described above,
* and agree to abide by those obligations.
*
* ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
* WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
* COMPLETENESS OR PERFORMANCE.
* $/LicenseInfo$
*/
#include "linden_common.h"
#include "stdtypes.h"
#include "llimagejpeg.h"
#include "llerror.h"
jmp_buf LLImageJPEG::sSetjmpBuffer ;
LLImageJPEG::LLImageJPEG(S32 quality)
:
LLImageFormatted(IMG_CODEC_JPEG),
mOutputBuffer( NULL ),
mOutputBufferSize( 0 ),
mEncodeQuality( quality ) // on a scale from 1 to 100
{
}
LLImageJPEG::~LLImageJPEG()
{
llassert( !mOutputBuffer ); // Should already be deleted at end of encode.
delete[] mOutputBuffer;
}
BOOL LLImageJPEG::updateData()
{
resetLastError();
// Check to make sure that this instance has been initialized with data
if (!getData() || (0 == getDataSize()))
{
setLastError("Uninitialized instance of LLImageJPEG");
return FALSE;
}
////////////////////////////////////////
// Step 1: allocate and initialize JPEG decompression object
// This struct contains the JPEG decompression parameters and pointers to
// working space (which is allocated as needed by the JPEG library).
struct jpeg_decompress_struct cinfo;
cinfo.client_data = this;
struct jpeg_error_mgr jerr;
cinfo.err = jpeg_std_error(&jerr);
// Customize with our own callbacks
jerr.error_exit = &LLImageJPEG::errorExit; // Error exit handler: does not return to caller
jerr.emit_message = &LLImageJPEG::errorEmitMessage; // Conditionally emit a trace or warning message
jerr.output_message = &LLImageJPEG::errorOutputMessage; // Routine that actually outputs a trace or error message
//
//try/catch will crash on Mac and Linux if LLImageJPEG::errorExit throws an error
//so as instead, we use setjmp/longjmp to avoid this crash, which is the best we can get. --bao
//
if(setjmp(sSetjmpBuffer))
{
jpeg_destroy_decompress(&cinfo);
return FALSE;
}
try
{
// Now we can initialize the JPEG decompression object.
jpeg_create_decompress(&cinfo);
////////////////////////////////////////
// Step 2: specify data source
// (Code is modified version of jpeg_stdio_src();
if (cinfo.src == NULL)
{
cinfo.src = (struct jpeg_source_mgr *)
(*cinfo.mem->alloc_small) ((j_common_ptr) &cinfo, JPOOL_PERMANENT,
sizeof(struct jpeg_source_mgr));
}
cinfo.src->init_source = &LLImageJPEG::decodeInitSource;
cinfo.src->fill_input_buffer = &LLImageJPEG::decodeFillInputBuffer;
cinfo.src->skip_input_data = &LLImageJPEG::decodeSkipInputData;
cinfo.src->resync_to_restart = jpeg_resync_to_restart; // For now, use default method, but we should be able to do better.
cinfo.src->term_source = &LLImageJPEG::decodeTermSource;
cinfo.src->bytes_in_buffer = getDataSize();
cinfo.src->next_input_byte = getData();
////////////////////////////////////////
// Step 3: read file parameters with jpeg_read_header()
jpeg_read_header( &cinfo, TRUE );
// Data set by jpeg_read_header
setSize(cinfo.image_width, cinfo.image_height, 3); // Force to 3 components (RGB)
/*
// More data set by jpeg_read_header
cinfo.num_components;
cinfo.jpeg_color_space; // Colorspace of image
cinfo.saw_JFIF_marker; // TRUE if a JFIF APP0 marker was seen
cinfo.JFIF_major_version; // Version information from JFIF marker
cinfo.JFIF_minor_version; //
cinfo.density_unit; // Resolution data from JFIF marker
cinfo.X_density;
cinfo.Y_density;
cinfo.saw_Adobe_marker; // TRUE if an Adobe APP14 marker was seen
cinfo.Adobe_transform; // Color transform code from Adobe marker
*/
}
catch (int)
{
jpeg_destroy_decompress(&cinfo);
return FALSE;
}
////////////////////////////////////////
// Step 4: Release JPEG decompression object
jpeg_destroy_decompress(&cinfo);
return TRUE;
}
// Initialize source --- called by jpeg_read_header
// before any data is actually read.
void LLImageJPEG::decodeInitSource( j_decompress_ptr cinfo )
{
// no work necessary here
}
// Fill the input buffer --- called whenever buffer is emptied.
boolean LLImageJPEG::decodeFillInputBuffer( j_decompress_ptr cinfo )
{
// jpeg_source_mgr* src = cinfo->src;
// LLImageJPEG* self = (LLImageJPEG*) cinfo->client_data;
// Should never get here, since we provide the entire buffer up front.
ERREXIT(cinfo, JERR_INPUT_EMPTY);
return TRUE;
}
// Skip data --- used to skip over a potentially large amount of
// uninteresting data (such as an APPn marker).
//
// Writers of suspendable-input applications must note that skip_input_data
// is not granted the right to give a suspension return. If the skip extends
// beyond the data currently in the buffer, the buffer can be marked empty so
// that the next read will cause a fill_input_buffer call that can suspend.
// Arranging for additional bytes to be discarded before reloading the input
// buffer is the application writer's problem.
void LLImageJPEG::decodeSkipInputData (j_decompress_ptr cinfo, long num_bytes)
{
jpeg_source_mgr* src = cinfo->src;
// LLImageJPEG* self = (LLImageJPEG*) cinfo->client_data;
src->next_input_byte += (size_t) num_bytes;
src->bytes_in_buffer -= (size_t) num_bytes;
}
void LLImageJPEG::decodeTermSource (j_decompress_ptr cinfo)
{
// no work necessary here
}
// Returns true when done, whether or not decode was successful.
BOOL LLImageJPEG::decode(LLImageRaw* raw_image, F32 decode_time)
{
llassert_always(raw_image);
resetLastError();
// Check to make sure that this instance has been initialized with data
if (!getData() || (0 == getDataSize()))
{
setLastError("LLImageJPEG trying to decode an image with no data!");
return TRUE; // done
}
S32 row_stride = 0;
U8* raw_image_data = NULL;
////////////////////////////////////////
// Step 1: allocate and initialize JPEG decompression object
// This struct contains the JPEG decompression parameters and pointers to
// working space (which is allocated as needed by the JPEG library).
struct jpeg_decompress_struct cinfo;
struct jpeg_error_mgr jerr;
cinfo.err = jpeg_std_error(&jerr);
// Customize with our own callbacks
jerr.error_exit = &LLImageJPEG::errorExit; // Error exit handler: does not return to caller
jerr.emit_message = &LLImageJPEG::errorEmitMessage; // Conditionally emit a trace or warning message
jerr.output_message = &LLImageJPEG::errorOutputMessage; // Routine that actually outputs a trace or error message
//
//try/catch will crash on Mac and Linux if LLImageJPEG::errorExit throws an error
//so as instead, we use setjmp/longjmp to avoid this crash, which is the best we can get. --bao
//
if(setjmp(sSetjmpBuffer))
{
jpeg_destroy_decompress(&cinfo);
return TRUE; // done
}
try
{
// Now we can initialize the JPEG decompression object.
jpeg_create_decompress(&cinfo);
////////////////////////////////////////
// Step 2: specify data source
// (Code is modified version of jpeg_stdio_src();
if (cinfo.src == NULL)
{
cinfo.src = (struct jpeg_source_mgr *)
(*cinfo.mem->alloc_small) ((j_common_ptr) &cinfo, JPOOL_PERMANENT,
sizeof(struct jpeg_source_mgr));
}
cinfo.src->init_source = &LLImageJPEG::decodeInitSource;
cinfo.src->fill_input_buffer = &LLImageJPEG::decodeFillInputBuffer;
cinfo.src->skip_input_data = &LLImageJPEG::decodeSkipInputData;
cinfo.src->resync_to_restart = jpeg_resync_to_restart; // For now, use default method, but we should be able to do better.
cinfo.src->term_source = &LLImageJPEG::decodeTermSource;
cinfo.src->bytes_in_buffer = getDataSize();
cinfo.src->next_input_byte = getData();
////////////////////////////////////////
// Step 3: read file parameters with jpeg_read_header()
jpeg_read_header(&cinfo, TRUE);
// We can ignore the return value from jpeg_read_header since
// (a) suspension is not possible with our data source, and
// (b) we passed TRUE to reject a tables-only JPEG file as an error.
// See libjpeg.doc for more info.
setSize(cinfo.image_width, cinfo.image_height, 3); // Force to 3 components (RGB)
raw_image->resize(getWidth(), getHeight(), getComponents());
raw_image_data = raw_image->getData();
////////////////////////////////////////
// Step 4: set parameters for decompression
cinfo.out_color_components = 3;
cinfo.out_color_space = JCS_RGB;
////////////////////////////////////////
// Step 5: Start decompressor
jpeg_start_decompress(&cinfo);
// We can ignore the return value since suspension is not possible
// with our data source.
// We may need to do some setup of our own at this point before reading
// the data. After jpeg_start_decompress() we have the correct scaled
// output image dimensions available, as well as the output colormap
// if we asked for color quantization.
// In this example, we need to make an output work buffer of the right size.
// JSAMPLEs per row in output buffer
row_stride = cinfo.output_width * cinfo.output_components;
////////////////////////////////////////
// Step 6: while (scan lines remain to be read)
// jpeg_read_scanlines(...);
// Here we use the library's state variable cinfo.output_scanline as the
// loop counter, so that we don't have to keep track ourselves.
// Move pointer to last line
raw_image_data += row_stride * (cinfo.output_height - 1);
while (cinfo.output_scanline < cinfo.output_height)
{
// jpeg_read_scanlines expects an array of pointers to scanlines.
// Here the array is only one element long, but you could ask for
// more than one scanline at a time if that's more convenient.
jpeg_read_scanlines(&cinfo, &raw_image_data, 1);
raw_image_data -= row_stride; // move pointer up a line
}
////////////////////////////////////////
// Step 7: Finish decompression
jpeg_finish_decompress(&cinfo);
////////////////////////////////////////
// Step 8: Release JPEG decompression object
jpeg_destroy_decompress(&cinfo);
}
catch (int)
{
jpeg_destroy_decompress(&cinfo);
return TRUE; // done
}
// Check to see whether any corrupt-data warnings occurred
if( jerr.num_warnings != 0 )
{
// TODO: extract the warning to find out what went wrong.
setLastError( "Unable to decode JPEG image.");
return TRUE; // done
}
return TRUE;
}
// Initialize destination --- called by jpeg_start_compress before any data is actually written.
// static
void LLImageJPEG::encodeInitDestination ( j_compress_ptr cinfo )
{
LLImageJPEG* self = (LLImageJPEG*) cinfo->client_data;
cinfo->dest->next_output_byte = self->mOutputBuffer;
cinfo->dest->free_in_buffer = self->mOutputBufferSize;
}
// Empty the output buffer --- called whenever buffer fills up.
//
// In typical applications, this should write the entire output buffer
// (ignoring the current state of next_output_byte & free_in_buffer),
// reset the pointer & count to the start of the buffer, and return TRUE
// indicating that the buffer has been dumped.
//
// In applications that need to be able to suspend compression due to output
// overrun, a FALSE return indicates that the buffer cannot be emptied now.
// In this situation, the compressor will return to its caller (possibly with
// an indication that it has not accepted all the supplied scanlines). The
// application should resume compression after it has made more room in the
// output buffer. Note that there are substantial restrictions on the use of
// suspension --- see the documentation.
//
// When suspending, the compressor will back up to a convenient restart point
// (typically the start of the current MCU). next_output_byte & free_in_buffer
// indicate where the restart point will be if the current call returns FALSE.
// Data beyond this point will be regenerated after resumption, so do not
// write it out when emptying the buffer externally.
boolean LLImageJPEG::encodeEmptyOutputBuffer( j_compress_ptr cinfo )
{
LLImageJPEG* self = (LLImageJPEG*) cinfo->client_data;
// Should very rarely happen, since our output buffer is
// as large as the input to start out with.
// Double the buffer size;
S32 new_buffer_size = self->mOutputBufferSize * 2;
U8* new_buffer = new U8[ new_buffer_size ];
if (!new_buffer)
{
llerrs << "Out of memory in LLImageJPEG::encodeEmptyOutputBuffer( j_compress_ptr cinfo )" << llendl;
return FALSE;
}
memcpy( new_buffer, self->mOutputBuffer, self->mOutputBufferSize ); /* Flawfinder: ignore */
delete[] self->mOutputBuffer;
self->mOutputBuffer = new_buffer;
cinfo->dest->next_output_byte = self->mOutputBuffer + self->mOutputBufferSize;
cinfo->dest->free_in_buffer = self->mOutputBufferSize;
self->mOutputBufferSize = new_buffer_size;
return TRUE;
}
// Terminate destination --- called by jpeg_finish_compress
// after all data has been written. Usually needs to flush buffer.
//
// NB: *not* called by jpeg_abort or jpeg_destroy; surrounding
// application must deal with any cleanup that should happen even
// for error exit.
void LLImageJPEG::encodeTermDestination( j_compress_ptr cinfo )
{
LLImageJPEG* self = (LLImageJPEG*) cinfo->client_data;
S32 file_bytes = (S32)(self->mOutputBufferSize - cinfo->dest->free_in_buffer);
self->allocateData(file_bytes);
memcpy( self->getData(), self->mOutputBuffer, file_bytes ); /* Flawfinder: ignore */
}
// static
void LLImageJPEG::errorExit( j_common_ptr cinfo )
{
//LLImageJPEG* self = (LLImageJPEG*) cinfo->client_data;
// Always display the message
(*cinfo->err->output_message)(cinfo);
// Let the memory manager delete any temp files
jpeg_destroy(cinfo);
// Return control to the setjmp point
longjmp(sSetjmpBuffer, 1) ;
}
// Decide whether to emit a trace or warning message.
// msg_level is one of:
// -1: recoverable corrupt-data warning, may want to abort.
// 0: important advisory messages (always display to user).
// 1: first level of tracing detail.
// 2,3,...: successively more detailed tracing messages.
// An application might override this method if it wanted to abort on warnings
// or change the policy about which messages to display.
// static
void LLImageJPEG::errorEmitMessage( j_common_ptr cinfo, int msg_level )
{
struct jpeg_error_mgr * err = cinfo->err;
if (msg_level < 0)
{
// It's a warning message. Since corrupt files may generate many warnings,
// the policy implemented here is to show only the first warning,
// unless trace_level >= 3.
if (err->num_warnings == 0 || err->trace_level >= 3)
{
(*err->output_message) (cinfo);
}
// Always count warnings in num_warnings.
err->num_warnings++;
}
else
{
// It's a trace message. Show it if trace_level >= msg_level.
if (err->trace_level >= msg_level)
{
(*err->output_message) (cinfo);
}
}
}
// static
void LLImageJPEG::errorOutputMessage( j_common_ptr cinfo )
{
// Create the message
char buffer[JMSG_LENGTH_MAX]; /* Flawfinder: ignore */
(*cinfo->err->format_message) (cinfo, buffer);
std::string error = buffer ;
LLImage::setLastError(error);
BOOL is_decode = (cinfo->is_decompressor != 0);
llwarns << "LLImageJPEG " << (is_decode ? "decode " : "encode ") << " failed: " << buffer << llendl;
}
BOOL LLImageJPEG::encode( const LLImageRaw* raw_image, F32 encode_time )
{
llassert_always(raw_image);
resetLastError();
switch( raw_image->getComponents() )
{
case 1:
case 3:
break;
default:
setLastError("Unable to encode a JPEG image that doesn't have 1 or 3 components.");
return FALSE;
}
setSize(raw_image->getWidth(), raw_image->getHeight(), raw_image->getComponents());
// Allocate a temporary buffer big enough to hold the entire compressed image (and then some)
// (Note: we make it bigger in emptyOutputBuffer() if we need to)
delete[] mOutputBuffer;
mOutputBufferSize = getWidth() * getHeight() * getComponents() + 1024;
mOutputBuffer = new U8[ mOutputBufferSize ];
const U8* raw_image_data = NULL;
S32 row_stride = 0;
////////////////////////////////////////
// Step 1: allocate and initialize JPEG compression object
// This struct contains the JPEG compression parameters and pointers to
// working space (which is allocated as needed by the JPEG library).
struct jpeg_compress_struct cinfo;
cinfo.client_data = this;
// We have to set up the error handler first, in case the initialization
// step fails. (Unlikely, but it could happen if you are out of memory.)
// This routine fills in the contents of struct jerr, and returns jerr's
// address which we place into the link field in cinfo.
struct jpeg_error_mgr jerr;
cinfo.err = jpeg_std_error(&jerr);
// Customize with our own callbacks
jerr.error_exit = &LLImageJPEG::errorExit; // Error exit handler: does not return to caller
jerr.emit_message = &LLImageJPEG::errorEmitMessage; // Conditionally emit a trace or warning message
jerr.output_message = &LLImageJPEG::errorOutputMessage; // Routine that actually outputs a trace or error message
//
//try/catch will crash on Mac and Linux if LLImageJPEG::errorExit throws an error
//so as instead, we use setjmp/longjmp to avoid this crash, which is the best we can get. --bao
//
if( setjmp(sSetjmpBuffer) )
{
// If we get here, the JPEG code has signaled an error.
// We need to clean up the JPEG object, close the input file, and return.
jpeg_destroy_compress(&cinfo);
delete[] mOutputBuffer;
mOutputBuffer = NULL;
mOutputBufferSize = 0;
return FALSE;
}
try
{
// Now we can initialize the JPEG compression object.
jpeg_create_compress(&cinfo);
////////////////////////////////////////
// Step 2: specify data destination
// (code is a modified form of jpeg_stdio_dest() )
if( cinfo.dest == NULL)
{
cinfo.dest = (struct jpeg_destination_mgr *)
(*cinfo.mem->alloc_small) ((j_common_ptr) &cinfo, JPOOL_PERMANENT,
sizeof(struct jpeg_destination_mgr));
}
cinfo.dest->next_output_byte = mOutputBuffer; // => next byte to write in buffer
cinfo.dest->free_in_buffer = mOutputBufferSize; // # of byte spaces remaining in buffer
cinfo.dest->init_destination = &LLImageJPEG::encodeInitDestination;
cinfo.dest->empty_output_buffer = &LLImageJPEG::encodeEmptyOutputBuffer;
cinfo.dest->term_destination = &LLImageJPEG::encodeTermDestination;
////////////////////////////////////////
// Step 3: set parameters for compression
//
// First we supply a description of the input image.
// Four fields of the cinfo struct must be filled in:
cinfo.image_width = getWidth(); // image width and height, in pixels
cinfo.image_height = getHeight();
switch( getComponents() )
{
case 1:
cinfo.input_components = 1; // # of color components per pixel
cinfo.in_color_space = JCS_GRAYSCALE; // colorspace of input image
break;
case 3:
cinfo.input_components = 3; // # of color components per pixel
cinfo.in_color_space = JCS_RGB; // colorspace of input image
break;
default:
setLastError("Unable to encode a JPEG image that doesn't have 1 or 3 components.");
return FALSE;
}
// Now use the library's routine to set default compression parameters.
// (You must set at least cinfo.in_color_space before calling this,
// since the defaults depend on the source color space.)
jpeg_set_defaults(&cinfo);
// Now you can set any non-default parameters you wish to.
jpeg_set_quality(&cinfo, mEncodeQuality, TRUE ); // limit to baseline-JPEG values
////////////////////////////////////////
// Step 4: Start compressor
//
// TRUE ensures that we will write a complete interchange-JPEG file.
// Pass TRUE unless you are very sure of what you're doing.
jpeg_start_compress(&cinfo, TRUE);
////////////////////////////////////////
// Step 5: while (scan lines remain to be written)
// jpeg_write_scanlines(...);
// Here we use the library's state variable cinfo.next_scanline as the
// loop counter, so that we don't have to keep track ourselves.
// To keep things simple, we pass one scanline per call; you can pass
// more if you wish, though.
row_stride = getWidth() * getComponents(); // JSAMPLEs per row in image_buffer
// NOTE: For compatibility with LLImage, we need to invert the rows.
raw_image_data = raw_image->getData();
const U8* last_row_data = raw_image_data + (getHeight()-1) * row_stride;
JSAMPROW row_pointer[1]; // pointer to JSAMPLE row[s]
while (cinfo.next_scanline < cinfo.image_height)
{
// jpeg_write_scanlines expects an array of pointers to scanlines.
// Here the array is only one element long, but you could pass
// more than one scanline at a time if that's more convenient.
//Ugly const uncast here (jpeg_write_scanlines should take a const* but doesn't)
//row_pointer[0] = (JSAMPROW)(raw_image_data + (cinfo.next_scanline * row_stride));
row_pointer[0] = (JSAMPROW)(last_row_data - (cinfo.next_scanline * row_stride));
jpeg_write_scanlines(&cinfo, row_pointer, 1);
}
////////////////////////////////////////
// Step 6: Finish compression
jpeg_finish_compress(&cinfo);
// After finish_compress, we can release the temp output buffer.
delete[] mOutputBuffer;
mOutputBuffer = NULL;
mOutputBufferSize = 0;
////////////////////////////////////////
// Step 7: release JPEG compression object
jpeg_destroy_compress(&cinfo);
}
catch(int)
{
jpeg_destroy_compress(&cinfo);
delete[] mOutputBuffer;
mOutputBuffer = NULL;
mOutputBufferSize = 0;
return FALSE;
}
return TRUE;
}

View File

@@ -0,0 +1,92 @@
/**
* @file llimagejpeg.h
* @brief This class compresses and decompresses JPEG files
*
* $LicenseInfo:firstyear=2002&license=viewergpl$
*
* Copyright (c) 2002-2009, Linden Research, Inc.
*
* Second Life Viewer Source Code
* The source code in this file ("Source Code") is provided by Linden Lab
* to you under the terms of the GNU General Public License, version 2.0
* ("GPL"), unless you have obtained a separate licensing agreement
* ("Other License"), formally executed by you and Linden Lab. Terms of
* the GPL can be found in doc/GPL-license.txt in this distribution, or
* online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
*
* There are special exceptions to the terms and conditions of the GPL as
* it is applied to this Source Code. View the full text of the exception
* in the file doc/FLOSS-exception.txt in this software distribution, or
* online at
* http://secondlifegrid.net/programs/open_source/licensing/flossexception
*
* By copying, modifying or distributing this software, you acknowledge
* that you have read and understood your obligations described above,
* and agree to abide by those obligations.
*
* ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
* WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
* COMPLETENESS OR PERFORMANCE.
* $/LicenseInfo$
*/
#ifndef LL_LLIMAGEJPEG_H
#define LL_LLIMAGEJPEG_H
#include <csetjmp>
#include "llimage.h"
extern "C" {
#ifdef LL_STANDALONE
# include <jpeglib.h>
# include <jerror.h>
#else
# include "jpeglib/jpeglib.h"
# include "jpeglib/jerror.h"
#endif
}
class LLImageJPEG : public LLImageFormatted
{
protected:
virtual ~LLImageJPEG();
public:
LLImageJPEG(S32 quality = 75);
/*virtual*/ std::string getExtension() { return std::string("jpg"); }
/*virtual*/ BOOL updateData();
/*virtual*/ BOOL decode(LLImageRaw* raw_image, F32 decode_time);
/*virtual*/ BOOL encode(const LLImageRaw* raw_image, F32 encode_time);
void setEncodeQuality( S32 q ) { mEncodeQuality = q; } // on a scale from 1 to 100
S32 getEncodeQuality() { return mEncodeQuality; }
// Callbacks registered with jpeglib
static void encodeInitDestination ( j_compress_ptr cinfo );
static boolean encodeEmptyOutputBuffer(j_compress_ptr cinfo);
static void encodeTermDestination(j_compress_ptr cinfo);
static void decodeInitSource(j_decompress_ptr cinfo);
static boolean decodeFillInputBuffer(j_decompress_ptr cinfo);
static void decodeSkipInputData(j_decompress_ptr cinfo, long num_bytes);
static void decodeTermSource(j_decompress_ptr cinfo);
static void errorExit(j_common_ptr cinfo);
static void errorEmitMessage(j_common_ptr cinfo, int msg_level);
static void errorOutputMessage(j_common_ptr cinfo);
static BOOL decompress(LLImageJPEG* imagep);
protected:
U8* mOutputBuffer; // temp buffer used during encoding
S32 mOutputBufferSize; // bytes in mOuputBuffer
S32 mEncodeQuality; // on a scale from 1 to 100
private:
static jmp_buf sSetjmpBuffer; // To allow the library to abort.
};
#endif // LL_LLIMAGEJPEG_H

View File

@@ -0,0 +1,150 @@
/*
* @file llimagepng.cpp
* @brief LLImageFormatted glue to encode / decode PNG files.
*
* $LicenseInfo:firstyear=2007&license=viewergpl$
*
* Copyright (c) 2007-2009, Linden Research, Inc.
*
* Second Life Viewer Source Code
* The source code in this file ("Source Code") is provided by Linden Lab
* to you under the terms of the GNU General Public License, version 2.0
* ("GPL"), unless you have obtained a separate licensing agreement
* ("Other License"), formally executed by you and Linden Lab. Terms of
* the GPL can be found in doc/GPL-license.txt in this distribution, or
* online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
*
* There are special exceptions to the terms and conditions of the GPL as
* it is applied to this Source Code. View the full text of the exception
* in the file doc/FLOSS-exception.txt in this software distribution, or
* online at
* http://secondlifegrid.net/programs/open_source/licensing/flossexception
*
* By copying, modifying or distributing this software, you acknowledge
* that you have read and understood your obligations described above,
* and agree to abide by those obligations.
*
* ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
* WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
* COMPLETENESS OR PERFORMANCE.
* $/LicenseInfo$
*/
#include "linden_common.h"
#include "stdtypes.h"
#include "llerror.h"
#include "llimage.h"
#include "llpngwrapper.h"
#include "llimagepng.h"
// ---------------------------------------------------------------------------
// LLImagePNG
// ---------------------------------------------------------------------------
LLImagePNG::LLImagePNG()
: LLImageFormatted(IMG_CODEC_PNG),
mTmpWriteBuffer(NULL)
{
}
LLImagePNG::~LLImagePNG()
{
if (mTmpWriteBuffer)
{
delete[] mTmpWriteBuffer;
}
}
// Virtual
// Parse PNG image information and set the appropriate
// width, height and component (channel) information.
BOOL LLImagePNG::updateData()
{
resetLastError();
// Check to make sure that this instance has been initialized with data
if (!getData() || (0 == getDataSize()))
{
setLastError("Uninitialized instance of LLImagePNG");
return FALSE;
}
// Decode the PNG data and extract sizing information
LLPngWrapper pngWrapper;
LLPngWrapper::ImageInfo infop;
if (! pngWrapper.readPng(getData(), NULL, &infop))
{
setLastError(pngWrapper.getErrorMessage());
return FALSE;
}
setSize(infop.mWidth, infop.mHeight, infop.mComponents);
return TRUE;
}
// Virtual
// Decode an in-memory PNG image into the raw RGB or RGBA format
// used within SecondLife.
BOOL LLImagePNG::decode(LLImageRaw* raw_image, F32 decode_time)
{
llassert_always(raw_image);
resetLastError();
// Check to make sure that this instance has been initialized with data
if (!getData() || (0 == getDataSize()))
{
setLastError("LLImagePNG trying to decode an image with no data!");
return FALSE;
}
// Decode the PNG data into the raw image
LLPngWrapper pngWrapper;
if (! pngWrapper.readPng(getData(), raw_image))
{
setLastError(pngWrapper.getErrorMessage());
return FALSE;
}
return TRUE;
}
// Virtual
// Encode the in memory RGB image into PNG format.
BOOL LLImagePNG::encode(const LLImageRaw* raw_image, F32 encode_time)
{
llassert_always(raw_image);
resetLastError();
// Image logical size
setSize(raw_image->getWidth(), raw_image->getHeight(), raw_image->getComponents());
// Temporary buffer to hold the encoded image. Note: the final image
// size should be much smaller due to compression.
if (mTmpWriteBuffer)
{
delete[] mTmpWriteBuffer;
}
U32 bufferSize = getWidth() * getHeight() * getComponents() + 1024;
U8* mTmpWriteBuffer = new U8[ bufferSize ];
// Delegate actual encoding work to wrapper
LLPngWrapper pngWrapper;
if (! pngWrapper.writePng(raw_image, mTmpWriteBuffer))
{
setLastError(pngWrapper.getErrorMessage());
return FALSE;
}
// Resize internal buffer and copy from temp
U32 encodedSize = pngWrapper.getFinalSize();
allocateData(encodedSize);
memcpy(getData(), mTmpWriteBuffer, encodedSize);
delete[] mTmpWriteBuffer;
return TRUE;
}

View File

@@ -0,0 +1,55 @@
/*
* @file llimagepng.h
*
* $LicenseInfo:firstyear=2007&license=viewergpl$
*
* Copyright (c) 2007-2009, Linden Research, Inc.
*
* Second Life Viewer Source Code
* The source code in this file ("Source Code") is provided by Linden Lab
* to you under the terms of the GNU General Public License, version 2.0
* ("GPL"), unless you have obtained a separate licensing agreement
* ("Other License"), formally executed by you and Linden Lab. Terms of
* the GPL can be found in doc/GPL-license.txt in this distribution, or
* online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
*
* There are special exceptions to the terms and conditions of the GPL as
* it is applied to this Source Code. View the full text of the exception
* in the file doc/FLOSS-exception.txt in this software distribution, or
* online at
* http://secondlifegrid.net/programs/open_source/licensing/flossexception
*
* By copying, modifying or distributing this software, you acknowledge
* that you have read and understood your obligations described above,
* and agree to abide by those obligations.
*
* ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
* WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
* COMPLETENESS OR PERFORMANCE.
* $/LicenseInfo$
*/
#ifndef LL_LLIMAGEPNG_H
#define LL_LLIMAGEPNG_H
#include "stdtypes.h"
#include "llimage.h"
class LLImagePNG : public LLImageFormatted
{
protected:
~LLImagePNG();
public:
LLImagePNG();
/*virtual*/ std::string getExtension() { return std::string("png"); }
/*virtual*/ BOOL updateData();
/*virtual*/ BOOL decode(LLImageRaw* raw_image, F32 decode_time);
/*virtual*/ BOOL encode(const LLImageRaw* raw_image, F32 encode_time);
private:
U8* mTmpWriteBuffer;
};
#endif

1189
indra/llimage/llimagetga.cpp Normal file

File diff suppressed because it is too large Load Diff

114
indra/llimage/llimagetga.h Normal file
View File

@@ -0,0 +1,114 @@
/**
* @file llimagetga.h
* @brief Image implementation to compresses and decompressed TGA files.
*
* $LicenseInfo:firstyear=2001&license=viewergpl$
*
* Copyright (c) 2001-2009, Linden Research, Inc.
*
* Second Life Viewer Source Code
* The source code in this file ("Source Code") is provided by Linden Lab
* to you under the terms of the GNU General Public License, version 2.0
* ("GPL"), unless you have obtained a separate licensing agreement
* ("Other License"), formally executed by you and Linden Lab. Terms of
* the GPL can be found in doc/GPL-license.txt in this distribution, or
* online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
*
* There are special exceptions to the terms and conditions of the GPL as
* it is applied to this Source Code. View the full text of the exception
* in the file doc/FLOSS-exception.txt in this software distribution, or
* online at
* http://secondlifegrid.net/programs/open_source/licensing/flossexception
*
* By copying, modifying or distributing this software, you acknowledge
* that you have read and understood your obligations described above,
* and agree to abide by those obligations.
*
* ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
* WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
* COMPLETENESS OR PERFORMANCE.
* $/LicenseInfo$
*/
#ifndef LL_LLIMAGETGA_H
#define LL_LLIMAGETGA_H
#include "llimage.h"
// This class compresses and decompressed TGA (targa) files
class LLImageTGA : public LLImageFormatted
{
protected:
virtual ~LLImageTGA();
public:
LLImageTGA();
LLImageTGA(const std::string& file_name);
/*virtual*/ std::string getExtension() { return std::string("tga"); }
/*virtual*/ BOOL updateData();
/*virtual*/ BOOL decode(LLImageRaw* raw_image, F32 decode_time=0.0);
/*virtual*/ BOOL encode(const LLImageRaw* raw_image, F32 encode_time=0.0);
BOOL decodeAndProcess(LLImageRaw* raw_image, F32 domain, F32 weight);
private:
BOOL decodeTruecolor( LLImageRaw* raw_image, BOOL rle, BOOL flipped );
BOOL decodeTruecolorRle8( LLImageRaw* raw_image );
BOOL decodeTruecolorRle15( LLImageRaw* raw_image );
BOOL decodeTruecolorRle24( LLImageRaw* raw_image );
BOOL decodeTruecolorRle32( LLImageRaw* raw_image, BOOL &alpha_opaque );
void decodeTruecolorPixel15( U8* dst, const U8* src );
BOOL decodeTruecolorNonRle( LLImageRaw* raw_image, BOOL &alpha_opaque );
BOOL decodeColorMap( LLImageRaw* raw_image, BOOL rle, BOOL flipped );
void decodeColorMapPixel8(U8* dst, const U8* src);
void decodeColorMapPixel15(U8* dst, const U8* src);
void decodeColorMapPixel24(U8* dst, const U8* src);
void decodeColorMapPixel32(U8* dst, const U8* src);
bool loadFile(const std::string& file_name);
private:
// Class specific data
U32 mDataOffset; // Offset from start of data to the actual header.
// Data from header
U8 mIDLength; // Length of identifier string
U8 mColorMapType; // 0 = No Map
U8 mImageType; // Supported: 2 = Uncompressed true color, 3 = uncompressed monochrome without colormap
U8 mColorMapIndexLo; // First color map entry (low order byte)
U8 mColorMapIndexHi; // First color map entry (high order byte)
U8 mColorMapLengthLo; // Color map length (low order byte)
U8 mColorMapLengthHi; // Color map length (high order byte)
U8 mColorMapDepth; // Size of color map entry (15, 16, 24, or 32 bits)
U8 mXOffsetLo; // X offset of image (low order byte)
U8 mXOffsetHi; // X offset of image (hi order byte)
U8 mYOffsetLo; // Y offset of image (low order byte)
U8 mYOffsetHi; // Y offset of image (hi order byte)
U8 mWidthLo; // Width (low order byte)
U8 mWidthHi; // Width (hi order byte)
U8 mHeightLo; // Height (low order byte)
U8 mHeightHi; // Height (hi order byte)
U8 mPixelSize; // 8, 16, 24, 32 bits per pixel
U8 mAttributeBits; // 4 bits: number of attributes per pixel
U8 mOriginRightBit; // 1 bit: origin, 0 = left, 1 = right
U8 mOriginTopBit; // 1 bit: origin, 0 = bottom, 1 = top
U8 mInterleave; // 2 bits: interleaved flag, 0 = none, 1 = interleaved 2, 2 = interleaved 4
U8* mColorMap;
S32 mColorMapStart;
S32 mColorMapLength;
S32 mColorMapBytesPerEntry;
BOOL mIs15Bit;
static const U8 s5to8bits[32];
};
#endif

View File

@@ -0,0 +1,174 @@
/**
* @file llimageworker.cpp
* @brief Base class for images.
*
* $LicenseInfo:firstyear=2001&license=viewergpl$
*
* Copyright (c) 2001-2009, Linden Research, Inc.
*
* Second Life Viewer Source Code
* The source code in this file ("Source Code") is provided by Linden Lab
* to you under the terms of the GNU General Public License, version 2.0
* ("GPL"), unless you have obtained a separate licensing agreement
* ("Other License"), formally executed by you and Linden Lab. Terms of
* the GPL can be found in doc/GPL-license.txt in this distribution, or
* online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
*
* There are special exceptions to the terms and conditions of the GPL as
* it is applied to this Source Code. View the full text of the exception
* in the file doc/FLOSS-exception.txt in this software distribution, or
* online at
* http://secondlifegrid.net/programs/open_source/licensing/flossexception
*
* By copying, modifying or distributing this software, you acknowledge
* that you have read and understood your obligations described above,
* and agree to abide by those obligations.
*
* ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
* WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
* COMPLETENESS OR PERFORMANCE.
* $/LicenseInfo$
*/
#include "linden_common.h"
#include "llimageworker.h"
#include "llimagedxt.h"
//----------------------------------------------------------------------------
// MAIN THREAD
LLImageDecodeThread::LLImageDecodeThread(bool threaded)
: LLQueuedThread("imagedecode", threaded)
{
mCreationMutex = new LLMutex(getAPRPool());
}
// MAIN THREAD
// virtual
S32 LLImageDecodeThread::update(U32 max_time_ms)
{
LLMutexLock lock(mCreationMutex);
for (creation_list_t::iterator iter = mCreationList.begin();
iter != mCreationList.end(); ++iter)
{
creation_info& info = *iter;
ImageRequest* req = new ImageRequest(info.handle, info.image,
info.priority, info.discard, info.needs_aux,
info.responder);
addRequest(req);
}
mCreationList.clear();
S32 res = LLQueuedThread::update(max_time_ms);
return res;
}
LLImageDecodeThread::handle_t LLImageDecodeThread::decodeImage(LLImageFormatted* image,
U32 priority, S32 discard, BOOL needs_aux, Responder* responder)
{
LLMutexLock lock(mCreationMutex);
handle_t handle = generateHandle();
mCreationList.push_back(creation_info(handle, image, priority, discard, needs_aux, responder));
return handle;
}
// Used by unit test only
// Returns the size of the mutex guarded list as an indication of sanity
S32 LLImageDecodeThread::tut_size()
{
LLMutexLock lock(mCreationMutex);
S32 res = mCreationList.size();
return res;
}
LLImageDecodeThread::Responder::~Responder()
{
}
//----------------------------------------------------------------------------
LLImageDecodeThread::ImageRequest::ImageRequest(handle_t handle, LLImageFormatted* image,
U32 priority, S32 discard, BOOL needs_aux,
LLImageDecodeThread::Responder* responder)
: LLQueuedThread::QueuedRequest(handle, priority, FLAG_AUTO_COMPLETE),
mFormattedImage(image),
mDiscardLevel(discard),
mNeedsAux(needs_aux),
mDecodedRaw(FALSE),
mDecodedAux(FALSE),
mResponder(responder)
{
}
LLImageDecodeThread::ImageRequest::~ImageRequest()
{
mDecodedImageRaw = NULL;
mDecodedImageAux = NULL;
mFormattedImage = NULL;
}
//----------------------------------------------------------------------------
// Returns true when done, whether or not decode was successful.
bool LLImageDecodeThread::ImageRequest::processRequest()
{
const F32 decode_time_slice = .1f;
bool done = true;
if (!mDecodedRaw && mFormattedImage.notNull())
{
// Decode primary channels
if (mDecodedImageRaw.isNull())
{
// parse formatted header
if (!mFormattedImage->updateData())
{
return true; // done (failed)
}
if (!(mFormattedImage->getWidth() * mFormattedImage->getHeight() * mFormattedImage->getComponents()))
{
return true; // done (failed)
}
if (mDiscardLevel >= 0)
{
mFormattedImage->setDiscardLevel(mDiscardLevel);
}
mDecodedImageRaw = new LLImageRaw(mFormattedImage->getWidth(),
mFormattedImage->getHeight(),
mFormattedImage->getComponents());
}
done = mFormattedImage->decode(mDecodedImageRaw, decode_time_slice); // 1ms
mDecodedRaw = done;
}
if (done && mNeedsAux && !mDecodedAux && mFormattedImage.notNull())
{
// Decode aux channel
if (!mDecodedImageAux)
{
mDecodedImageAux = new LLImageRaw(mFormattedImage->getWidth(),
mFormattedImage->getHeight(),
1);
}
done = mFormattedImage->decodeChannels(mDecodedImageAux, decode_time_slice, 4, 4); // 1ms
mDecodedAux = done;
}
return done;
}
void LLImageDecodeThread::ImageRequest::finishRequest(bool completed)
{
if (mResponder.notNull())
{
bool success = completed && mDecodedRaw && (!mNeedsAux || mDecodedAux);
mResponder->completed(success, mDecodedImageRaw, mDecodedImageAux);
}
// Will automatically be deleted
}
// Used by unit test only
// Checks that a responder exists for this instance so that something can happen when completion is reached
bool LLImageDecodeThread::ImageRequest::tut_isOK()
{
return mResponder.notNull();
}

View File

@@ -0,0 +1,107 @@
/**
* @file llimageworker.h
* @brief Object for managing images and their textures.
*
* $LicenseInfo:firstyear=2000&license=viewergpl$
*
* Copyright (c) 2000-2009, Linden Research, Inc.
*
* Second Life Viewer Source Code
* The source code in this file ("Source Code") is provided by Linden Lab
* to you under the terms of the GNU General Public License, version 2.0
* ("GPL"), unless you have obtained a separate licensing agreement
* ("Other License"), formally executed by you and Linden Lab. Terms of
* the GPL can be found in doc/GPL-license.txt in this distribution, or
* online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
*
* There are special exceptions to the terms and conditions of the GPL as
* it is applied to this Source Code. View the full text of the exception
* in the file doc/FLOSS-exception.txt in this software distribution, or
* online at
* http://secondlifegrid.net/programs/open_source/licensing/flossexception
*
* By copying, modifying or distributing this software, you acknowledge
* that you have read and understood your obligations described above,
* and agree to abide by those obligations.
*
* ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
* WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
* COMPLETENESS OR PERFORMANCE.
* $/LicenseInfo$
*/
#ifndef LL_LLIMAGEWORKER_H
#define LL_LLIMAGEWORKER_H
#include "llimage.h"
#include "llqueuedthread.h"
class LLImageDecodeThread : public LLQueuedThread
{
public:
class Responder : public LLThreadSafeRefCount
{
protected:
virtual ~Responder();
public:
virtual void completed(bool success, LLImageRaw* raw, LLImageRaw* aux) = 0;
};
class ImageRequest : public LLQueuedThread::QueuedRequest
{
protected:
virtual ~ImageRequest(); // use deleteRequest()
public:
ImageRequest(handle_t handle, LLImageFormatted* image,
U32 priority, S32 discard, BOOL needs_aux,
LLImageDecodeThread::Responder* responder);
/*virtual*/ bool processRequest();
/*virtual*/ void finishRequest(bool completed);
// Used by unit tests to check the consitency of the request instance
bool tut_isOK();
private:
// input
LLPointer<LLImageFormatted> mFormattedImage;
S32 mDiscardLevel;
BOOL mNeedsAux;
// output
LLPointer<LLImageRaw> mDecodedImageRaw;
LLPointer<LLImageRaw> mDecodedImageAux;
BOOL mDecodedRaw;
BOOL mDecodedAux;
LLPointer<LLImageDecodeThread::Responder> mResponder;
};
public:
LLImageDecodeThread(bool threaded = true);
handle_t decodeImage(LLImageFormatted* image,
U32 priority, S32 discard, BOOL needs_aux,
Responder* responder);
S32 update(U32 max_time_ms);
// Used by unit tests to check the consistency of the thread instance
S32 tut_size();
private:
struct creation_info
{
handle_t handle;
LLImageFormatted* image;
U32 priority;
S32 discard;
BOOL needs_aux;
LLPointer<Responder> responder;
creation_info(handle_t h, LLImageFormatted* i, U32 p, S32 d, BOOL aux, Responder* r)
: handle(h), image(i), priority(p), discard(d), needs_aux(aux), responder(r)
{}
};
typedef std::list<creation_info> creation_list_t;
creation_list_t mCreationList;
LLMutex* mCreationMutex;
};
#endif

View File

@@ -0,0 +1,46 @@
/**
* @file llmapimagetype.h
*
* $LicenseInfo:firstyear=2003&license=viewergpl$
*
* Copyright (c) 2003-2009, Linden Research, Inc.
*
* Second Life Viewer Source Code
* The source code in this file ("Source Code") is provided by Linden Lab
* to you under the terms of the GNU General Public License, version 2.0
* ("GPL"), unless you have obtained a separate licensing agreement
* ("Other License"), formally executed by you and Linden Lab. Terms of
* the GPL can be found in doc/GPL-license.txt in this distribution, or
* online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
*
* There are special exceptions to the terms and conditions of the GPL as
* it is applied to this Source Code. View the full text of the exception
* in the file doc/FLOSS-exception.txt in this software distribution, or
* online at
* http://secondlifegrid.net/programs/open_source/licensing/flossexception
*
* By copying, modifying or distributing this software, you acknowledge
* that you have read and understood your obligations described above,
* and agree to abide by those obligations.
*
* ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
* WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
* COMPLETENESS OR PERFORMANCE.
* $/LicenseInfo$
*/
#ifndef LL_LLMAPIMAGETYPE_H
#define LL_LLMAPIMAGETYPE_H
typedef enum e_map_image_type
{
MIT_TERRAIN = 0,
MIT_POPULAR = 1,
MIT_OBJECTS = 2,
MIT_OBJECTS_FOR_SALE = 3,
MIT_LAND_TO_BUY = 4,
MIT_OBJECT_NEW = 5,
MIT_EOF = 6
} EMapImageType;
#endif

View File

@@ -0,0 +1,396 @@
/*
* @file llpngwrapper.cpp
* @brief Encapsulates libpng read/write functionality.
*
* $LicenseInfo:firstyear=2007&license=viewergpl$
*
* Copyright (c) 2007-2009, Linden Research, Inc.
*
* Second Life Viewer Source Code
* The source code in this file ("Source Code") is provided by Linden Lab
* to you under the terms of the GNU General Public License, version 2.0
* ("GPL"), unless you have obtained a separate licensing agreement
* ("Other License"), formally executed by you and Linden Lab. Terms of
* the GPL can be found in doc/GPL-license.txt in this distribution, or
* online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
*
* There are special exceptions to the terms and conditions of the GPL as
* it is applied to this Source Code. View the full text of the exception
* in the file doc/FLOSS-exception.txt in this software distribution, or
* online at
* http://secondlifegrid.net/programs/open_source/licensing/flossexception
*
* By copying, modifying or distributing this software, you acknowledge
* that you have read and understood your obligations described above,
* and agree to abide by those obligations.
*
* ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
* WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
* COMPLETENESS OR PERFORMANCE.
* $/LicenseInfo$
*/
#include "linden_common.h"
#include "stdtypes.h"
#include "llerror.h"
#include "llimage.h"
#include "llpngwrapper.h"
// ---------------------------------------------------------------------------
// LLPngWrapper
// ---------------------------------------------------------------------------
LLPngWrapper::LLPngWrapper()
: mReadPngPtr( NULL ),
mReadInfoPtr( NULL ),
mWritePngPtr( NULL ),
mWriteInfoPtr( NULL ),
mRowPointers( NULL ),
mWidth( 0 ),
mHeight( 0 ),
mBitDepth( 0 ),
mColorType( 0 ),
mChannels( 0 ),
mInterlaceType( 0 ),
mCompressionType( 0 ),
mFilterMethod( 0 ),
mFinalSize( 0 ),
mHasBKGD(false),
mBackgroundColor(),
mGamma(0.f)
{
}
LLPngWrapper::~LLPngWrapper()
{
releaseResources();
}
// Checks the src for a valid PNG header
BOOL LLPngWrapper::isValidPng(U8* src)
{
const int PNG_BYTES_TO_CHECK = 8;
int sig = png_sig_cmp((png_bytep)src, (png_size_t)0, PNG_BYTES_TO_CHECK);
if (sig != 0)
{
mErrorMessage = "Invalid or corrupt PNG file";
return FALSE;
}
return TRUE;
}
// Called by the libpng library when a fatal encoding or decoding error
// occurs. We simply throw the error message and let our try/catch
// block clean up.
void LLPngWrapper::errorHandler(png_structp png_ptr, png_const_charp msg)
{
throw msg;
}
// Called by the libpng library when reading (decoding) the PNG file. We
// copy the PNG data from our internal buffer into the PNG's data buffer.
void LLPngWrapper::readDataCallback(png_structp png_ptr, png_bytep dest, png_size_t length)
{
PngDataInfo *dataInfo = (PngDataInfo *) png_get_io_ptr(png_ptr);
U8 *src = &dataInfo->mData[dataInfo->mOffset];
memcpy(dest, src, length);
dataInfo->mOffset += static_cast<U32>(length);
}
// Called by the libpng library when writing (encoding) the PNG file. We
// copy the encoded result into our data buffer.
void LLPngWrapper::writeDataCallback(png_structp png_ptr, png_bytep src, png_size_t length)
{
PngDataInfo *dataInfo = (PngDataInfo *) png_get_io_ptr(png_ptr);
U8 *dest = &dataInfo->mData[dataInfo->mOffset];
memcpy(dest, src, length);
dataInfo->mOffset += static_cast<U32>(length);
}
// Flush the write output pointer
void LLPngWrapper::writeFlush(png_structp png_ptr)
{
// no-op since we're just writing to memory
}
// Read the PNG file using the libpng. The low-level interface is used here
// because we want to do various transformations (including setting the
// matte background if any, and applying gama) which can't be done with
// the high-level interface. The scanline also begins at the bottom of
// the image (per SecondLife conventions) instead of at the top, so we
// must assign row-pointers in "reverse" order.
BOOL LLPngWrapper::readPng(U8* src, LLImageRaw* rawImage, ImageInfo *infop)
{
try
{
// Create and initialize the png structures
mReadPngPtr = png_create_read_struct(PNG_LIBPNG_VER_STRING,
this, &errorHandler, NULL);
if (mReadPngPtr == NULL)
{
throw "Problem creating png read structure";
}
// Allocate/initialize the memory for image information.
mReadInfoPtr = png_create_info_struct(mReadPngPtr);
// Set up the input control
PngDataInfo dataPtr;
dataPtr.mData = src;
dataPtr.mOffset = 0;
png_set_read_fn(mReadPngPtr, &dataPtr, &readDataCallback);
png_set_sig_bytes(mReadPngPtr, 0);
// setup low-level read and get header information
png_read_info(mReadPngPtr, mReadInfoPtr);
png_get_IHDR(mReadPngPtr, mReadInfoPtr, &mWidth, &mHeight,
&mBitDepth, &mColorType, &mInterlaceType,
&mCompressionType, &mFilterMethod);
// Normalize the image, then get updated image information
// after transformations have been applied
normalizeImage();
updateMetaData();
// If a raw object is supplied, read the PNG image into its
// data space
if (rawImage != NULL)
{
rawImage->resize(static_cast<U16>(mWidth),
static_cast<U16>(mHeight), mChannels);
U8 *dest = rawImage->getData();
int offset = mWidth * mChannels;
// Set up the row pointers and read the image
mRowPointers = new U8* [mHeight];
for (U32 i=0; i < mHeight; i++)
{
mRowPointers[i] = &dest[(mHeight-i-1)*offset];
}
png_read_image(mReadPngPtr, mRowPointers);
// Finish up, ensures all metadata are updated
png_read_end(mReadPngPtr, NULL);
}
// If an info object is supplied, copy the relevant info
if (infop != NULL)
{
infop->mHeight = static_cast<U16>(mHeight);
infop->mWidth = static_cast<U16>(mWidth);
infop->mComponents = mChannels;
}
mFinalSize = dataPtr.mOffset;
}
catch (png_const_charp msg)
{
mErrorMessage = msg;
releaseResources();
return (FALSE);
}
// Clean up and return
releaseResources();
return (TRUE);
}
// Do transformations to normalize the input to 8-bpp RGBA
void LLPngWrapper::normalizeImage()
{
// 1. Expand any palettes
// 2. Convert grayscales to RGB
// 3. Create alpha layer from transparency
// 4. Ensure 8-bpp for all images
// 5. Apply background matte if any
// 6. Set (or guess) gamma
if (mColorType == PNG_COLOR_TYPE_PALETTE)
{
png_set_palette_to_rgb(mReadPngPtr);
}
if (mColorType == PNG_COLOR_TYPE_GRAY && mBitDepth < 8)
{
png_set_gray_1_2_4_to_8(mReadPngPtr);
}
if (mColorType == PNG_COLOR_TYPE_GRAY
|| mColorType == PNG_COLOR_TYPE_GRAY_ALPHA)
{
png_set_gray_to_rgb(mReadPngPtr);
}
if (png_get_valid(mReadPngPtr, mReadInfoPtr, PNG_INFO_tRNS))
{
png_set_tRNS_to_alpha(mReadPngPtr);
}
if (mBitDepth < 8)
{
png_set_packing(mReadPngPtr);
}
else if (mBitDepth == 16)
{
png_set_strip_16(mReadPngPtr);
}
mHasBKGD = png_get_bKGD(mReadPngPtr, mReadInfoPtr, &mBackgroundColor);
if (mHasBKGD)
{
png_set_background(mReadPngPtr, mBackgroundColor,
PNG_BACKGROUND_GAMMA_FILE, 1, 1.0);
}
#if LL_DARWIN
const F64 SCREEN_GAMMA = 1.8;
#else
const F64 SCREEN_GAMMA = 2.2;
#endif
if (png_get_gAMA(mReadPngPtr, mReadInfoPtr, &mGamma))
{
png_set_gamma(mReadPngPtr, SCREEN_GAMMA, mGamma);
}
else
{
png_set_gamma(mReadPngPtr, SCREEN_GAMMA, 1/SCREEN_GAMMA);
}
}
// Read out the image meta-data
void LLPngWrapper::updateMetaData()
{
png_read_update_info(mReadPngPtr, mReadInfoPtr);
mWidth = png_get_image_width(mReadPngPtr, mReadInfoPtr);
mHeight = png_get_image_height(mReadPngPtr, mReadInfoPtr);
mBitDepth = png_get_bit_depth(mReadPngPtr, mReadInfoPtr);
mColorType = png_get_color_type(mReadPngPtr, mReadInfoPtr);
mChannels = png_get_channels(mReadPngPtr, mReadInfoPtr);
mHasBKGD = png_get_bKGD(mReadPngPtr, mReadInfoPtr, &mBackgroundColor);
}
// Method to write raw image into PNG at dest. The raw scanline begins
// at the bottom of the image per SecondLife conventions.
BOOL LLPngWrapper::writePng(const LLImageRaw* rawImage, U8* dest)
{
try
{
S8 numComponents = rawImage->getComponents();
switch (numComponents)
{
case 1:
mColorType = PNG_COLOR_TYPE_GRAY;
break;
case 2:
mColorType = PNG_COLOR_TYPE_GRAY_ALPHA;
break;
case 3:
mColorType = PNG_COLOR_TYPE_RGB;
break;
case 4:
mColorType = PNG_COLOR_TYPE_RGB_ALPHA;
break;
default:
mColorType = -1;
}
if (mColorType == -1)
{
throw "Unsupported image: unexpected number of channels";
}
mWritePngPtr = png_create_write_struct(PNG_LIBPNG_VER_STRING,
NULL, &errorHandler, NULL);
if (!mWritePngPtr)
{
throw "Problem creating png write structure";
}
mWriteInfoPtr = png_create_info_struct(mWritePngPtr);
// Setup write function
PngDataInfo dataPtr;
dataPtr.mData = dest;
dataPtr.mOffset = 0;
png_set_write_fn(mWritePngPtr, &dataPtr, &writeDataCallback, &writeFlush);
// Setup image params
mWidth = rawImage->getWidth();
mHeight = rawImage->getHeight();
mBitDepth = 8; // Fixed to 8-bpp in SL
mChannels = numComponents;
mInterlaceType = PNG_INTERLACE_NONE;
mCompressionType = PNG_COMPRESSION_TYPE_DEFAULT;
mFilterMethod = PNG_FILTER_TYPE_DEFAULT;
// Write header
png_set_IHDR(mWritePngPtr, mWriteInfoPtr, mWidth, mHeight,
mBitDepth, mColorType, mInterlaceType,
mCompressionType, mFilterMethod);
// Get data and compute row size
const U8* data = rawImage->getData();
int offset = mWidth * mChannels;
// Ready to write, start with the header
png_write_info(mWritePngPtr, mWriteInfoPtr);
// Write image (sorry, must const-cast for libpng)
const U8 * rowPointer;
for (U32 i=0; i < mHeight; i++)
{
rowPointer = &data[(mHeight-1-i)*offset];
png_write_row(mWritePngPtr, const_cast<png_bytep>(rowPointer));
}
// Finish up
png_write_end(mWritePngPtr, mWriteInfoPtr);
mFinalSize = dataPtr.mOffset;
}
catch (png_const_charp msg)
{
mErrorMessage = msg;
releaseResources();
return (FALSE);
}
releaseResources();
return TRUE;
}
// Cleanup various internal structures
void LLPngWrapper::releaseResources()
{
if (mReadPngPtr || mReadInfoPtr)
{
png_destroy_read_struct(&mReadPngPtr, &mReadInfoPtr, png_infopp_NULL);
mReadPngPtr = NULL;
mReadInfoPtr = NULL;
}
if (mWritePngPtr || mWriteInfoPtr)
{
png_destroy_write_struct(&mWritePngPtr, &mWriteInfoPtr);
mWritePngPtr = NULL;
mWriteInfoPtr = NULL;
}
if (mRowPointers)
{
delete[] mRowPointers;
mRowPointers = NULL;
}
}
// Get final image size after compression
U32 LLPngWrapper::getFinalSize()
{
return mFinalSize;
}
// Get last error message, if any
const std::string& LLPngWrapper::getErrorMessage()
{
return mErrorMessage;
}

View File

@@ -0,0 +1,105 @@
/*
* @file llpngwrapper.h
*
* $LicenseInfo:firstyear=2007&license=viewergpl$
*
* Copyright (c) 2007-2009, Linden Research, Inc.
*
* Second Life Viewer Source Code
* The source code in this file ("Source Code") is provided by Linden Lab
* to you under the terms of the GNU General Public License, version 2.0
* ("GPL"), unless you have obtained a separate licensing agreement
* ("Other License"), formally executed by you and Linden Lab. Terms of
* the GPL can be found in doc/GPL-license.txt in this distribution, or
* online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
*
* There are special exceptions to the terms and conditions of the GPL as
* it is applied to this Source Code. View the full text of the exception
* in the file doc/FLOSS-exception.txt in this software distribution, or
* online at
* http://secondlifegrid.net/programs/open_source/licensing/flossexception
*
* By copying, modifying or distributing this software, you acknowledge
* that you have read and understood your obligations described above,
* and agree to abide by those obligations.
*
* ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
* WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
* COMPLETENESS OR PERFORMANCE.
* $/LicenseInfo$
*/
#ifndef LL_LLPNGWRAPPER_H
#define LL_LLPNGWRAPPER_H
#include "libpng12/png.h"
#include "llimage.h"
class LLPngWrapper
{
public:
LLPngWrapper();
virtual ~LLPngWrapper();
public:
struct ImageInfo
{
U16 mWidth;
U16 mHeight;
S8 mComponents;
};
BOOL isValidPng(U8* src);
BOOL readPng(U8* src, LLImageRaw* rawImage, ImageInfo *infop = NULL);
BOOL writePng(const LLImageRaw* rawImage, U8* dst);
U32 getFinalSize();
const std::string& getErrorMessage();
protected:
void normalizeImage();
void updateMetaData();
private:
// Structure for writing/reading PNG data to/from memory
// as opposed to using a file.
struct PngDataInfo
{
U8 *mData;
U32 mOffset;
};
static void writeFlush(png_structp png_ptr);
static void errorHandler(png_structp png_ptr, png_const_charp msg);
static void readDataCallback(png_structp png_ptr, png_bytep dest, png_size_t length);
static void writeDataCallback(png_structp png_ptr, png_bytep src, png_size_t length);
void releaseResources();
png_structp mReadPngPtr;
png_infop mReadInfoPtr;
png_structp mWritePngPtr;
png_infop mWriteInfoPtr;
U8 **mRowPointers;
png_uint_32 mWidth;
png_uint_32 mHeight;
S32 mBitDepth;
S32 mColorType;
S32 mChannels;
S32 mInterlaceType;
S32 mCompressionType;
S32 mFilterMethod;
U32 mFinalSize;
bool mHasBKGD;
png_color_16p mBackgroundColor;
F64 mGamma;
std::string mErrorMessage;
};
#endif

View File

@@ -0,0 +1,260 @@
/**
* @file llimageworker_test.cpp
* @author Merov Linden
* @date 2009-04-28
*
* $LicenseInfo:firstyear=2006&license=viewergpl$
*
* Copyright (c) 2006-2009, Linden Research, Inc.
*
* Second Life Viewer Source Code
* The source code in this file ("Source Code") is provided by Linden Lab
* to you under the terms of the GNU General Public License, version 2.0
* ("GPL"), unless you have obtained a separate licensing agreement
* ("Other License"), formally executed by you and Linden Lab. Terms of
* the GPL can be found in doc/GPL-license.txt in this distribution, or
* online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
*
* There are special exceptions to the terms and conditions of the GPL as
* it is applied to this Source Code. View the full text of the exception
* in the file doc/FLOSS-exception.txt in this software distribution, or
* online at
* http://secondlifegrid.net/programs/open_source/licensing/flossexception
*
* By copying, modifying or distributing this software, you acknowledge
* that you have read and understood your obligations described above,
* and agree to abide by those obligations.
*
* ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
* WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
* COMPLETENESS OR PERFORMANCE.
* $/LicenseInfo$
*/
// Precompiled header: almost always required for newview cpp files
#include <list>
#include <map>
#include <algorithm>
// Class to test
#include "../llimageworker.h"
// For timer class
#include "../llcommon/lltimer.h"
// Tut header
#include "../test/lltut.h"
// -------------------------------------------------------------------------------------------
// Stubbing: Declarations required to link and run the class being tested
// Notes:
// * Add here stubbed implementation of the few classes and methods used in the class to be tested
// * Add as little as possible (let the link errors guide you)
// * Do not make any assumption as to how those classes or methods work (i.e. don't copy/paste code)
// * A simulator for a class can be implemented here. Please comment and document thoroughly.
LLImageBase::LLImageBase() {}
LLImageBase::~LLImageBase() {}
void LLImageBase::dump() { }
void LLImageBase::sanityCheck() { }
void LLImageBase::deleteData() { }
U8* LLImageBase::allocateData(S32 size) { return NULL; }
U8* LLImageBase::reallocateData(S32 size) { return NULL; }
LLImageRaw::LLImageRaw(U16 width, U16 height, S8 components) { }
LLImageRaw::~LLImageRaw() { }
void LLImageRaw::deleteData() { }
U8* LLImageRaw::allocateData(S32 size) { return NULL; }
U8* LLImageRaw::reallocateData(S32 size) { return NULL; }
// End Stubbing
// -------------------------------------------------------------------------------------------
// -------------------------------------------------------------------------------------------
// TUT
// -------------------------------------------------------------------------------------------
namespace tut
{
// Test wrapper declarations
// Note: We derive the responder class for 2 reasons:
// 1. It's a pure virtual class and we can't compile without completed() being implemented
// 2. We actually need a responder to test that the thread work test completed
// We implement this making no assumption on what's done in the thread or worker
// though, just that the responder's completed() method is called in the end.
// Note on responders: responders are ref counted and *will* be deleted by the request they are
// attached to when the queued request is deleted. The recommended way of using them is to
// create them when creating a request, put a callback method in completed() and not rely on
// anything to survive in the responder object once completed() has been called. Let the request
// do the deletion and clean up itself.
class responder_test : public LLImageDecodeThread::Responder
{
public:
responder_test(bool* res)
{
done = res;
*done = false;
}
virtual void completed(bool success, LLImageRaw* raw, LLImageRaw* aux)
{
*done = true;
}
private:
// This is what can be thought of as the minimal implementation of a responder
// Done will be switched to true when completed() is called and can be tested
// outside the responder. A better way of doing this is to store a callback here.
bool* done;
};
// Test wrapper declaration : decode thread
struct imagedecodethread_test
{
// Instance to be tested
LLImageDecodeThread* mThread;
// Constructor and destructor of the test wrapper
imagedecodethread_test()
{
mThread = NULL;
}
~imagedecodethread_test()
{
delete mThread;
}
};
// Test wrapper declaration : image worker
// Note: this class is not meant to be instantiated outside an LLImageDecodeThread instance
// but it's not a bad idea to get its public API a good shake as part of a thorough unit test set.
// Some gotcha with the destructor though (see below).
struct imagerequest_test
{
// Instance to be tested
LLImageDecodeThread::ImageRequest* mRequest;
bool done;
// Constructor and destructor of the test wrapper
imagerequest_test()
{
done = false;
mRequest = new LLImageDecodeThread::ImageRequest(0, 0,
LLQueuedThread::PRIORITY_NORMAL, 0, FALSE,
new responder_test(&done));
}
~imagerequest_test()
{
// We should delete the object *but*, because its destructor is protected, that cannot be
// done from outside an LLImageDecodeThread instance... So we leak memory here... It's fine...
//delete mRequest;
}
};
// Tut templating thingamagic: test group, object and test instance
typedef test_group<imagedecodethread_test> imagedecodethread_t;
typedef imagedecodethread_t::object imagedecodethread_object_t;
tut::imagedecodethread_t tut_imagedecodethread("imagedecodethread");
typedef test_group<imagerequest_test> imagerequest_t;
typedef imagerequest_t::object imagerequest_object_t;
tut::imagerequest_t tut_imagerequest("imagerequest");
// ---------------------------------------------------------------------------------------
// Test functions
// Notes:
// * Test as many as you possibly can without requiring a full blown simulation of everything
// * The tests are executed in sequence so the test instance state may change between calls
// * Remember that you cannot test private methods with tut
// ---------------------------------------------------------------------------------------
// ---------------------------------------------------------------------------------------
// Test the LLImageDecodeThread interface
// ---------------------------------------------------------------------------------------
//
// Note on Unit Testing Queued Thread Classes
//
// Since methods on such a class are called on a separate loop and that we can't insert tut
// ensure() calls in there, we exercise the class with 2 sets of tests:
// - 1: Test as a single threaded instance: We declare the class but ask for no thread
// to be spawned (easy with LLThreads since there's a boolean argument on the constructor
// just for that). We can then unit test each public method like we do on a normal class.
// - 2: Test as a threaded instance: We let the thread launch and check that its external
// behavior is as expected (i.e. it runs, can accept a work order and processes
// it). Typically though there's no guarantee that this exercises all the methods of the
// class which is why we also need the previous "non threaded" set of unit tests for
// complete coverage.
//
// ---------------------------------------------------------------------------------------
template<> template<>
void imagedecodethread_object_t::test<1>()
{
// Test a *non threaded* instance of the class
mThread = new LLImageDecodeThread(false);
ensure("LLImageDecodeThread: non threaded constructor failed", mThread != NULL);
// Test that we start with an empty list right at creation
ensure("LLImageDecodeThread: non threaded init state incorrect", mThread->tut_size() == 0);
// Insert something in the queue
bool done = false;
LLImageDecodeThread::handle_t decodeHandle = mThread->decodeImage(NULL, LLQueuedThread::PRIORITY_NORMAL, 0, FALSE, new responder_test(&done));
// Verifies we got a valid handle
ensure("LLImageDecodeThread: non threaded decodeImage(), returned handle is null", decodeHandle != 0);
// Verifies that we do now have something in the queued list
ensure("LLImageDecodeThread: non threaded decodeImage() insertion in threaded list failed", mThread->tut_size() == 1);
// Trigger queue handling "manually" (on a threaded instance, this is done on the thread loop)
S32 res = mThread->update(0);
// Verifies that we successfully handled the list
ensure("LLImageDecodeThread: non threaded update() list handling test failed", res == 0);
// Verifies that the list is now empty
ensure("LLImageDecodeThread: non threaded update() list emptying test failed", mThread->tut_size() == 0);
}
template<> template<>
void imagedecodethread_object_t::test<2>()
{
// Test a *threaded* instance of the class
mThread = new LLImageDecodeThread(true);
ensure("LLImageDecodeThread: threaded constructor failed", mThread != NULL);
// Test that we start with an empty list right at creation
ensure("LLImageDecodeThread: threaded init state incorrect", mThread->tut_size() == 0);
// Insert something in the queue
bool done = false;
LLImageDecodeThread::handle_t decodeHandle = mThread->decodeImage(NULL, LLQueuedThread::PRIORITY_NORMAL, 0, FALSE, new responder_test(&done));
// Verifies we get back a valid handle
ensure("LLImageDecodeThread: threaded decodeImage(), returned handle is null", decodeHandle != 0);
// Wait a little so to simulate the main thread doing something on its main loop...
ms_sleep(500); // 500 milliseconds
// Verifies that the responder has *not* been called yet in the meantime
ensure("LLImageDecodeThread: responder creation failed", done == false);
// Ask the thread to update: that means tells the queue to check itself and creates work requests
mThread->update(1);
// Wait till the thread has time to handle the work order (though it doesn't do much per work order...)
const U32 INCREMENT_TIME = 500; // 500 milliseconds
const U32 MAX_TIME = 20 * INCREMENT_TIME; // Do the loop 20 times max, i.e. wait 10 seconds but no more
U32 total_time = 0;
while ((done == false) && (total_time < MAX_TIME))
{
ms_sleep(INCREMENT_TIME);
total_time += INCREMENT_TIME;
}
// Verifies that the responder has now been called
ensure("LLImageDecodeThread: threaded work unit not processed", done == true);
}
// ---------------------------------------------------------------------------------------
// Test the LLImageDecodeThread::ImageRequest interface
// ---------------------------------------------------------------------------------------
template<> template<>
void imagerequest_object_t::test<1>()
{
// Test that we start with a correct request at creation
ensure("LLImageDecodeThread::ImageRequest::ImageRequest() constructor test failed", mRequest->tut_isOK());
bool res = mRequest->processRequest();
// Verifies that we processed the request successfully
ensure("LLImageDecodeThread::ImageRequest::processRequest() processing request test failed", res == true);
// Check that we can call the finishing call safely
try {
mRequest->finishRequest(false);
} catch (...) {
fail("LLImageDecodeThread::ImageRequest::finishRequest() test failed");
}
}
}