summaryrefslogtreecommitdiff
path: root/indra/llimage
diff options
context:
space:
mode:
Diffstat (limited to 'indra/llimage')
-rwxr-xr-x[-rw-r--r--]indra/llimage/CMakeLists.txt15
-rwxr-xr-x[-rw-r--r--]indra/llimage/llimage.cpp222
-rwxr-xr-x[-rw-r--r--]indra/llimage/llimage.h20
-rwxr-xr-x[-rw-r--r--]indra/llimage/llimagebmp.cpp4
-rwxr-xr-x[-rw-r--r--]indra/llimage/llimagebmp.h0
-rwxr-xr-x[-rw-r--r--]indra/llimage/llimagedimensionsinfo.cpp20
-rwxr-xr-x[-rw-r--r--]indra/llimage/llimagedimensionsinfo.h2
-rwxr-xr-x[-rw-r--r--]indra/llimage/llimagedxt.cpp22
-rwxr-xr-x[-rw-r--r--]indra/llimage/llimagedxt.h0
-rwxr-xr-xindra/llimage/llimagefilter.cpp939
-rwxr-xr-xindra/llimage/llimagefilter.h137
-rwxr-xr-x[-rw-r--r--]indra/llimage/llimagej2c.cpp3
-rwxr-xr-x[-rw-r--r--]indra/llimage/llimagej2c.h0
-rwxr-xr-x[-rw-r--r--]indra/llimage/llimagejpeg.cpp14
-rwxr-xr-x[-rw-r--r--]indra/llimage/llimagejpeg.h3
-rwxr-xr-x[-rw-r--r--]indra/llimage/llimagepng.cpp4
-rwxr-xr-x[-rw-r--r--]indra/llimage/llimagepng.h0
-rwxr-xr-x[-rw-r--r--]indra/llimage/llimagetga.cpp22
-rwxr-xr-x[-rw-r--r--]indra/llimage/llimagetga.h0
-rwxr-xr-x[-rw-r--r--]indra/llimage/llimageworker.cpp7
-rwxr-xr-x[-rw-r--r--]indra/llimage/llimageworker.h0
-rwxr-xr-x[-rw-r--r--]indra/llimage/llmapimagetype.h0
-rwxr-xr-x[-rw-r--r--]indra/llimage/llpngwrapper.cpp9
-rwxr-xr-x[-rw-r--r--]indra/llimage/llpngwrapper.h3
-rwxr-xr-x[-rw-r--r--]indra/llimage/tests/llimageworker_test.cpp9
25 files changed, 1310 insertions, 145 deletions
diff --git a/indra/llimage/CMakeLists.txt b/indra/llimage/CMakeLists.txt
index ea8c1a1107..293ada7548 100644..100755
--- a/indra/llimage/CMakeLists.txt
+++ b/indra/llimage/CMakeLists.txt
@@ -7,12 +7,15 @@ include(LLCommon)
include(LLImage)
include(LLMath)
include(LLVFS)
+include(LLKDU)
+include(LLImageJ2COJ)
include(ZLIB)
include(LLAddBuildTest)
include(Tut)
include_directories(
${LLCOMMON_INCLUDE_DIRS}
+ ${LLCOMMON_SYSTEM_INCLUDE_DIRS}
${LLMATH_INCLUDE_DIRS}
${LLVFS_INCLUDE_DIRS}
${PNG_INCLUDE_DIRS}
@@ -24,6 +27,7 @@ set(llimage_SOURCE_FILES
llimage.cpp
llimagedimensionsinfo.cpp
llimagedxt.cpp
+ llimagefilter.cpp
llimagej2c.cpp
llimagejpeg.cpp
llimagepng.cpp
@@ -39,6 +43,7 @@ set(llimage_HEADER_FILES
llimagebmp.h
llimagedimensionsinfo.h
llimagedxt.h
+ llimagefilter.h
llimagej2c.h
llimagejpeg.h
llimagepng.h
@@ -56,8 +61,16 @@ list(APPEND llimage_SOURCE_FILES ${llimage_HEADER_FILES})
add_library (llimage ${llimage_SOURCE_FILES})
# Libraries on which this library depends, needed for Linux builds
# Sort by high-level to low-level
+if (USE_KDU)
+ target_link_libraries(llimage ${LLKDU_LIBRARIES})
+else (USE_KDU)
+ target_link_libraries(llimage ${LLIMAGEJ2COJ_LIBRARIES})
+endif (USE_KDU)
+
target_link_libraries(llimage
- llcommon
+ ${LLVFS_LIBRARIES}
+ ${LLMATH_LIBRARIES}
+ ${LLCOMMON_LIBRARIES}
${JPEG_LIBRARIES}
${PNG_LIBRARIES}
${ZLIB_LIBRARIES}
diff --git a/indra/llimage/llimage.cpp b/indra/llimage/llimage.cpp
index a88ac148ef..16df27bb8e 100644..100755
--- a/indra/llimage/llimage.cpp
+++ b/indra/llimage/llimage.cpp
@@ -26,6 +26,7 @@
#include "linden_common.h"
+#include "llimageworker.h"
#include "llimage.h"
#include "llmath.h"
@@ -37,7 +38,6 @@
#include "llimagejpeg.h"
#include "llimagepng.h"
#include "llimagedxt.h"
-#include "llimageworker.h"
#include "llmemory.h"
//---------------------------------------------------------------------------
@@ -89,15 +89,15 @@ void LLImage::setLastError(const std::string& message)
//---------------------------------------------------------------------------
LLImageBase::LLImageBase()
- : mData(NULL),
- mDataSize(0),
- mWidth(0),
- mHeight(0),
- mComponents(0),
- mBadBufferAllocation(false),
- mAllowOverSize(false)
-{
-}
+: LLTrace::MemTrackable<LLImageBase>("LLImage"),
+ mData(NULL),
+ mDataSize(0),
+ mWidth(0),
+ mHeight(0),
+ mComponents(0),
+ mBadBufferAllocation(false),
+ mAllowOverSize(false)
+{}
// virtual
LLImageBase::~LLImageBase()
@@ -127,12 +127,12 @@ void LLImageBase::destroyPrivatePool()
// virtual
void LLImageBase::dump()
{
- llinfos << "LLImageBase mComponents " << mComponents
+ LL_INFOS() << "LLImageBase mComponents " << mComponents
<< " mData " << mData
<< " mDataSize " << mDataSize
<< " mWidth " << mWidth
<< " mHeight " << mHeight
- << llendl;
+ << LL_ENDL;
}
// virtual
@@ -144,13 +144,13 @@ void LLImageBase::sanityCheck()
|| mComponents > (S8)MAX_IMAGE_COMPONENTS
)
{
- llerrs << "Failed LLImageBase::sanityCheck "
+ LL_ERRS() << "Failed LLImageBase::sanityCheck "
<< "width " << mWidth
<< "height " << mHeight
<< "datasize " << mDataSize
<< "components " << mComponents
<< "data " << mData
- << llendl;
+ << LL_ENDL;
}
}
@@ -158,8 +158,9 @@ void LLImageBase::sanityCheck()
void LLImageBase::deleteData()
{
FREE_MEM(sPrivatePoolp, mData) ;
- mData = NULL;
+ disclaimMem(mDataSize);
mDataSize = 0;
+ mData = NULL;
}
// virtual
@@ -170,7 +171,7 @@ U8* LLImageBase::allocateData(S32 size)
size = mWidth * mHeight * mComponents;
if (size <= 0)
{
- llerrs << llformat("LLImageBase::allocateData called with bad dimensions: %dx%dx%d",mWidth,mHeight,(S32)mComponents) << llendl;
+ LL_ERRS() << llformat("LLImageBase::allocateData called with bad dimensions: %dx%dx%d",mWidth,mHeight,(S32)mComponents) << LL_ENDL;
}
}
@@ -178,14 +179,14 @@ U8* LLImageBase::allocateData(S32 size)
static const U32 MAX_BUFFER_SIZE = 4096 * 4096 * 16 ; //256 MB
if (size < 1 || size > MAX_BUFFER_SIZE)
{
- llinfos << "width: " << mWidth << " height: " << mHeight << " components: " << mComponents << llendl ;
+ LL_INFOS() << "width: " << mWidth << " height: " << mHeight << " components: " << mComponents << LL_ENDL ;
if(mAllowOverSize)
{
- llinfos << "Oversize: " << size << llendl ;
+ LL_INFOS() << "Oversize: " << size << LL_ENDL ;
}
else
{
- llerrs << "LLImageBase::allocateData: bad size: " << size << llendl;
+ LL_ERRS() << "LLImageBase::allocateData: bad size: " << size << LL_ENDL;
}
}
if (!mData || size != mDataSize)
@@ -195,12 +196,13 @@ U8* LLImageBase::allocateData(S32 size)
mData = (U8*)ALLOCATE_MEM(sPrivatePoolp, size);
if (!mData)
{
- llwarns << "Failed to allocate image data size [" << size << "]" << llendl;
+ LL_WARNS() << "Failed to allocate image data size [" << size << "]" << LL_ENDL;
size = 0 ;
mWidth = mHeight = 0 ;
mBadBufferAllocation = true ;
}
mDataSize = size;
+ claimMem(mDataSize);
}
return mData;
@@ -212,7 +214,7 @@ U8* LLImageBase::reallocateData(S32 size)
U8 *new_datap = (U8*)ALLOCATE_MEM(sPrivatePoolp, size);
if (!new_datap)
{
- llwarns << "Out of memory in LLImageBase::reallocateData" << llendl;
+ LL_WARNS() << "Out of memory in LLImageBase::reallocateData" << LL_ENDL;
return 0;
}
if (mData)
@@ -222,7 +224,9 @@ U8* LLImageBase::reallocateData(S32 size)
FREE_MEM(sPrivatePoolp, mData) ;
}
mData = new_datap;
+ disclaimMem(mDataSize);
mDataSize = size;
+ claimMem(mDataSize);
return mData;
}
@@ -230,7 +234,7 @@ const U8* LLImageBase::getData() const
{
if(mBadBufferAllocation)
{
- llerrs << "Bad memory allocation for the image buffer!" << llendl ;
+ LL_ERRS() << "Bad memory allocation for the image buffer!" << LL_ENDL ;
}
return mData;
@@ -240,7 +244,7 @@ U8* LLImageBase::getData()
{
if(mBadBufferAllocation)
{
- llerrs << "Bad memory allocation for the image buffer!" << llendl ;
+ LL_ERRS() << "Bad memory allocation for the image buffer!" << LL_ENDL ;
}
return mData;
@@ -288,7 +292,6 @@ LLImageRaw::LLImageRaw(U16 width, U16 height, S8 components)
LLImageRaw::LLImageRaw(U8 *data, U16 width, U16 height, S8 components, bool no_copy)
: LLImageBase()
{
-
if(no_copy)
{
setDataAndSize(data, width, height, components);
@@ -449,18 +452,8 @@ void LLImageRaw::verticalFlip()
void LLImageRaw::expandToPowerOfTwo(S32 max_dim, BOOL scale_image)
{
// Find new sizes
- S32 new_width = MIN_IMAGE_SIZE;
- S32 new_height = MIN_IMAGE_SIZE;
-
- while( (new_width < getWidth()) && (new_width < max_dim) )
- {
- new_width <<= 1;
- }
-
- while( (new_height < getHeight()) && (new_height < max_dim) )
- {
- new_height <<= 1;
- }
+ S32 new_width = expandDimToPowerOfTwo(getWidth(), max_dim);
+ S32 new_height = expandDimToPowerOfTwo(getHeight(), max_dim);
scale( new_width, new_height, scale_image );
}
@@ -468,55 +461,61 @@ void LLImageRaw::expandToPowerOfTwo(S32 max_dim, BOOL scale_image)
void LLImageRaw::contractToPowerOfTwo(S32 max_dim, BOOL scale_image)
{
// Find new sizes
- S32 new_width = max_dim;
- S32 new_height = max_dim;
-
- while( (new_width > getWidth()) && (new_width > MIN_IMAGE_SIZE) )
- {
- new_width >>= 1;
- }
-
- while( (new_height > getHeight()) && (new_height > MIN_IMAGE_SIZE) )
- {
- new_height >>= 1;
- }
+ S32 new_width = contractDimToPowerOfTwo(getWidth(), MIN_IMAGE_SIZE);
+ S32 new_height = contractDimToPowerOfTwo(getHeight(), MIN_IMAGE_SIZE);
scale( new_width, new_height, scale_image );
}
-void LLImageRaw::biasedScaleToPowerOfTwo(S32 max_dim)
+// static
+S32 LLImageRaw::biasedDimToPowerOfTwo(S32 curr_dim, S32 max_dim)
{
// Strong bias towards rounding down (to save bandwidth)
// No bias would mean THRESHOLD == 1.5f;
- const F32 THRESHOLD = 1.75f;
-
+ const F32 THRESHOLD = 1.75f;
+
// Find new sizes
- S32 larger_w = max_dim; // 2^n >= mWidth
- S32 smaller_w = max_dim; // 2^(n-1) <= mWidth
- while( (smaller_w > getWidth()) && (smaller_w > MIN_IMAGE_SIZE) )
+ S32 larger_dim = max_dim; // 2^n >= curr_dim
+ S32 smaller_dim = max_dim; // 2^(n-1) <= curr_dim
+ while( (smaller_dim > curr_dim) && (smaller_dim > MIN_IMAGE_SIZE) )
{
- larger_w = smaller_w;
- smaller_w >>= 1;
+ larger_dim = smaller_dim;
+ smaller_dim >>= 1;
}
- S32 new_width = ( (F32)getWidth() / smaller_w > THRESHOLD ) ? larger_w : smaller_w;
+ return ( ((F32)curr_dim / (F32)smaller_dim) > THRESHOLD ) ? larger_dim : smaller_dim;
+}
+// static
+S32 LLImageRaw::expandDimToPowerOfTwo(S32 curr_dim, S32 max_dim)
+{
+ S32 new_dim = MIN_IMAGE_SIZE;
+ while( (new_dim < curr_dim) && (new_dim < max_dim) )
+ {
+ new_dim <<= 1;
+ }
+ return new_dim;
+}
- S32 larger_h = max_dim; // 2^m >= mHeight
- S32 smaller_h = max_dim; // 2^(m-1) <= mHeight
- while( (smaller_h > getHeight()) && (smaller_h > MIN_IMAGE_SIZE) )
+// static
+S32 LLImageRaw::contractDimToPowerOfTwo(S32 curr_dim, S32 min_dim)
+{
+ S32 new_dim = MAX_IMAGE_SIZE;
+ while( (new_dim > curr_dim) && (new_dim > min_dim) )
{
- larger_h = smaller_h;
- smaller_h >>= 1;
+ new_dim >>= 1;
}
- S32 new_height = ( (F32)getHeight() / smaller_h > THRESHOLD ) ? larger_h : smaller_h;
+ return new_dim;
+}
+void LLImageRaw::biasedScaleToPowerOfTwo(S32 max_dim)
+{
+ // Find new sizes
+ S32 new_width = biasedDimToPowerOfTwo(getWidth(),max_dim);
+ S32 new_height = biasedDimToPowerOfTwo(getHeight(),max_dim);
scale( new_width, new_height );
}
-
-
-
// Calculates (U8)(255*(a/255.f)*(b/255.f) + 0.5f). Thanks, Jim Blinn!
inline U8 LLImageRaw::fastFractionalMult( U8 a, U8 b )
{
@@ -563,7 +562,7 @@ void LLImageRaw::composite( LLImageRaw* src )
// Src and dst can be any size. Src has 4 components. Dst has 3 components.
void LLImageRaw::compositeScaled4onto3(LLImageRaw* src)
{
- llinfos << "compositeScaled4onto3" << llendl;
+ LL_INFOS() << "compositeScaled4onto3" << LL_ENDL;
LLImageRaw* dst = this; // Just for clarity.
@@ -640,6 +639,29 @@ void LLImageRaw::compositeUnscaled4onto3( LLImageRaw* src )
}
}
+void LLImageRaw::copyUnscaledAlphaMask( LLImageRaw* src, const LLColor4U& fill)
+{
+ LLImageRaw* dst = this; // Just for clarity.
+
+ llassert( 1 == src->getComponents() );
+ llassert( 4 == dst->getComponents() );
+ llassert( (src->getWidth() == dst->getWidth()) && (src->getHeight() == dst->getHeight()) );
+
+ S32 pixels = getWidth() * getHeight();
+ U8* src_data = src->getData();
+ U8* dst_data = dst->getData();
+ for ( S32 i = 0; i < pixels; i++ )
+ {
+ dst_data[0] = fill.mV[0];
+ dst_data[1] = fill.mV[1];
+ dst_data[2] = fill.mV[2];
+ dst_data[3] = src_data[0];
+ src_data += 1;
+ dst_data += 4;
+ }
+}
+
+
// Fill the buffer with a constant color
void LLImageRaw::fill( const LLColor4U& color )
{
@@ -666,15 +688,24 @@ void LLImageRaw::fill( const LLColor4U& color )
}
}
+LLPointer<LLImageRaw> LLImageRaw::duplicate()
+{
+ if(getNumRefs() < 2)
+ {
+ return this; //nobody else refences to this image, no need to duplicate.
+ }
-
+ //make a duplicate
+ LLPointer<LLImageRaw> dup = new LLImageRaw(getData(), getWidth(), getHeight(), getComponents());
+ return dup;
+}
// Src and dst can be any size. Src and dst can each have 3 or 4 components.
void LLImageRaw::copy(LLImageRaw* src)
{
if (!src)
{
- llwarns << "LLImageRaw::copy called with a null src pointer" << llendl;
+ LL_WARNS() << "LLImageRaw::copy called with a null src pointer" << LL_ENDL;
return;
}
@@ -1005,13 +1036,13 @@ void LLImageRaw::copyLineScaled( U8* in, U8* out, S32 in_pixel_len, S32 out_pixe
a *= norm_factor; // skip conditional
S32 t4 = x * out_pixel_step * components;
- out[t4 + 0] = U8(llround(r));
+ out[t4 + 0] = U8(ll_round(r));
if (components >= 2)
- out[t4 + 1] = U8(llround(g));
+ out[t4 + 1] = U8(ll_round(g));
if (components >= 3)
- out[t4 + 2] = U8(llround(b));
+ out[t4 + 2] = U8(ll_round(b));
if( components == 4)
- out[t4 + 3] = U8(llround(a));
+ out[t4 + 3] = U8(ll_round(a));
}
}
}
@@ -1086,10 +1117,10 @@ void LLImageRaw::compositeRowScaled4onto3( U8* in, U8* out, S32 in_pixel_len, S3
b *= norm_factor;
a *= norm_factor;
- in_scaled_r = U8(llround(r));
- in_scaled_g = U8(llround(g));
- in_scaled_b = U8(llround(b));
- in_scaled_a = U8(llround(a));
+ in_scaled_r = U8(ll_round(r));
+ in_scaled_g = U8(ll_round(g));
+ in_scaled_b = U8(ll_round(b));
+ in_scaled_a = U8(ll_round(a));
}
if( in_scaled_a )
@@ -1141,7 +1172,7 @@ static std::string find_file(std::string &name, S8 *codec)
for (int i=0; i<(int)(NUM_FILE_EXTENSIONS); i++)
{
tname = name + "." + std::string(file_extensions[i].exten);
- llifstream ifs(tname, llifstream::binary);
+ llifstream ifs(tname.c_str(), llifstream::binary);
if (ifs.is_open())
{
ifs.close();
@@ -1188,11 +1219,11 @@ bool LLImageRaw::createFromFile(const std::string &filename, bool j2c_lowest_mip
return false; // format not recognized
}
- llifstream ifs(name, llifstream::binary);
+ llifstream ifs(name.c_str(), llifstream::binary);
if (!ifs.is_open())
{
- // SJB: changed from llinfos to lldebugs to reduce spam
- lldebugs << "Unable to open image file: " << name << llendl;
+ // SJB: changed from LL_INFOS() to LL_DEBUGS() to reduce spam
+ LL_DEBUGS() << "Unable to open image file: " << name << LL_ENDL;
return false;
}
@@ -1206,7 +1237,7 @@ bool LLImageRaw::createFromFile(const std::string &filename, bool j2c_lowest_mip
if (!length)
{
- llinfos << "Zero length file file: " << name << llendl;
+ LL_INFOS() << "Zero length file file: " << name << LL_ENDL;
return false;
}
@@ -1242,7 +1273,7 @@ bool LLImageRaw::createFromFile(const std::string &filename, bool j2c_lowest_mip
if (!success)
{
deleteData();
- llwarns << "Unable to decode image" << name << llendl;
+ LL_WARNS() << "Unable to decode image" << name << LL_ENDL;
return false;
}
@@ -1347,11 +1378,11 @@ void LLImageFormatted::dump()
{
LLImageBase::dump();
- llinfos << "LLImageFormatted"
+ LL_INFOS() << "LLImageFormatted"
<< " mDecoding " << mDecoding
<< " mCodec " << S32(mCodec)
<< " mDecoded " << mDecoded
- << llendl;
+ << LL_ENDL;
}
//----------------------------------------------------------------------------
@@ -1434,11 +1465,11 @@ void LLImageFormatted::sanityCheck()
if (mCodec >= IMG_CODEC_EOF)
{
- llerrs << "Failed LLImageFormatted::sanityCheck "
+ LL_ERRS() << "Failed LLImageFormatted::sanityCheck "
<< "decoding " << S32(mDecoding)
<< "decoded " << S32(mDecoded)
<< "codec " << S32(mCodec)
- << llendl;
+ << LL_ENDL;
}
}
@@ -1585,7 +1616,10 @@ static void avg4_colors2(const U8* a, const U8* b, const U8* c, const U8* d, U8*
void LLImageBase::setDataAndSize(U8 *data, S32 size)
{
ll_assert_aligned(data, 16);
- mData = data; mDataSize = size;
+ mData = data;
+ disclaimMem(mDataSize);
+ mDataSize = size;
+ claimMem(mDataSize);
}
//static
@@ -1613,7 +1647,7 @@ void LLImageBase::generateMip(const U8* indata, U8* mipdata, S32 width, S32 heig
*(U8*)data = (U8)(((U32)(indata[0]) + indata[1] + indata[in_width] + indata[in_width+1])>>2);
break;
default:
- llerrs << "generateMmip called with bad num channels" << llendl;
+ LL_ERRS() << "generateMmip called with bad num channels" << LL_ENDL;
}
indata += nchannels*2;
data += nchannels;
@@ -1670,17 +1704,17 @@ F32 LLImageBase::calc_download_priority(F32 virtual_size, F32 visible_pixels, S3
bytes_weight *= bytes_weight;
- //llinfos << "VS: " << virtual_size << llendl;
+ //LL_INFOS() << "VS: " << virtual_size << LL_ENDL;
F32 virtual_size_factor = virtual_size / (10.f*10.f);
// The goal is for weighted priority to be <= 0 when we've reached a point where
// we've sent enough data.
- //llinfos << "BytesSent: " << bytes_sent << llendl;
- //llinfos << "BytesWeight: " << bytes_weight << llendl;
- //llinfos << "PreLog: " << bytes_weight * virtual_size_factor << llendl;
+ //LL_INFOS() << "BytesSent: " << bytes_sent << LL_ENDL;
+ //LL_INFOS() << "BytesWeight: " << bytes_weight << LL_ENDL;
+ //LL_INFOS() << "PreLog: " << bytes_weight * virtual_size_factor << LL_ENDL;
w_priority = (F32)log10(bytes_weight * virtual_size_factor);
- //llinfos << "PreScale: " << w_priority << llendl;
+ //LL_INFOS() << "PreScale: " << w_priority << LL_ENDL;
// We don't want to affect how MANY bytes we send based on the visible pixels, but the order
// in which they're sent. We post-multiply so we don't change the zero point.
diff --git a/indra/llimage/llimage.h b/indra/llimage/llimage.h
index 6cb1226da0..cd3f76f1fd 100644..100755
--- a/indra/llimage/llimage.h
+++ b/indra/llimage/llimage.h
@@ -29,7 +29,8 @@
#include "lluuid.h"
#include "llstring.h"
-#include "llthread.h"
+#include "llpointer.h"
+#include "lltrace.h"
const S32 MIN_IMAGE_MIP = 2; // 4x4, only used for expand/contract power of 2
const S32 MAX_IMAGE_MIP = 11; // 2048x2048
@@ -110,7 +111,9 @@ protected:
//============================================================================
// Image base class
-class LLImageBase : public LLThreadSafeRefCount
+class LLImageBase
+: public LLThreadSafeRefCount,
+ public LLTrace::MemTrackable<LLImageBase>
{
protected:
virtual ~LLImageBase();
@@ -162,6 +165,8 @@ public:
static void destroyPrivatePool() ;
static LLPrivateMemoryPool* getPrivatePool() {return sPrivatePoolp;}
+ //static LLTrace::MemStatHandle sMemStat;
+
private:
U8 *mData;
S32 mDataSize;
@@ -204,6 +209,9 @@ public:
void verticalFlip();
+ static S32 biasedDimToPowerOfTwo(S32 curr_dim, S32 max_dim = MAX_IMAGE_SIZE);
+ static S32 expandDimToPowerOfTwo(S32 curr_dim, S32 max_dim = MAX_IMAGE_SIZE);
+ static S32 contractDimToPowerOfTwo(S32 curr_dim, S32 min_dim = MIN_IMAGE_SIZE);
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);
@@ -214,6 +222,9 @@ public:
// Copy operations
+ //duplicate this raw image if refCount > 1.
+ LLPointer<LLImageRaw> duplicate();
+
// Src and dst can be any size. Src and dst can each have 3 or 4 components.
void copy( LLImageRaw* src );
@@ -226,6 +237,11 @@ public:
// Src and dst are same size. Src has 3 components. Dst has 4 components.
void copyUnscaled3onto4( LLImageRaw* src );
+ // Src and dst are same size. Src has 1 component. Dst has 4 components.
+ // Alpha component is set to source alpha mask component.
+ // RGB components are set to fill color.
+ void copyUnscaledAlphaMask( LLImageRaw* src, const LLColor4U& fill);
+
// Src and dst can be any size. Src and dst have same number of components.
void copyScaled( LLImageRaw* src );
diff --git a/indra/llimage/llimagebmp.cpp b/indra/llimage/llimagebmp.cpp
index 60b1c628d7..8573fe0d91 100644..100755
--- a/indra/llimage/llimagebmp.cpp
+++ b/indra/llimage/llimagebmp.cpp
@@ -321,7 +321,7 @@ BOOL LLImageBMP::updateData()
mColorPalette = new U8[color_palette_size];
if (!mColorPalette)
{
- llerrs << "Out of memory in LLImageBMP::updateData()" << llendl;
+ LL_ERRS() << "Out of memory in LLImageBMP::updateData()" << LL_ENDL;
return FALSE;
}
memcpy( mColorPalette, mdata + FILE_HEADER_SIZE + BITMAP_HEADER_SIZE + extension_size, color_palette_size ); /* Flawfinder: ignore */
@@ -528,7 +528,7 @@ BOOL LLImageBMP::encode(const LLImageRaw* raw_image, F32 encode_time)
if( (2 == src_components) || (4 == src_components) )
{
- llinfos << "Dropping alpha information during BMP encoding" << llendl;
+ LL_INFOS() << "Dropping alpha information during BMP encoding" << LL_ENDL;
}
setSize(raw_image->getWidth(), raw_image->getHeight(), dst_components);
diff --git a/indra/llimage/llimagebmp.h b/indra/llimage/llimagebmp.h
index db0b45def0..db0b45def0 100644..100755
--- a/indra/llimage/llimagebmp.h
+++ b/indra/llimage/llimagebmp.h
diff --git a/indra/llimage/llimagedimensionsinfo.cpp b/indra/llimage/llimagedimensionsinfo.cpp
index c6bfa50b40..5bf3f29b3c 100644..100755
--- a/indra/llimage/llimagedimensionsinfo.cpp
+++ b/indra/llimage/llimagedimensionsinfo.cpp
@@ -77,7 +77,7 @@ bool LLImageDimensionsInfo::getImageDimensionsBmp()
const S32 DATA_LEN = 26; // BMP header (14) + DIB header size (4) + width (4) + height (4)
if (!checkFileLength(DATA_LEN))
{
- llwarns << "Premature end of file" << llendl;
+ LL_WARNS() << "Premature end of file" << LL_ENDL;
return false;
}
@@ -89,7 +89,7 @@ bool LLImageDimensionsInfo::getImageDimensionsBmp()
// We only support Windows bitmaps (BM), according to LLImageBMP::updateData().
if (signature[0] != 'B' || signature[1] != 'M')
{
- llwarns << "Not a BMP" << llendl;
+ LL_WARNS() << "Not a BMP" << LL_ENDL;
return false;
}
@@ -108,7 +108,7 @@ bool LLImageDimensionsInfo::getImageDimensionsTga()
// Make sure the file is long enough.
if (!checkFileLength(TGA_FILE_HEADER_SIZE + 1 /* width */ + 1 /* height */))
{
- llwarns << "Premature end of file" << llendl;
+ LL_WARNS() << "Premature end of file" << LL_ENDL;
return false;
}
@@ -127,7 +127,7 @@ bool LLImageDimensionsInfo::getImageDimensionsPng()
// Make sure the file is long enough.
if (!checkFileLength(PNG_MAGIC_SIZE + 8 + sizeof(S32) * 2 /* width, height */))
{
- llwarns << "Premature end of file" << llendl;
+ LL_WARNS() << "Premature end of file" << LL_ENDL;
return false;
}
@@ -139,7 +139,7 @@ bool LLImageDimensionsInfo::getImageDimensionsPng()
// Make sure it's a PNG file.
if (memcmp(signature, png_magic, PNG_MAGIC_SIZE) != 0)
{
- llwarns << "Not a PNG" << llendl;
+ LL_WARNS() << "Not a PNG" << LL_ENDL;
return false;
}
@@ -156,7 +156,7 @@ void on_jpeg_error(j_common_ptr cinfo)
{
(void) cinfo;
sJpegErrorEncountered = true;
- llwarns << "Libjpeg has encountered an error!" << llendl;
+ LL_WARNS() << "Libjpeg has encountered an error!" << LL_ENDL;
}
bool LLImageDimensionsInfo::getImageDimensionsJpeg()
@@ -172,17 +172,17 @@ bool LLImageDimensionsInfo::getImageDimensionsJpeg()
/* Make sure this is a JPEG file. */
const size_t JPEG_MAGIC_SIZE = 2;
- const uint8_t jpeg_magic[JPEG_MAGIC_SIZE] = {0xFF, 0xD8};
- uint8_t signature[JPEG_MAGIC_SIZE];
+ const U8 jpeg_magic[JPEG_MAGIC_SIZE] = {0xFF, 0xD8};
+ U8 signature[JPEG_MAGIC_SIZE];
if (fread(signature, sizeof(signature), 1, fp) != 1)
{
- llwarns << "Premature end of file" << llendl;
+ LL_WARNS() << "Premature end of file" << LL_ENDL;
return false;
}
if (memcmp(signature, jpeg_magic, JPEG_MAGIC_SIZE) != 0)
{
- llwarns << "Not a JPEG" << llendl;
+ LL_WARNS() << "Not a JPEG" << LL_ENDL;
return false;
}
fseek(fp, 0, SEEK_SET); // go back to start of the file
diff --git a/indra/llimage/llimagedimensionsinfo.h b/indra/llimage/llimagedimensionsinfo.h
index 382fdb2a0e..8f716c5d02 100644..100755
--- a/indra/llimage/llimagedimensionsinfo.h
+++ b/indra/llimage/llimagedimensionsinfo.h
@@ -27,6 +27,8 @@
#ifndef LL_LLIMAGEDIMENSIONSINFO_H
#define LL_LLIMAGEDIMENSIONSINFO_H
+#include "llapr.h"
+
//-----------------------------------------------------------------------------
// LLImageDimensionsInfo
// helper class to get image dimensions WITHOUT loading image to memore
diff --git a/indra/llimage/llimagedxt.cpp b/indra/llimage/llimagedxt.cpp
index 34c6793522..04e0e752eb 100644..100755
--- a/indra/llimage/llimagedxt.cpp
+++ b/indra/llimage/llimagedxt.cpp
@@ -52,7 +52,7 @@ S32 LLImageDXT::formatBits(EFileFormat format)
case FORMAT_RGB8: return 24;
case FORMAT_RGBA8: return 32;
default:
- llerrs << "LLImageDXT::Unknown format: " << format << llendl;
+ LL_ERRS() << "LLImageDXT::Unknown format: " << format << LL_ENDL;
return 0;
}
};
@@ -82,7 +82,7 @@ S32 LLImageDXT::formatComponents(EFileFormat format)
case FORMAT_RGB8: return 3;
case FORMAT_RGBA8: return 4;
default:
- llerrs << "LLImageDXT::Unknown format: " << format << llendl;
+ LL_ERRS() << "LLImageDXT::Unknown format: " << format << LL_ENDL;
return 0;
}
};
@@ -207,7 +207,7 @@ BOOL LLImageDXT::updateData()
if (data_size < mHeaderSize)
{
- llerrs << "LLImageDXT: not enough data" << llendl;
+ LL_ERRS() << "LLImageDXT: not enough data" << LL_ENDL;
}
S32 ncomponents = formatComponents(mFileFormat);
setSize(width, height, ncomponents);
@@ -224,7 +224,7 @@ S32 LLImageDXT::getMipOffset(S32 discard)
{
if (mFileFormat >= FORMAT_DXT1 && mFileFormat <= FORMAT_DXT5)
{
- llerrs << "getMipOffset called with old (unsupported) format" << llendl;
+ LL_ERRS() << "getMipOffset called with old (unsupported) format" << LL_ENDL;
}
S32 width = getWidth(), height = getHeight();
S32 num_mips = calcNumMips(width, height);
@@ -251,7 +251,7 @@ void LLImageDXT::setFormat()
{
case 3: mFileFormat = FORMAT_DXR1; break;
case 4: mFileFormat = FORMAT_DXR3; break;
- default: llerrs << "LLImageDXT::setFormat called with ncomponents = " << ncomponents << llendl;
+ default: LL_ERRS() << "LLImageDXT::setFormat called with ncomponents = " << ncomponents << LL_ENDL;
}
mHeaderSize = calcHeaderSize();
}
@@ -265,7 +265,7 @@ BOOL LLImageDXT::decode(LLImageRaw* raw_image, F32 time)
if (mFileFormat >= FORMAT_DXT1 && mFileFormat <= FORMAT_DXR5)
{
- llwarns << "Attempt to decode compressed LLImageDXT to Raw (unsupported)" << llendl;
+ LL_WARNS() << "Attempt to decode compressed LLImageDXT to Raw (unsupported)" << LL_ENDL;
return FALSE;
}
@@ -303,7 +303,7 @@ BOOL LLImageDXT::getMipData(LLPointer<LLImageRaw>& raw, S32 discard)
}
else if (discard < mDiscardLevel)
{
- llerrs << "Request for invalid discard level" << llendl;
+ LL_ERRS() << "Request for invalid discard level" << LL_ENDL;
}
U8* data = getData() + getMipOffset(discard);
S32 width = 0;
@@ -331,7 +331,7 @@ BOOL LLImageDXT::encodeDXT(const LLImageRaw* raw_image, F32 time, bool explicit_
format = FORMAT_RGBA8;
break;
default:
- llerrs << "LLImageDXT::encode: Unhandled channel number: " << ncomponents << llendl;
+ LL_ERRS() << "LLImageDXT::encode: Unhandled channel number: " << ncomponents << LL_ENDL;
return 0;
}
@@ -422,7 +422,7 @@ bool LLImageDXT::convertToDXR()
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;
+ LL_WARNS() << "convertToDXR: can not convert format: " << llformat("0x%08x",getFourCC(mFileFormat)) << LL_ENDL;
return false;
}
mFileFormat = newformat;
@@ -433,7 +433,7 @@ bool LLImageDXT::convertToDXR()
U8* newdata = (U8*)ALLOCATE_MEM(LLImageBase::getPrivatePool(), total_bytes);
if (!newdata)
{
- llerrs << "Out of memory in LLImageDXT::convertToDXR()" << llendl;
+ LL_ERRS() << "Out of memory in LLImageDXT::convertToDXR()" << LL_ENDL;
return false;
}
llassert(total_bytes > 0);
@@ -466,7 +466,7 @@ S32 LLImageDXT::calcDataSize(S32 discard_level)
{
if (mFileFormat == FORMAT_UNKNOWN)
{
- llerrs << "calcDataSize called with unloaded LLImageDXT" << llendl;
+ LL_ERRS() << "calcDataSize called with unloaded LLImageDXT" << LL_ENDL;
return 0;
}
if (discard_level < 0)
diff --git a/indra/llimage/llimagedxt.h b/indra/llimage/llimagedxt.h
index a8756ba8ed..a8756ba8ed 100644..100755
--- a/indra/llimage/llimagedxt.h
+++ b/indra/llimage/llimagedxt.h
diff --git a/indra/llimage/llimagefilter.cpp b/indra/llimage/llimagefilter.cpp
new file mode 100755
index 0000000000..41adc7be9a
--- /dev/null
+++ b/indra/llimage/llimagefilter.cpp
@@ -0,0 +1,939 @@
+/**
+ * @file llimagefilter.cpp
+ * @brief Simple Image Filtering. See https://wiki.lindenlab.com/wiki/SL_Viewer_Image_Filters for complete documentation.
+ *
+ * $LicenseInfo:firstyear=2001&license=viewerlgpl$
+ * Second Life Viewer Source Code
+ * Copyright (C) 2014, Linden Research, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
+ * $/LicenseInfo$
+ */
+
+#include "linden_common.h"
+
+#include "llimagefilter.h"
+
+#include "llmath.h"
+#include "v3color.h"
+#include "v4coloru.h"
+#include "m3math.h"
+#include "v3math.h"
+#include "llsdserialize.h"
+#include "llstring.h"
+
+//---------------------------------------------------------------------------
+// LLImageFilter
+//---------------------------------------------------------------------------
+
+LLImageFilter::LLImageFilter(const std::string& file_path) :
+ mFilterData(LLSD::emptyArray()),
+ mImage(NULL),
+ mHistoRed(NULL),
+ mHistoGreen(NULL),
+ mHistoBlue(NULL),
+ mHistoBrightness(NULL),
+ mStencilBlendMode(STENCIL_BLEND_MODE_BLEND),
+ mStencilShape(STENCIL_SHAPE_UNIFORM),
+ mStencilGamma(1.0),
+ mStencilMin(0.0),
+ mStencilMax(1.0)
+{
+ // Load filter description from file
+ llifstream filter_xml(file_path.c_str());
+ if (filter_xml.is_open())
+ {
+ // Load and parse the file
+ LLPointer<LLSDParser> parser = new LLSDXMLParser();
+ parser->parse(filter_xml, mFilterData, LLSDSerialize::SIZE_UNLIMITED);
+ filter_xml.close();
+ }
+}
+
+LLImageFilter::~LLImageFilter()
+{
+ mImage = NULL;
+ ll_aligned_free_16(mHistoRed);
+ ll_aligned_free_16(mHistoGreen);
+ ll_aligned_free_16(mHistoBlue);
+ ll_aligned_free_16(mHistoBrightness);
+}
+
+/*
+ *TODO
+ * Rename stencil to mask
+ * Improve perf: use LUT for alpha blending in uniform case
+ * Add gradient coloring as a filter
+ */
+
+//============================================================================
+// Apply the filter data to the image passed as parameter
+//============================================================================
+
+void LLImageFilter::executeFilter(LLPointer<LLImageRaw> raw_image)
+{
+ mImage = raw_image;
+
+ //std::cout << "Filter : size = " << mFilterData.size() << std::endl;
+ for (S32 i = 0; i < mFilterData.size(); ++i)
+ {
+ std::string filter_name = mFilterData[i][0].asString();
+ // Dump out the filter values (for debug)
+ //std::cout << "Filter : name = " << mFilterData[i][0].asString() << ", params = ";
+ //for (S32 j = 1; j < mFilterData[i].size(); ++j)
+ //{
+ // std::cout << mFilterData[i][j].asString() << ", ";
+ //}
+ //std::cout << std::endl;
+
+ if (filter_name == "stencil")
+ {
+ // Get the shape of the stencil, that is how the procedural alpha is computed geometrically
+ std::string filter_shape = mFilterData[i][1].asString();
+ EStencilShape shape = STENCIL_SHAPE_UNIFORM;
+ if (filter_shape == "uniform")
+ {
+ shape = STENCIL_SHAPE_UNIFORM;
+ }
+ else if (filter_shape == "gradient")
+ {
+ shape = STENCIL_SHAPE_GRADIENT;
+ }
+ else if (filter_shape == "vignette")
+ {
+ shape = STENCIL_SHAPE_VIGNETTE;
+ }
+ else if (filter_shape == "scanlines")
+ {
+ shape = STENCIL_SHAPE_SCAN_LINES;
+ }
+ // Get the blend mode of the stencil, that is how the effect is blended in the background through the stencil
+ std::string filter_mode = mFilterData[i][2].asString();
+ EStencilBlendMode mode = STENCIL_BLEND_MODE_BLEND;
+ if (filter_mode == "blend")
+ {
+ mode = STENCIL_BLEND_MODE_BLEND;
+ }
+ else if (filter_mode == "add")
+ {
+ mode = STENCIL_BLEND_MODE_ADD;
+ }
+ else if (filter_mode == "add_back")
+ {
+ mode = STENCIL_BLEND_MODE_ABACK;
+ }
+ else if (filter_mode == "fade")
+ {
+ mode = STENCIL_BLEND_MODE_FADE;
+ }
+ // Get the float params: mandatory min, max then the optional parameters (4 max)
+ F32 min = (F32)(mFilterData[i][3].asReal());
+ F32 max = (F32)(mFilterData[i][4].asReal());
+ F32 params[4] = {0.0, 0.0, 0.0, 0.0};
+ for (S32 j = 5; (j < mFilterData[i].size()) && (j < 9); j++)
+ {
+ params[j-5] = (F32)(mFilterData[i][j].asReal());
+ }
+ // Set the stencil
+ setStencil(shape,mode,min,max,params);
+ }
+ else if (filter_name == "sepia")
+ {
+ filterSepia();
+ }
+ else if (filter_name == "grayscale")
+ {
+ filterGrayScale();
+ }
+ else if (filter_name == "saturate")
+ {
+ filterSaturate((float)(mFilterData[i][1].asReal()));
+ }
+ else if (filter_name == "rotate")
+ {
+ filterRotate((float)(mFilterData[i][1].asReal()));
+ }
+ else if (filter_name == "gamma")
+ {
+ LLColor3 color((float)(mFilterData[i][2].asReal()),(float)(mFilterData[i][3].asReal()),(float)(mFilterData[i][4].asReal()));
+ filterGamma((float)(mFilterData[i][1].asReal()),color);
+ }
+ else if (filter_name == "colorize")
+ {
+ LLColor3 color((float)(mFilterData[i][1].asReal()),(float)(mFilterData[i][2].asReal()),(float)(mFilterData[i][3].asReal()));
+ LLColor3 alpha((F32)(mFilterData[i][4].asReal()),(float)(mFilterData[i][5].asReal()),(float)(mFilterData[i][6].asReal()));
+ filterColorize(color,alpha);
+ }
+ else if (filter_name == "contrast")
+ {
+ LLColor3 color((float)(mFilterData[i][2].asReal()),(float)(mFilterData[i][3].asReal()),(float)(mFilterData[i][4].asReal()));
+ filterContrast((float)(mFilterData[i][1].asReal()),color);
+ }
+ else if (filter_name == "brighten")
+ {
+ LLColor3 color((float)(mFilterData[i][2].asReal()),(float)(mFilterData[i][3].asReal()),(float)(mFilterData[i][4].asReal()));
+ filterBrightness((float)(mFilterData[i][1].asReal()),color);
+ }
+ else if (filter_name == "darken")
+ {
+ LLColor3 color((float)(mFilterData[i][2].asReal()),(float)(mFilterData[i][3].asReal()),(float)(mFilterData[i][4].asReal()));
+ filterBrightness((float)(-mFilterData[i][1].asReal()),color);
+ }
+ else if (filter_name == "linearize")
+ {
+ LLColor3 color((float)(mFilterData[i][2].asReal()),(float)(mFilterData[i][3].asReal()),(float)(mFilterData[i][4].asReal()));
+ filterLinearize((float)(mFilterData[i][1].asReal()),color);
+ }
+ else if (filter_name == "posterize")
+ {
+ LLColor3 color((float)(mFilterData[i][2].asReal()),(float)(mFilterData[i][3].asReal()),(float)(mFilterData[i][4].asReal()));
+ filterEqualize((S32)(mFilterData[i][1].asReal()),color);
+ }
+ else if (filter_name == "screen")
+ {
+ std::string screen_name = mFilterData[i][1].asString();
+ EScreenMode mode = SCREEN_MODE_2DSINE;
+ if (screen_name == "2Dsine")
+ {
+ mode = SCREEN_MODE_2DSINE;
+ }
+ else if (screen_name == "line")
+ {
+ mode = SCREEN_MODE_LINE;
+ }
+ filterScreen(mode,(F32)(mFilterData[i][2].asReal()),(F32)(mFilterData[i][3].asReal()));
+ }
+ else if (filter_name == "blur")
+ {
+ LLMatrix3 kernel;
+ for (S32 i = 0; i < NUM_VALUES_IN_MAT3; i++)
+ for (S32 j = 0; j < NUM_VALUES_IN_MAT3; j++)
+ kernel.mMatrix[i][j] = 1.0;
+ convolve(kernel,true,false);
+ }
+ else if (filter_name == "sharpen")
+ {
+ LLMatrix3 kernel;
+ for (S32 k = 0; k < NUM_VALUES_IN_MAT3; k++)
+ for (S32 j = 0; j < NUM_VALUES_IN_MAT3; j++)
+ kernel.mMatrix[k][j] = -1.0;
+ kernel.mMatrix[1][1] = 9.0;
+ convolve(kernel,false,false);
+ }
+ else if (filter_name == "gradient")
+ {
+ LLMatrix3 kernel;
+ for (S32 k = 0; k < NUM_VALUES_IN_MAT3; k++)
+ for (S32 j = 0; j < NUM_VALUES_IN_MAT3; j++)
+ kernel.mMatrix[k][j] = -1.0;
+ kernel.mMatrix[1][1] = 8.0;
+ convolve(kernel,false,true);
+ }
+ else if (filter_name == "convolve")
+ {
+ LLMatrix3 kernel;
+ S32 index = 1;
+ bool normalize = (mFilterData[i][index++].asReal() > 0.0);
+ bool abs_value = (mFilterData[i][index++].asReal() > 0.0);
+ for (S32 k = 0; k < NUM_VALUES_IN_MAT3; k++)
+ for (S32 j = 0; j < NUM_VALUES_IN_MAT3; j++)
+ kernel.mMatrix[k][j] = mFilterData[i][index++].asReal();
+ convolve(kernel,normalize,abs_value);
+ }
+ else if (filter_name == "colortransform")
+ {
+ LLMatrix3 transform;
+ S32 index = 1;
+ for (S32 k = 0; k < NUM_VALUES_IN_MAT3; k++)
+ for (S32 j = 0; j < NUM_VALUES_IN_MAT3; j++)
+ transform.mMatrix[k][j] = mFilterData[i][index++].asReal();
+ transform.transpose();
+ colorTransform(transform);
+ }
+ else
+ {
+ LL_WARNS() << "Filter unknown, cannot execute filter command : " << filter_name << LL_ENDL;
+ }
+ }
+}
+
+//============================================================================
+// Filter Primitives
+//============================================================================
+
+void LLImageFilter::blendStencil(F32 alpha, U8* pixel, U8 red, U8 green, U8 blue)
+{
+ F32 inv_alpha = 1.0 - alpha;
+ switch (mStencilBlendMode)
+ {
+ case STENCIL_BLEND_MODE_BLEND:
+ // Classic blend of incoming color with the background image
+ pixel[VRED] = inv_alpha * pixel[VRED] + alpha * red;
+ pixel[VGREEN] = inv_alpha * pixel[VGREEN] + alpha * green;
+ pixel[VBLUE] = inv_alpha * pixel[VBLUE] + alpha * blue;
+ break;
+ case STENCIL_BLEND_MODE_ADD:
+ // Add incoming color to the background image
+ pixel[VRED] = llclampb(pixel[VRED] + alpha * red);
+ pixel[VGREEN] = llclampb(pixel[VGREEN] + alpha * green);
+ pixel[VBLUE] = llclampb(pixel[VBLUE] + alpha * blue);
+ break;
+ case STENCIL_BLEND_MODE_ABACK:
+ // Add back background image to the incoming color
+ pixel[VRED] = llclampb(inv_alpha * pixel[VRED] + red);
+ pixel[VGREEN] = llclampb(inv_alpha * pixel[VGREEN] + green);
+ pixel[VBLUE] = llclampb(inv_alpha * pixel[VBLUE] + blue);
+ break;
+ case STENCIL_BLEND_MODE_FADE:
+ // Fade incoming color to black
+ pixel[VRED] = alpha * red;
+ pixel[VGREEN] = alpha * green;
+ pixel[VBLUE] = alpha * blue;
+ break;
+ }
+}
+
+void LLImageFilter::colorCorrect(const U8* lut_red, const U8* lut_green, const U8* lut_blue)
+{
+ const S32 components = mImage->getComponents();
+ llassert( components >= 1 && components <= 4 );
+
+ S32 width = mImage->getWidth();
+ S32 height = mImage->getHeight();
+
+ U8* dst_data = mImage->getData();
+ for (S32 j = 0; j < height; j++)
+ {
+ for (S32 i = 0; i < width; i++)
+ {
+ // Blend LUT value
+ blendStencil(getStencilAlpha(i,j), dst_data, lut_red[dst_data[VRED]], lut_green[dst_data[VGREEN]], lut_blue[dst_data[VBLUE]]);
+ dst_data += components;
+ }
+ }
+}
+
+void LLImageFilter::colorTransform(const LLMatrix3 &transform)
+{
+ const S32 components = mImage->getComponents();
+ llassert( components >= 1 && components <= 4 );
+
+ S32 width = mImage->getWidth();
+ S32 height = mImage->getHeight();
+
+ U8* dst_data = mImage->getData();
+ for (S32 j = 0; j < height; j++)
+ {
+ for (S32 i = 0; i < width; i++)
+ {
+ // Compute transform
+ LLVector3 src((F32)(dst_data[VRED]),(F32)(dst_data[VGREEN]),(F32)(dst_data[VBLUE]));
+ LLVector3 dst = src * transform;
+ dst.clamp(0.0f,255.0f);
+
+ // Blend result
+ blendStencil(getStencilAlpha(i,j), dst_data, dst.mV[VRED], dst.mV[VGREEN], dst.mV[VBLUE]);
+ dst_data += components;
+ }
+ }
+}
+
+void LLImageFilter::convolve(const LLMatrix3 &kernel, bool normalize, bool abs_value)
+{
+ const S32 components = mImage->getComponents();
+ llassert( components >= 1 && components <= 4 );
+
+ // Compute normalization factors
+ F32 kernel_min = 0.0;
+ F32 kernel_max = 0.0;
+ for (S32 i = 0; i < NUM_VALUES_IN_MAT3; i++)
+ {
+ for (S32 j = 0; j < NUM_VALUES_IN_MAT3; j++)
+ {
+ if (kernel.mMatrix[i][j] >= 0.0)
+ kernel_max += kernel.mMatrix[i][j];
+ else
+ kernel_min += kernel.mMatrix[i][j];
+ }
+ }
+ if (abs_value)
+ {
+ kernel_max = llabs(kernel_max);
+ kernel_min = llabs(kernel_min);
+ kernel_max = llmax(kernel_max,kernel_min);
+ kernel_min = 0.0;
+ }
+ F32 kernel_range = kernel_max - kernel_min;
+
+ // Allocate temporary buffers and initialize algorithm's data
+ S32 width = mImage->getWidth();
+ S32 height = mImage->getHeight();
+
+ U8* dst_data = mImage->getData();
+
+ S32 buffer_size = width * components;
+ llassert_always(buffer_size > 0);
+ std::vector<U8> even_buffer(buffer_size);
+ std::vector<U8> odd_buffer(buffer_size);
+
+ U8* south_data = dst_data + buffer_size;
+ U8* east_west_data;
+ U8* north_data;
+
+ // Line 0 : we set the line to 0 (debatable)
+ memcpy( &even_buffer[0], dst_data, buffer_size ); /* Flawfinder: ignore */
+ for (S32 i = 0; i < width; i++)
+ {
+ blendStencil(getStencilAlpha(i,0), dst_data, 0, 0, 0);
+ dst_data += components;
+ }
+ south_data += buffer_size;
+
+ // All other lines
+ for (S32 j = 1; j < (height-1); j++)
+ {
+ // We need to buffer 2 lines. We flip north and east-west (current) to avoid moving too much memory around
+ if (j % 2)
+ {
+ memcpy( &odd_buffer[0], dst_data, buffer_size ); /* Flawfinder: ignore */
+ east_west_data = &odd_buffer[0];
+ north_data = &even_buffer[0];
+ }
+ else
+ {
+ memcpy( &even_buffer[0], dst_data, buffer_size ); /* Flawfinder: ignore */
+ east_west_data = &even_buffer[0];
+ north_data = &odd_buffer[0];
+ }
+ // First pixel : set to 0
+ blendStencil(getStencilAlpha(0,j), dst_data, 0, 0, 0);
+ dst_data += components;
+ // Set pointers to kernel
+ U8* NW = north_data;
+ U8* N = NW+components;
+ U8* NE = N+components;
+ U8* W = east_west_data;
+ U8* C = W+components;
+ U8* E = C+components;
+ U8* SW = south_data;
+ U8* S = SW+components;
+ U8* SE = S+components;
+ // All other pixels
+ for (S32 i = 1; i < (width-1); i++)
+ {
+ // Compute convolution
+ LLVector3 dst;
+ dst.mV[VRED] = (kernel.mMatrix[0][0]*NW[VRED] + kernel.mMatrix[0][1]*N[VRED] + kernel.mMatrix[0][2]*NE[VRED] +
+ kernel.mMatrix[1][0]*W[VRED] + kernel.mMatrix[1][1]*C[VRED] + kernel.mMatrix[1][2]*E[VRED] +
+ kernel.mMatrix[2][0]*SW[VRED] + kernel.mMatrix[2][1]*S[VRED] + kernel.mMatrix[2][2]*SE[VRED]);
+ dst.mV[VGREEN] = (kernel.mMatrix[0][0]*NW[VGREEN] + kernel.mMatrix[0][1]*N[VGREEN] + kernel.mMatrix[0][2]*NE[VGREEN] +
+ kernel.mMatrix[1][0]*W[VGREEN] + kernel.mMatrix[1][1]*C[VGREEN] + kernel.mMatrix[1][2]*E[VGREEN] +
+ kernel.mMatrix[2][0]*SW[VGREEN] + kernel.mMatrix[2][1]*S[VGREEN] + kernel.mMatrix[2][2]*SE[VGREEN]);
+ dst.mV[VBLUE] = (kernel.mMatrix[0][0]*NW[VBLUE] + kernel.mMatrix[0][1]*N[VBLUE] + kernel.mMatrix[0][2]*NE[VBLUE] +
+ kernel.mMatrix[1][0]*W[VBLUE] + kernel.mMatrix[1][1]*C[VBLUE] + kernel.mMatrix[1][2]*E[VBLUE] +
+ kernel.mMatrix[2][0]*SW[VBLUE] + kernel.mMatrix[2][1]*S[VBLUE] + kernel.mMatrix[2][2]*SE[VBLUE]);
+ if (abs_value)
+ {
+ dst.mV[VRED] = llabs(dst.mV[VRED]);
+ dst.mV[VGREEN] = llabs(dst.mV[VGREEN]);
+ dst.mV[VBLUE] = llabs(dst.mV[VBLUE]);
+ }
+ if (normalize)
+ {
+ dst.mV[VRED] = (dst.mV[VRED] - kernel_min)/kernel_range;
+ dst.mV[VGREEN] = (dst.mV[VGREEN] - kernel_min)/kernel_range;
+ dst.mV[VBLUE] = (dst.mV[VBLUE] - kernel_min)/kernel_range;
+ }
+ dst.clamp(0.0f,255.0f);
+
+ // Blend result
+ blendStencil(getStencilAlpha(i,j), dst_data, dst.mV[VRED], dst.mV[VGREEN], dst.mV[VBLUE]);
+
+ // Next pixel
+ dst_data += components;
+ NW += components;
+ N += components;
+ NE += components;
+ W += components;
+ C += components;
+ E += components;
+ SW += components;
+ S += components;
+ SE += components;
+ }
+ // Last pixel : set to 0
+ blendStencil(getStencilAlpha(width-1,j), dst_data, 0, 0, 0);
+ dst_data += components;
+ south_data += buffer_size;
+ }
+
+ // Last line
+ for (S32 i = 0; i < width; i++)
+ {
+ blendStencil(getStencilAlpha(i,0), dst_data, 0, 0, 0);
+ dst_data += components;
+ }
+}
+
+void LLImageFilter::filterScreen(EScreenMode mode, const F32 wave_length, const F32 angle)
+{
+ const S32 components = mImage->getComponents();
+ llassert( components >= 1 && components <= 4 );
+
+ S32 width = mImage->getWidth();
+ S32 height = mImage->getHeight();
+
+ F32 wave_length_pixels = wave_length * (F32)(height) / 2.0;
+ F32 sin = sinf(angle*DEG_TO_RAD);
+ F32 cos = cosf(angle*DEG_TO_RAD);
+
+ // Precompute the gamma table : gives us the gray level to use when cutting outside the screen (prevents strong aliasing on the screen)
+ U8 gamma[256];
+ for (S32 i = 0; i < 256; i++)
+ {
+ F32 gamma_i = llclampf((float)(powf((float)(i)/255.0,1.0/4.0)));
+ gamma[i] = (U8)(255.0 * gamma_i);
+ }
+
+ U8* dst_data = mImage->getData();
+ for (S32 j = 0; j < height; j++)
+ {
+ for (S32 i = 0; i < width; i++)
+ {
+ // Compute screen value
+ F32 value = 0.0;
+ F32 di = 0.0;
+ F32 dj = 0.0;
+ switch (mode)
+ {
+ case SCREEN_MODE_2DSINE:
+ di = cos*i + sin*j;
+ dj = -sin*i + cos*j;
+ value = (sinf(2*F_PI*di/wave_length_pixels)*sinf(2*F_PI*dj/wave_length_pixels)+1.0)*255.0/2.0;
+ break;
+ case SCREEN_MODE_LINE:
+ dj = sin*i - cos*j;
+ value = (sinf(2*F_PI*dj/wave_length_pixels)+1.0)*255.0/2.0;
+ break;
+ }
+ U8 dst_value = (dst_data[VRED] >= (U8)(value) ? gamma[dst_data[VRED] - (U8)(value)] : 0);
+
+ // Blend result
+ blendStencil(getStencilAlpha(i,j), dst_data, dst_value, dst_value, dst_value);
+ dst_data += components;
+ }
+ }
+}
+
+//============================================================================
+// Procedural Stencils
+//============================================================================
+void LLImageFilter::setStencil(EStencilShape shape, EStencilBlendMode mode, F32 min, F32 max, F32* params)
+{
+ mStencilShape = shape;
+ mStencilBlendMode = mode;
+ mStencilMin = llmin(llmax(min, -1.0f), 1.0f);
+ mStencilMax = llmin(llmax(max, -1.0f), 1.0f);
+
+ // Each shape will interpret the 4 params differenly.
+ // We compute each systematically, though, clearly, values are meaningless when the shape doesn't correspond to the parameters
+ mStencilCenterX = (S32)(mImage->getWidth() + params[0] * (F32)(mImage->getHeight()))/2;
+ mStencilCenterY = (S32)(mImage->getHeight() + params[1] * (F32)(mImage->getHeight()))/2;
+ mStencilWidth = (S32)(params[2] * (F32)(mImage->getHeight()))/2;
+ mStencilGamma = (params[3] <= 0.0 ? 1.0 : params[3]);
+
+ mStencilWavelength = (params[0] <= 0.0 ? 10.0 : params[0] * (F32)(mImage->getHeight()) / 2.0);
+ mStencilSine = sinf(params[1]*DEG_TO_RAD);
+ mStencilCosine = cosf(params[1]*DEG_TO_RAD);
+
+ mStencilStartX = ((F32)(mImage->getWidth()) + params[0] * (F32)(mImage->getHeight()))/2.0;
+ mStencilStartY = ((F32)(mImage->getHeight()) + params[1] * (F32)(mImage->getHeight()))/2.0;
+ F32 end_x = ((F32)(mImage->getWidth()) + params[2] * (F32)(mImage->getHeight()))/2.0;
+ F32 end_y = ((F32)(mImage->getHeight()) + params[3] * (F32)(mImage->getHeight()))/2.0;
+ mStencilGradX = end_x - mStencilStartX;
+ mStencilGradY = end_y - mStencilStartY;
+ mStencilGradN = mStencilGradX*mStencilGradX + mStencilGradY*mStencilGradY;
+}
+
+F32 LLImageFilter::getStencilAlpha(S32 i, S32 j)
+{
+ F32 alpha = 1.0; // That init actually takes care of the STENCIL_SHAPE_UNIFORM case...
+ if (mStencilShape == STENCIL_SHAPE_VIGNETTE)
+ {
+ // alpha is a modified gaussian value, with a center and fading in a circular pattern toward the edges
+ // The gamma parameter controls the intensity of the drop down from alpha 1.0 (center) to 0.0
+ F32 d_center_square = (i - mStencilCenterX)*(i - mStencilCenterX) + (j - mStencilCenterY)*(j - mStencilCenterY);
+ alpha = powf(F_E, -(powf((d_center_square/(mStencilWidth*mStencilWidth)),mStencilGamma)/2.0f));
+ }
+ else if (mStencilShape == STENCIL_SHAPE_SCAN_LINES)
+ {
+ // alpha varies according to a squared sine function.
+ F32 d = mStencilSine*i - mStencilCosine*j;
+ alpha = (sinf(2*F_PI*d/mStencilWavelength) > 0.0 ? 1.0 : 0.0);
+ }
+ else if (mStencilShape == STENCIL_SHAPE_GRADIENT)
+ {
+ alpha = (((F32)(i) - mStencilStartX)*mStencilGradX + ((F32)(j) - mStencilStartY)*mStencilGradY) / mStencilGradN;
+ alpha = llclampf(alpha);
+ }
+
+ // We rescale alpha between min and max
+ return (mStencilMin + alpha * (mStencilMax - mStencilMin));
+}
+
+//============================================================================
+// Histograms
+//============================================================================
+
+U32* LLImageFilter::getBrightnessHistogram()
+{
+ if (!mHistoBrightness)
+ {
+ computeHistograms();
+ }
+ return mHistoBrightness;
+}
+
+void LLImageFilter::computeHistograms()
+{
+ const S32 components = mImage->getComponents();
+ llassert( components >= 1 && components <= 4 );
+
+ // Allocate memory for the histograms
+ if (!mHistoRed)
+ {
+ mHistoRed = (U32*) ll_aligned_malloc_16(256*sizeof(U32));
+ }
+ if (!mHistoGreen)
+ {
+ mHistoGreen = (U32*) ll_aligned_malloc_16(256*sizeof(U32));
+ }
+ if (!mHistoBlue)
+ {
+ mHistoBlue = (U32*) ll_aligned_malloc_16(256*sizeof(U32));
+ }
+ if (!mHistoBrightness)
+ {
+ mHistoBrightness = (U32*) ll_aligned_malloc_16(256*sizeof(U32));
+ }
+
+ // Initialize them
+ for (S32 i = 0; i < 256; i++)
+ {
+ mHistoRed[i] = 0;
+ mHistoGreen[i] = 0;
+ mHistoBlue[i] = 0;
+ mHistoBrightness[i] = 0;
+ }
+
+ // Compute them
+ S32 pixels = mImage->getWidth() * mImage->getHeight();
+ U8* dst_data = mImage->getData();
+ for (S32 i = 0; i < pixels; i++)
+ {
+ mHistoRed[dst_data[VRED]]++;
+ mHistoGreen[dst_data[VGREEN]]++;
+ mHistoBlue[dst_data[VBLUE]]++;
+ // Note: this is a very simple shorthand for brightness but it's OK for our use
+ S32 brightness = ((S32)(dst_data[VRED]) + (S32)(dst_data[VGREEN]) + (S32)(dst_data[VBLUE])) / 3;
+ mHistoBrightness[brightness]++;
+ // next pixel...
+ dst_data += components;
+ }
+}
+
+//============================================================================
+// Secondary Filters
+//============================================================================
+
+void LLImageFilter::filterGrayScale()
+{
+ LLMatrix3 gray_scale;
+ LLVector3 luminosity(0.2125, 0.7154, 0.0721);
+ gray_scale.setRows(luminosity, luminosity, luminosity);
+ gray_scale.transpose();
+ colorTransform(gray_scale);
+}
+
+void LLImageFilter::filterSepia()
+{
+ LLMatrix3 sepia;
+ sepia.setRows(LLVector3(0.3588, 0.7044, 0.1368),
+ LLVector3(0.2990, 0.5870, 0.1140),
+ LLVector3(0.2392, 0.4696, 0.0912));
+ sepia.transpose();
+ colorTransform(sepia);
+}
+
+void LLImageFilter::filterSaturate(F32 saturation)
+{
+ // Matrix to Lij
+ LLMatrix3 r_a;
+ LLMatrix3 r_b;
+
+ // 45 degre rotation around z
+ r_a.setRows(LLVector3( OO_SQRT2, OO_SQRT2, 0.0),
+ LLVector3(-OO_SQRT2, OO_SQRT2, 0.0),
+ LLVector3( 0.0, 0.0, 1.0));
+ // 54.73 degre rotation around y
+ float oo_sqrt3 = 1.0f / F_SQRT3;
+ float sin_54 = F_SQRT2 * oo_sqrt3;
+ r_b.setRows(LLVector3(oo_sqrt3, 0.0, -sin_54),
+ LLVector3(0.0, 1.0, 0.0),
+ LLVector3(sin_54, 0.0, oo_sqrt3));
+
+ // Coordinate conversion
+ LLMatrix3 Lij = r_b * r_a;
+ LLMatrix3 Lij_inv = Lij;
+ Lij_inv.transpose();
+
+ // Local saturation transform
+ LLMatrix3 s;
+ s.setRows(LLVector3(saturation, 0.0, 0.0),
+ LLVector3(0.0, saturation, 0.0),
+ LLVector3(0.0, 0.0, 1.0));
+
+ // Global saturation transform
+ LLMatrix3 transfo = Lij_inv * s * Lij;
+ colorTransform(transfo);
+}
+
+void LLImageFilter::filterRotate(F32 angle)
+{
+ // Matrix to Lij
+ LLMatrix3 r_a;
+ LLMatrix3 r_b;
+
+ // 45 degre rotation around z
+ r_a.setRows(LLVector3( OO_SQRT2, OO_SQRT2, 0.0),
+ LLVector3(-OO_SQRT2, OO_SQRT2, 0.0),
+ LLVector3( 0.0, 0.0, 1.0));
+ // 54.73 degre rotation around y
+ float oo_sqrt3 = 1.0f / F_SQRT3;
+ float sin_54 = F_SQRT2 * oo_sqrt3;
+ r_b.setRows(LLVector3(oo_sqrt3, 0.0, -sin_54),
+ LLVector3(0.0, 1.0, 0.0),
+ LLVector3(sin_54, 0.0, oo_sqrt3));
+
+ // Coordinate conversion
+ LLMatrix3 Lij = r_b * r_a;
+ LLMatrix3 Lij_inv = Lij;
+ Lij_inv.transpose();
+
+ // Local color rotation transform
+ LLMatrix3 r;
+ angle *= DEG_TO_RAD;
+ r.setRows(LLVector3( cosf(angle), sinf(angle), 0.0),
+ LLVector3(-sinf(angle), cosf(angle), 0.0),
+ LLVector3( 0.0, 0.0, 1.0));
+
+ // Global color rotation transform
+ LLMatrix3 transfo = Lij_inv * r * Lij;
+ colorTransform(transfo);
+}
+
+void LLImageFilter::filterGamma(F32 gamma, const LLColor3& alpha)
+{
+ U8 gamma_red_lut[256];
+ U8 gamma_green_lut[256];
+ U8 gamma_blue_lut[256];
+
+ for (S32 i = 0; i < 256; i++)
+ {
+ F32 gamma_i = llclampf((float)(powf((float)(i)/255.0,1.0/gamma)));
+ // Blend in with alpha values
+ gamma_red_lut[i] = (U8)((1.0 - alpha.mV[0]) * (float)(i) + alpha.mV[0] * 255.0 * gamma_i);
+ gamma_green_lut[i] = (U8)((1.0 - alpha.mV[1]) * (float)(i) + alpha.mV[1] * 255.0 * gamma_i);
+ gamma_blue_lut[i] = (U8)((1.0 - alpha.mV[2]) * (float)(i) + alpha.mV[2] * 255.0 * gamma_i);
+ }
+
+ colorCorrect(gamma_red_lut,gamma_green_lut,gamma_blue_lut);
+}
+
+void LLImageFilter::filterLinearize(F32 tail, const LLColor3& alpha)
+{
+ // Get the histogram
+ U32* histo = getBrightnessHistogram();
+
+ // Compute cumulated histogram
+ U32 cumulated_histo[256];
+ cumulated_histo[0] = histo[0];
+ for (S32 i = 1; i < 256; i++)
+ {
+ cumulated_histo[i] = cumulated_histo[i-1] + histo[i];
+ }
+
+ // Compute min and max counts minus tail
+ tail = llclampf(tail);
+ S32 total = cumulated_histo[255];
+ S32 min_c = (S32)((F32)(total) * tail);
+ S32 max_c = (S32)((F32)(total) * (1.0 - tail));
+
+ // Find min and max values
+ S32 min_v = 0;
+ while (cumulated_histo[min_v] < min_c)
+ {
+ min_v++;
+ }
+ S32 max_v = 255;
+ while (cumulated_histo[max_v] > max_c)
+ {
+ max_v--;
+ }
+
+ // Compute linear lookup table
+ U8 linear_red_lut[256];
+ U8 linear_green_lut[256];
+ U8 linear_blue_lut[256];
+ if (max_v == min_v)
+ {
+ // Degenerated binary split case
+ for (S32 i = 0; i < 256; i++)
+ {
+ U8 value_i = (i < min_v ? 0 : 255);
+ // Blend in with alpha values
+ linear_red_lut[i] = (U8)((1.0 - alpha.mV[0]) * (float)(i) + alpha.mV[0] * value_i);
+ linear_green_lut[i] = (U8)((1.0 - alpha.mV[1]) * (float)(i) + alpha.mV[1] * value_i);
+ linear_blue_lut[i] = (U8)((1.0 - alpha.mV[2]) * (float)(i) + alpha.mV[2] * value_i);
+ }
+ }
+ else
+ {
+ // Linearize between min and max
+ F32 slope = 255.0 / (F32)(max_v - min_v);
+ F32 translate = -min_v * slope;
+ for (S32 i = 0; i < 256; i++)
+ {
+ U8 value_i = (U8)(llclampb((S32)(slope*i + translate)));
+ // Blend in with alpha values
+ linear_red_lut[i] = (U8)((1.0 - alpha.mV[0]) * (float)(i) + alpha.mV[0] * value_i);
+ linear_green_lut[i] = (U8)((1.0 - alpha.mV[1]) * (float)(i) + alpha.mV[1] * value_i);
+ linear_blue_lut[i] = (U8)((1.0 - alpha.mV[2]) * (float)(i) + alpha.mV[2] * value_i);
+ }
+ }
+
+ // Apply lookup table
+ colorCorrect(linear_red_lut,linear_green_lut,linear_blue_lut);
+}
+
+void LLImageFilter::filterEqualize(S32 nb_classes, const LLColor3& alpha)
+{
+ // Regularize the parameter: must be between 2 and 255
+ nb_classes = llmax(nb_classes,2);
+ nb_classes = llclampb(nb_classes);
+
+ // Get the histogram
+ U32* histo = getBrightnessHistogram();
+
+ // Compute cumulated histogram
+ U32 cumulated_histo[256];
+ cumulated_histo[0] = histo[0];
+ for (S32 i = 1; i < 256; i++)
+ {
+ cumulated_histo[i] = cumulated_histo[i-1] + histo[i];
+ }
+
+ // Compute deltas
+ S32 total = cumulated_histo[255];
+ S32 delta_count = total / nb_classes;
+ S32 current_count = delta_count;
+ S32 delta_value = 256 / (nb_classes - 1);
+ S32 current_value = 0;
+
+ // Compute equalized lookup table
+ U8 equalize_red_lut[256];
+ U8 equalize_green_lut[256];
+ U8 equalize_blue_lut[256];
+ for (S32 i = 0; i < 256; i++)
+ {
+ // Blend in current_value with alpha values
+ equalize_red_lut[i] = (U8)((1.0 - alpha.mV[0]) * (float)(i) + alpha.mV[0] * current_value);
+ equalize_green_lut[i] = (U8)((1.0 - alpha.mV[1]) * (float)(i) + alpha.mV[1] * current_value);
+ equalize_blue_lut[i] = (U8)((1.0 - alpha.mV[2]) * (float)(i) + alpha.mV[2] * current_value);
+ if (cumulated_histo[i] >= current_count)
+ {
+ current_count += delta_count;
+ current_value += delta_value;
+ current_value = llclampb(current_value);
+ }
+ }
+
+ // Apply lookup table
+ colorCorrect(equalize_red_lut,equalize_green_lut,equalize_blue_lut);
+}
+
+void LLImageFilter::filterColorize(const LLColor3& color, const LLColor3& alpha)
+{
+ U8 red_lut[256];
+ U8 green_lut[256];
+ U8 blue_lut[256];
+
+ F32 red_composite = 255.0 * alpha.mV[0] * color.mV[0];
+ F32 green_composite = 255.0 * alpha.mV[1] * color.mV[1];
+ F32 blue_composite = 255.0 * alpha.mV[2] * color.mV[2];
+
+ for (S32 i = 0; i < 256; i++)
+ {
+ red_lut[i] = (U8)(llclampb((S32)((1.0 - alpha.mV[0]) * (F32)(i) + red_composite)));
+ green_lut[i] = (U8)(llclampb((S32)((1.0 - alpha.mV[1]) * (F32)(i) + green_composite)));
+ blue_lut[i] = (U8)(llclampb((S32)((1.0 - alpha.mV[2]) * (F32)(i) + blue_composite)));
+ }
+
+ colorCorrect(red_lut,green_lut,blue_lut);
+}
+
+void LLImageFilter::filterContrast(F32 slope, const LLColor3& alpha)
+{
+ U8 contrast_red_lut[256];
+ U8 contrast_green_lut[256];
+ U8 contrast_blue_lut[256];
+
+ F32 translate = 128.0 * (1.0 - slope);
+
+ for (S32 i = 0; i < 256; i++)
+ {
+ U8 value_i = (U8)(llclampb((S32)(slope*i + translate)));
+ // Blend in with alpha values
+ contrast_red_lut[i] = (U8)((1.0 - alpha.mV[0]) * (float)(i) + alpha.mV[0] * value_i);
+ contrast_green_lut[i] = (U8)((1.0 - alpha.mV[1]) * (float)(i) + alpha.mV[1] * value_i);
+ contrast_blue_lut[i] = (U8)((1.0 - alpha.mV[2]) * (float)(i) + alpha.mV[2] * value_i);
+ }
+
+ colorCorrect(contrast_red_lut,contrast_green_lut,contrast_blue_lut);
+}
+
+void LLImageFilter::filterBrightness(F32 add, const LLColor3& alpha)
+{
+ U8 brightness_red_lut[256];
+ U8 brightness_green_lut[256];
+ U8 brightness_blue_lut[256];
+
+ S32 add_value = (S32)(add * 255.0);
+
+ for (S32 i = 0; i < 256; i++)
+ {
+ U8 value_i = (U8)(llclampb(i + add_value));
+ // Blend in with alpha values
+ brightness_red_lut[i] = (U8)((1.0 - alpha.mV[0]) * (float)(i) + alpha.mV[0] * value_i);
+ brightness_green_lut[i] = (U8)((1.0 - alpha.mV[1]) * (float)(i) + alpha.mV[1] * value_i);
+ brightness_blue_lut[i] = (U8)((1.0 - alpha.mV[2]) * (float)(i) + alpha.mV[2] * value_i);
+ }
+
+ colorCorrect(brightness_red_lut,brightness_green_lut,brightness_blue_lut);
+}
+
+//============================================================================
diff --git a/indra/llimage/llimagefilter.h b/indra/llimage/llimagefilter.h
new file mode 100755
index 0000000000..16ec395f76
--- /dev/null
+++ b/indra/llimage/llimagefilter.h
@@ -0,0 +1,137 @@
+/**
+ * @file llimagefilter.h
+ * @brief Simple Image Filtering. See https://wiki.lindenlab.com/wiki/SL_Viewer_Image_Filters for complete documentation.
+ *
+ * $LicenseInfo:firstyear=2000&license=viewerlgpl$
+ * Second Life Viewer Source Code
+ * Copyright (C) 2014, Linden Research, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
+ * $/LicenseInfo$
+ */
+
+#ifndef LL_LLIMAGEFILTER_H
+#define LL_LLIMAGEFILTER_H
+
+#include "llsd.h"
+#include "llimage.h"
+
+class LLImageRaw;
+class LLColor4U;
+class LLColor3;
+class LLMatrix3;
+
+typedef enum e_stencil_blend_mode
+{
+ STENCIL_BLEND_MODE_BLEND = 0,
+ STENCIL_BLEND_MODE_ADD = 1,
+ STENCIL_BLEND_MODE_ABACK = 2,
+ STENCIL_BLEND_MODE_FADE = 3
+} EStencilBlendMode;
+
+typedef enum e_stencil_shape
+{
+ STENCIL_SHAPE_UNIFORM = 0,
+ STENCIL_SHAPE_GRADIENT = 1,
+ STENCIL_SHAPE_VIGNETTE = 2,
+ STENCIL_SHAPE_SCAN_LINES = 3
+} EStencilShape;
+
+typedef enum e_screen_mode
+{
+ SCREEN_MODE_2DSINE = 0,
+ SCREEN_MODE_LINE = 1
+} EScreenMode;
+
+//============================================================================
+// LLImageFilter
+//============================================================================
+
+class LLImageFilter
+{
+public:
+ LLImageFilter(const std::string& file_path);
+ ~LLImageFilter();
+
+ void executeFilter(LLPointer<LLImageRaw> raw_image);
+
+private:
+ // Filter Operations : Transforms
+ void filterGrayScale(); // Convert to grayscale
+ void filterSepia(); // Convert to sepia
+ void filterSaturate(F32 saturation); // < 1.0 desaturates, > 1.0 saturates
+ void filterRotate(F32 angle); // Rotates hue according to angle, angle in degrees
+
+ // Filter Operations : Color Corrections
+ // When specified, the LLColor3 alpha parameter indicates the intensity of the effect for each color channel
+ // acting in effect as an alpha blending factor different for each channel. For instance (1.0,0.0,0.0) will apply
+ // the effect only to the Red channel. Intermediate values blends the effect with the source color.
+ void filterGamma(F32 gamma, const LLColor3& alpha); // Apply gamma to each channel
+ void filterLinearize(F32 tail, const LLColor3& alpha); // Use histogram to linearize constrast between min and max values minus tail
+ void filterEqualize(S32 nb_classes, const LLColor3& alpha); // Use histogram to equalize constrast between nb_classes throughout the image
+ void filterColorize(const LLColor3& color, const LLColor3& alpha); // Colorize with color and alpha per channel
+ void filterContrast(F32 slope, const LLColor3& alpha); // Change contrast according to slope: > 1.0 more contrast, < 1.0 less contrast
+ void filterBrightness(F32 add, const LLColor3& alpha); // Change brightness according to add: > 0 brighter, < 0 darker
+
+ // Filter Primitives
+ void colorTransform(const LLMatrix3 &transform);
+ void colorCorrect(const U8* lut_red, const U8* lut_green, const U8* lut_blue);
+ void filterScreen(EScreenMode mode, const F32 wave_length, const F32 angle);
+ void blendStencil(F32 alpha, U8* pixel, U8 red, U8 green, U8 blue);
+ void convolve(const LLMatrix3 &kernel, bool normalize, bool abs_value);
+
+ // Procedural Stencils
+ void setStencil(EStencilShape shape, EStencilBlendMode mode, F32 min, F32 max, F32* params);
+ F32 getStencilAlpha(S32 i, S32 j);
+
+ // Histograms
+ U32* getBrightnessHistogram();
+ void computeHistograms();
+
+ LLSD mFilterData;
+ LLPointer<LLImageRaw> mImage;
+
+ // Histograms (if we ever happen to need them)
+ U32 *mHistoRed;
+ U32 *mHistoGreen;
+ U32 *mHistoBlue;
+ U32 *mHistoBrightness;
+
+ // Current Stencil Settings
+ EStencilBlendMode mStencilBlendMode;
+ EStencilShape mStencilShape;
+ F32 mStencilMin;
+ F32 mStencilMax;
+
+ S32 mStencilCenterX;
+ S32 mStencilCenterY;
+ S32 mStencilWidth;
+ F32 mStencilGamma;
+
+ F32 mStencilWavelength;
+ F32 mStencilSine;
+ F32 mStencilCosine;
+
+ F32 mStencilStartX;
+ F32 mStencilStartY;
+ F32 mStencilGradX;
+ F32 mStencilGradY;
+ F32 mStencilGradN;
+};
+
+
+#endif
diff --git a/indra/llimage/llimagej2c.cpp b/indra/llimage/llimagej2c.cpp
index 5412f98ee5..7cd59a2983 100644..100755
--- a/indra/llimage/llimagej2c.cpp
+++ b/indra/llimage/llimagej2c.cpp
@@ -24,11 +24,13 @@
*/
#include "linden_common.h"
+#include "llapr.h"
#include "lldir.h"
#include "llimagej2c.h"
#include "lltimer.h"
#include "llmath.h"
#include "llmemory.h"
+#include "llsd.h"
typedef LLImageJ2CImpl* (*CreateLLImageJ2CFunction)();
typedef void (*DestroyLLImageJ2CFunction)(LLImageJ2CImpl*);
@@ -60,6 +62,7 @@ LLImageJ2C::LLImageJ2C() : LLImageFormatted(IMG_CODEC_J2C),
mAreaUsedForDataSizeCalcs(0)
{
mImpl = fallbackCreateLLImageJ2CImpl();
+ claimMem(mImpl);
// Clear data size table
for( S32 i = 0; i <= MAX_DISCARD_LEVEL; i++)
diff --git a/indra/llimage/llimagej2c.h b/indra/llimage/llimagej2c.h
index ce8195940d..ce8195940d 100644..100755
--- a/indra/llimage/llimagej2c.h
+++ b/indra/llimage/llimagej2c.h
diff --git a/indra/llimage/llimagejpeg.cpp b/indra/llimage/llimagejpeg.cpp
index b70f84efc8..e419c77ff2 100644..100755
--- a/indra/llimage/llimagejpeg.cpp
+++ b/indra/llimage/llimagejpeg.cpp
@@ -32,8 +32,7 @@
jmp_buf LLImageJPEG::sSetjmpBuffer ;
LLImageJPEG::LLImageJPEG(S32 quality)
- :
- LLImageFormatted(IMG_CODEC_JPEG),
+: LLImageFormatted(IMG_CODEC_JPEG),
mOutputBuffer( NULL ),
mOutputBufferSize( 0 ),
mEncodeQuality( quality ) // on a scale from 1 to 100
@@ -374,7 +373,7 @@ boolean LLImageJPEG::encodeEmptyOutputBuffer( j_compress_ptr cinfo )
U8* new_buffer = new U8[ new_buffer_size ];
if (!new_buffer)
{
- llerrs << "Out of memory in LLImageJPEG::encodeEmptyOutputBuffer( j_compress_ptr cinfo )" << llendl;
+ LL_ERRS() << "Out of memory in LLImageJPEG::encodeEmptyOutputBuffer( j_compress_ptr cinfo )" << LL_ENDL;
return FALSE;
}
memcpy( new_buffer, self->mOutputBuffer, self->mOutputBufferSize ); /* Flawfinder: ignore */
@@ -383,7 +382,9 @@ boolean LLImageJPEG::encodeEmptyOutputBuffer( j_compress_ptr cinfo )
cinfo->dest->next_output_byte = self->mOutputBuffer + self->mOutputBufferSize;
cinfo->dest->free_in_buffer = self->mOutputBufferSize;
+ self->disclaimMem(self->mOutputBufferSize);
self->mOutputBufferSize = new_buffer_size;
+ self->claimMem(new_buffer_size);
return TRUE;
}
@@ -465,7 +466,7 @@ void LLImageJPEG::errorOutputMessage( j_common_ptr cinfo )
LLImage::setLastError(error);
BOOL is_decode = (cinfo->is_decompressor != 0);
- llwarns << "LLImageJPEG " << (is_decode ? "decode " : "encode ") << " failed: " << buffer << llendl;
+ LL_WARNS() << "LLImageJPEG " << (is_decode ? "decode " : "encode ") << " failed: " << buffer << LL_ENDL;
}
BOOL LLImageJPEG::encode( const LLImageRaw* raw_image, F32 encode_time )
@@ -489,7 +490,9 @@ BOOL LLImageJPEG::encode( const LLImageRaw* raw_image, F32 encode_time )
// 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;
+ disclaimMem(mOutputBufferSize);
mOutputBufferSize = getWidth() * getHeight() * getComponents() + 1024;
+ claimMem(mOutputBufferSize);
mOutputBuffer = new U8[ mOutputBufferSize ];
const U8* raw_image_data = NULL;
@@ -526,6 +529,7 @@ BOOL LLImageJPEG::encode( const LLImageRaw* raw_image, F32 encode_time )
jpeg_destroy_compress(&cinfo);
delete[] mOutputBuffer;
mOutputBuffer = NULL;
+ disclaimMem(mOutputBufferSize);
mOutputBufferSize = 0;
return FALSE;
}
@@ -628,6 +632,7 @@ BOOL LLImageJPEG::encode( const LLImageRaw* raw_image, F32 encode_time )
// After finish_compress, we can release the temp output buffer.
delete[] mOutputBuffer;
mOutputBuffer = NULL;
+ disclaimMem(mOutputBufferSize);
mOutputBufferSize = 0;
////////////////////////////////////////
@@ -640,6 +645,7 @@ BOOL LLImageJPEG::encode( const LLImageRaw* raw_image, F32 encode_time )
jpeg_destroy_compress(&cinfo);
delete[] mOutputBuffer;
mOutputBuffer = NULL;
+ disclaimMem(mOutputBufferSize);
mOutputBufferSize = 0;
return FALSE;
}
diff --git a/indra/llimage/llimagejpeg.h b/indra/llimage/llimagejpeg.h
index 7ac7f5d2e0..2142660c81 100644..100755
--- a/indra/llimage/llimagejpeg.h
+++ b/indra/llimage/llimagejpeg.h
@@ -31,8 +31,9 @@
#include "llimage.h"
+#include "llwin32headerslean.h"
extern "C" {
-#ifdef LL_STANDALONE
+#ifdef LL_USESYSTEMLIBS
# include <jpeglib.h>
# include <jerror.h>
#else
diff --git a/indra/llimage/llimagepng.cpp b/indra/llimage/llimagepng.cpp
index 294f68b122..7735dc1379 100644..100755
--- a/indra/llimage/llimagepng.cpp
+++ b/indra/llimage/llimagepng.cpp
@@ -67,7 +67,7 @@ BOOL LLImagePNG::updateData()
}
LLPngWrapper::ImageInfo infop;
- if (! pngWrapper.readPng(getData(), NULL, &infop))
+ if (! pngWrapper.readPng(getData(), getDataSize(), NULL, &infop))
{
setLastError(pngWrapper.getErrorMessage());
return FALSE;
@@ -102,7 +102,7 @@ BOOL LLImagePNG::decode(LLImageRaw* raw_image, F32 decode_time)
return FALSE;
}
- if (! pngWrapper.readPng(getData(), raw_image))
+ if (! pngWrapper.readPng(getData(), getDataSize(), raw_image))
{
setLastError(pngWrapper.getErrorMessage());
return FALSE;
diff --git a/indra/llimage/llimagepng.h b/indra/llimage/llimagepng.h
index 1fbd850a2e..1fbd850a2e 100644..100755
--- a/indra/llimage/llimagepng.h
+++ b/indra/llimage/llimagepng.h
diff --git a/indra/llimage/llimagetga.cpp b/indra/llimage/llimagetga.cpp
index 58426d31fa..4eb8dc7440 100644..100755
--- a/indra/llimage/llimagetga.cpp
+++ b/indra/llimage/llimagetga.cpp
@@ -132,12 +132,12 @@ BOOL LLImageTGA::updateData()
** FIELD 2 : COLOR MAP TYPE (1 BYTES)
** FIELD 3 : IMAGE TYPE CODE (1 BYTES)
** = 0 NO IMAGE DATA INCLUDED
- ** = 1 UNCOMPRESSED, COLOR-MAPPED IMAGE
- ** = 2 UNCOMPRESSED, TRUE-COLOR IMAGE
- ** = 3 UNCOMPRESSED, BLACK AND WHITE IMAGE
- ** = 9 RUN-LENGTH ENCODED COLOR-MAPPED IMAGE
- ** = 10 RUN-LENGTH ENCODED TRUE-COLOR IMAGE
- ** = 11 RUN-LENGTH ENCODED BLACK AND WHITE IMAGE
+ ** = (0001) 1 UNCOMPRESSED, COLOR-MAPPED IMAGE
+ ** = (0010) 2 UNCOMPRESSED, TRUE-COLOR IMAGE
+ ** = (0011) 3 UNCOMPRESSED, BLACK AND WHITE IMAGE
+ ** = (1001) 9 RUN-LENGTH ENCODED COLOR-MAPPED IMAGE
+ ** = (1010) 10 RUN-LENGTH ENCODED TRUE-COLOR IMAGE
+ ** = (1011) 11 RUN-LENGTH ENCODED BLACK AND WHITE IMAGE
** FIELD 4 : COLOR MAP SPECIFICATION (5 BYTES)
** 4.1 : COLOR MAP ORIGIN (2 BYTES)
** 4.2 : COLOR MAP LENGTH (2 BYTES)
@@ -266,7 +266,7 @@ BOOL LLImageTGA::updateData()
mColorMap = new U8[ color_map_bytes ];
if (!mColorMap)
{
- llerrs << "Out of Memory in BOOL LLImageTGA::updateData()" << llendl;
+ LL_ERRS() << "Out of Memory in BOOL LLImageTGA::updateData()" << LL_ENDL;
return FALSE;
}
memcpy( mColorMap, getData() + mDataOffset, color_map_bytes ); /* Flawfinder: ignore */
@@ -1043,7 +1043,7 @@ BOOL LLImageTGA::decodeAndProcess( LLImageRaw* raw_image, F32 domain, F32 weight
// Only works for unflipped monochrome RLE images
if( (getComponents() != 1) || (mImageType != 11) || mOriginTopBit || mOriginRightBit )
{
- llerrs << "LLImageTGA trying to alpha-gradient process an image that's not a standard RLE, one component image" << llendl;
+ LL_ERRS() << "LLImageTGA trying to alpha-gradient process an image that's not a standard RLE, one component image" << LL_ENDL;
return FALSE;
}
@@ -1151,7 +1151,7 @@ bool LLImageTGA::loadFile( const std::string& path )
LLFILE* file = LLFile::fopen(path, "rb"); /* Flawfinder: ignore */
if( !file )
{
- llwarns << "Couldn't open file " << path << llendl;
+ LL_WARNS() << "Couldn't open file " << path << LL_ENDL;
return false;
}
@@ -1167,7 +1167,7 @@ bool LLImageTGA::loadFile( const std::string& path )
if( bytes_read != file_size )
{
deleteData();
- llwarns << "Couldn't read file " << path << llendl;
+ LL_WARNS() << "Couldn't read file " << path << LL_ENDL;
return false;
}
@@ -1175,7 +1175,7 @@ bool LLImageTGA::loadFile( const std::string& path )
if( !updateData() )
{
- llwarns << "Couldn't decode file " << path << llendl;
+ LL_WARNS() << "Couldn't decode file " << path << LL_ENDL;
deleteData();
return false;
}
diff --git a/indra/llimage/llimagetga.h b/indra/llimage/llimagetga.h
index 5da3525149..5da3525149 100644..100755
--- a/indra/llimage/llimagetga.h
+++ b/indra/llimage/llimagetga.h
diff --git a/indra/llimage/llimageworker.cpp b/indra/llimage/llimageworker.cpp
index ad2eb0f69c..4875fe7001 100644..100755
--- a/indra/llimage/llimageworker.cpp
+++ b/indra/llimage/llimageworker.cpp
@@ -60,7 +60,7 @@ S32 LLImageDecodeThread::update(F32 max_time_ms)
bool res = addRequest(req);
if (!res)
{
- llerrs << "request added after LLLFSThread::cleanupClass()" << llendl;
+ LL_ERRS() << "request added after LLLFSThread::cleanupClass()" << LL_ENDL;
}
}
mCreationList.clear();
@@ -143,7 +143,8 @@ bool LLImageDecodeThread::ImageRequest::processRequest()
mFormattedImage->getComponents());
}
done = mFormattedImage->decode(mDecodedImageRaw, decode_time_slice); // 1ms
- mDecodedRaw = done;
+ // some decoders are removing data when task is complete and there were errors
+ mDecodedRaw = done && mDecodedImageRaw->getData();
}
if (done && mNeedsAux && !mDecodedAux && mFormattedImage.notNull())
{
@@ -155,7 +156,7 @@ bool LLImageDecodeThread::ImageRequest::processRequest()
1);
}
done = mFormattedImage->decodeChannels(mDecodedImageAux, decode_time_slice, 4, 4); // 1ms
- mDecodedAux = done;
+ mDecodedAux = done && mDecodedImageAux->getData();
}
return done;
diff --git a/indra/llimage/llimageworker.h b/indra/llimage/llimageworker.h
index 1bfb0ddfd3..1bfb0ddfd3 100644..100755
--- a/indra/llimage/llimageworker.h
+++ b/indra/llimage/llimageworker.h
diff --git a/indra/llimage/llmapimagetype.h b/indra/llimage/llmapimagetype.h
index 0a040d3db9..0a040d3db9 100644..100755
--- a/indra/llimage/llmapimagetype.h
+++ b/indra/llimage/llmapimagetype.h
diff --git a/indra/llimage/llpngwrapper.cpp b/indra/llimage/llpngwrapper.cpp
index 2cc7d3c460..aad139f570 100644..100755
--- a/indra/llimage/llpngwrapper.cpp
+++ b/indra/llimage/llpngwrapper.cpp
@@ -87,6 +87,12 @@ void LLPngWrapper::errorHandler(png_structp png_ptr, png_const_charp msg)
void LLPngWrapper::readDataCallback(png_structp png_ptr, png_bytep dest, png_size_t length)
{
PngDataInfo *dataInfo = (PngDataInfo *) png_get_io_ptr(png_ptr);
+ if(dataInfo->mOffset + length > dataInfo->mDataSize)
+ {
+ png_error(png_ptr, "Data read error. Requested data size exceeds available data size.");
+ return;
+ }
+
U8 *src = &dataInfo->mData[dataInfo->mOffset];
memcpy(dest, src, length);
dataInfo->mOffset += static_cast<U32>(length);
@@ -114,7 +120,7 @@ void LLPngWrapper::writeFlush(png_structp png_ptr)
// 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)
+BOOL LLPngWrapper::readPng(U8* src, S32 dataSize, LLImageRaw* rawImage, ImageInfo *infop)
{
try
{
@@ -133,6 +139,7 @@ BOOL LLPngWrapper::readPng(U8* src, LLImageRaw* rawImage, ImageInfo *infop)
PngDataInfo dataPtr;
dataPtr.mData = src;
dataPtr.mOffset = 0;
+ dataPtr.mDataSize = dataSize;
png_set_read_fn(mReadPngPtr, &dataPtr, &readDataCallback);
png_set_sig_bytes(mReadPngPtr, 0);
diff --git a/indra/llimage/llpngwrapper.h b/indra/llimage/llpngwrapper.h
index 739f435996..27d7df3bef 100644..100755
--- a/indra/llimage/llpngwrapper.h
+++ b/indra/llimage/llpngwrapper.h
@@ -44,7 +44,7 @@ public:
};
BOOL isValidPng(U8* src);
- BOOL readPng(U8* src, LLImageRaw* rawImage, ImageInfo *infop = NULL);
+ BOOL readPng(U8* src, S32 dataSize, LLImageRaw* rawImage, ImageInfo *infop = NULL);
BOOL writePng(const LLImageRaw* rawImage, U8* dst);
U32 getFinalSize();
const std::string& getErrorMessage();
@@ -61,6 +61,7 @@ private:
{
U8 *mData;
U32 mOffset;
+ S32 mDataSize;
};
static void writeFlush(png_structp png_ptr);
diff --git a/indra/llimage/tests/llimageworker_test.cpp b/indra/llimage/tests/llimageworker_test.cpp
index e255d65b43..51c5c63556 100644..100755
--- a/indra/llimage/tests/llimageworker_test.cpp
+++ b/indra/llimage/tests/llimageworker_test.cpp
@@ -31,6 +31,8 @@
#include "../llimageworker.h"
// For timer class
#include "../llcommon/lltimer.h"
+// for lltrace class
+#include "../llcommon/lltrace.h"
// Tut header
#include "../test/lltut.h"
@@ -43,7 +45,8 @@
// * A simulator for a class can be implemented here. Please comment and document thoroughly.
LLImageBase::LLImageBase()
-: mData(NULL),
+: LLTrace::MemTrackable<LLImageBase>("LLImageBase"),
+mData(NULL),
mDataSize(0),
mWidth(0),
mHeight(0),
@@ -64,6 +67,8 @@ LLImageRaw::~LLImageRaw() { }
void LLImageRaw::deleteData() { }
U8* LLImageRaw::allocateData(S32 size) { return NULL; }
U8* LLImageRaw::reallocateData(S32 size) { return NULL; }
+const U8* LLImageBase::getData() const { return NULL; }
+U8* LLImageBase::getData() { return NULL; }
// End Stubbing
// -------------------------------------------------------------------------------------------
@@ -110,7 +115,6 @@ namespace tut
{
// Instance to be tested
LLImageDecodeThread* mThread;
-
// Constructor and destructor of the test wrapper
imagedecodethread_test()
{
@@ -136,6 +140,7 @@ namespace tut
imagerequest_test()
{
done = false;
+
mRequest = new LLImageDecodeThread::ImageRequest(0, 0,
LLQueuedThread::PRIORITY_NORMAL, 0, FALSE,
new responder_test(&done));