Imported existing code
This commit is contained in:
65
indra/llimage/CMakeLists.txt
Normal file
65
indra/llimage/CMakeLists.txt
Normal 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
1703
indra/llimage/llimage.cpp
Normal file
File diff suppressed because it is too large
Load Diff
319
indra/llimage/llimage.h
Normal file
319
indra/llimage/llimage.h
Normal 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
|
||||
659
indra/llimage/llimagebmp.cpp
Normal file
659
indra/llimage/llimagebmp.cpp
Normal 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;
|
||||
}
|
||||
70
indra/llimage/llimagebmp.h
Normal file
70
indra/llimage/llimagebmp.h
Normal 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
|
||||
511
indra/llimage/llimagedxt.cpp
Normal file
511
indra/llimage/llimagedxt.cpp
Normal 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
146
indra/llimage/llimagedxt.h
Normal 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
|
||||
506
indra/llimage/llimagej2c.cpp
Normal file
506
indra/llimage/llimagej2c.cpp
Normal 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
123
indra/llimage/llimagej2c.h
Normal 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
|
||||
654
indra/llimage/llimagejpeg.cpp
Normal file
654
indra/llimage/llimagejpeg.cpp
Normal 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;
|
||||
}
|
||||
92
indra/llimage/llimagejpeg.h
Normal file
92
indra/llimage/llimagejpeg.h
Normal 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
|
||||
150
indra/llimage/llimagepng.cpp
Normal file
150
indra/llimage/llimagepng.cpp
Normal 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;
|
||||
}
|
||||
|
||||
55
indra/llimage/llimagepng.h
Normal file
55
indra/llimage/llimagepng.h
Normal 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
1189
indra/llimage/llimagetga.cpp
Normal file
File diff suppressed because it is too large
Load Diff
114
indra/llimage/llimagetga.h
Normal file
114
indra/llimage/llimagetga.h
Normal 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
|
||||
174
indra/llimage/llimageworker.cpp
Normal file
174
indra/llimage/llimageworker.cpp
Normal 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();
|
||||
}
|
||||
107
indra/llimage/llimageworker.h
Normal file
107
indra/llimage/llimageworker.h
Normal 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
|
||||
46
indra/llimage/llmapimagetype.h
Normal file
46
indra/llimage/llmapimagetype.h
Normal 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
|
||||
396
indra/llimage/llpngwrapper.cpp
Normal file
396
indra/llimage/llpngwrapper.cpp
Normal 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;
|
||||
}
|
||||
105
indra/llimage/llpngwrapper.h
Normal file
105
indra/llimage/llpngwrapper.h
Normal 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
|
||||
260
indra/llimage/tests/llimageworker_test.cpp
Normal file
260
indra/llimage/tests/llimageworker_test.cpp
Normal 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");
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user