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.cpp395
-rwxr-xr-x[-rw-r--r--]indra/llimage/llimage.h74
-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.cpp85
-rwxr-xr-x[-rw-r--r--]indra/llimage/llimagedimensionsinfo.h5
-rwxr-xr-x[-rw-r--r--]indra/llimage/llimagedxt.cpp25
-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.cpp112
-rwxr-xr-x[-rw-r--r--]indra/llimage/llimagej2c.h14
-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.cpp16
-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.cpp15
-rwxr-xr-x[-rw-r--r--]indra/llimage/llimageworker.h4
-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.cpp12
25 files changed, 1577 insertions, 326 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 39211bf7fa..16df27bb8e 100644..100755
--- a/indra/llimage/llimage.cpp
+++ b/indra/llimage/llimage.cpp
@@ -26,11 +26,11 @@
#include "linden_common.h"
+#include "llimageworker.h"
#include "llimage.h"
#include "llmath.h"
#include "v4coloru.h"
-#include "llmemtype.h"
#include "llimagebmp.h"
#include "llimagetga.h"
@@ -38,7 +38,7 @@
#include "llimagejpeg.h"
#include "llimagepng.h"
#include "llimagedxt.h"
-#include "llimageworker.h"
+#include "llmemory.h"
//---------------------------------------------------------------------------
// LLImage
@@ -47,11 +47,18 @@
//static
std::string LLImage::sLastErrorMessage;
LLMutex* LLImage::sMutex = NULL;
+bool LLImage::sUseNewByteRange = false;
+S32 LLImage::sMinimalReverseByteRangePercent = 75;
+LLPrivateMemoryPool* LLImageBase::sPrivatePoolp = NULL ;
//static
-void LLImage::initClass()
+void LLImage::initClass(bool use_new_byte_range, S32 minimal_reverse_byte_range_percent)
{
+ sUseNewByteRange = use_new_byte_range;
+ sMinimalReverseByteRangePercent = minimal_reverse_byte_range_percent;
sMutex = new LLMutex(NULL);
+
+ LLImageBase::createPrivatePool() ;
}
//static
@@ -59,6 +66,8 @@ void LLImage::cleanupClass()
{
delete sMutex;
sMutex = NULL;
+
+ LLImageBase::destroyPrivatePool() ;
}
//static
@@ -80,16 +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),
- mMemType(LLMemType::MTYPE_IMAGEBASE)
-{
-}
+: LLTrace::MemTrackable<LLImageBase>("LLImage"),
+ mData(NULL),
+ mDataSize(0),
+ mWidth(0),
+ mHeight(0),
+ mComponents(0),
+ mBadBufferAllocation(false),
+ mAllowOverSize(false)
+{}
// virtual
LLImageBase::~LLImageBase()
@@ -97,15 +105,34 @@ LLImageBase::~LLImageBase()
deleteData(); // virtual
}
+//static
+void LLImageBase::createPrivatePool()
+{
+ if(!sPrivatePoolp)
+ {
+ sPrivatePoolp = LLPrivateMemoryPoolManager::getInstance()->newPool(LLPrivateMemoryPool::STATIC_THREADED) ;
+ }
+}
+
+//static
+void LLImageBase::destroyPrivatePool()
+{
+ if(sPrivatePoolp)
+ {
+ LLPrivateMemoryPoolManager::getInstance()->deletePool(sPrivatePoolp) ;
+ sPrivatePoolp = NULL ;
+ }
+}
+
// 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
@@ -117,35 +144,34 @@ 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;
}
}
// virtual
void LLImageBase::deleteData()
{
- delete[] mData;
- mData = NULL;
+ FREE_MEM(sPrivatePoolp, mData) ;
+ disclaimMem(mDataSize);
mDataSize = 0;
+ mData = NULL;
}
// virtual
U8* LLImageBase::allocateData(S32 size)
{
- LLMemType mt1(mMemType);
-
if (size < 0)
{
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;
}
}
@@ -153,29 +179,30 @@ 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)
{
deleteData(); // virtual
mBadBufferAllocation = false ;
- mData = new U8[size];
+ mData = (U8*)ALLOCATE_MEM(sPrivatePoolp, size);
if (!mData)
{
- llwarns << "allocate image data: " << 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;
@@ -184,21 +211,22 @@ U8* LLImageBase::allocateData(S32 size)
// virtual
U8* LLImageBase::reallocateData(S32 size)
{
- LLMemType mt1(mMemType);
- U8 *new_datap = new U8[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)
{
S32 bytes = llmin(mDataSize, size);
memcpy(new_datap, mData, bytes); /* Flawfinder: ignore */
- delete[] mData;
+ FREE_MEM(sPrivatePoolp, mData) ;
}
mData = new_datap;
+ disclaimMem(mDataSize);
mDataSize = size;
+ claimMem(mDataSize);
return mData;
}
@@ -206,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;
@@ -216,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;
@@ -250,24 +278,25 @@ S32 LLImageRaw::sRawImageCount = 0;
LLImageRaw::LLImageRaw()
: LLImageBase()
{
- mMemType = LLMemType::MTYPE_IMAGERAW;
++sRawImageCount;
}
LLImageRaw::LLImageRaw(U16 width, U16 height, S8 components)
: LLImageBase()
{
- mMemType = LLMemType::MTYPE_IMAGERAW;
//llassert( S32(width) * S32(height) * S32(components) <= MAX_IMAGE_DATA_SIZE );
allocateDataSize(width, height, components);
++sRawImageCount;
}
-LLImageRaw::LLImageRaw(U8 *data, U16 width, U16 height, S8 components)
+LLImageRaw::LLImageRaw(U8 *data, U16 width, U16 height, S8 components, bool no_copy)
: LLImageBase()
{
- mMemType = LLMemType::MTYPE_IMAGERAW;
- if(allocateDataSize(width, height, components))
+ if(no_copy)
+ {
+ setDataAndSize(data, width, height, components);
+ }
+ else if(allocateDataSize(width, height, components))
{
memcpy(getData(), data, width*height*components);
}
@@ -341,27 +370,6 @@ BOOL LLImageRaw::resize(U16 width, U16 height, S8 components)
return TRUE;
}
-U8 * LLImageRaw::getSubImage(U32 x_pos, U32 y_pos, U32 width, U32 height) const
-{
- LLMemType mt1(mMemType);
- U8 *data = new U8[width*height*getComponents()];
-
- // Should do some simple bounds checking
- if (!data)
- {
- llerrs << "Out of memory in LLImageRaw::getSubImage" << llendl;
- return NULL;
- }
-
- U32 i;
- for (i = y_pos; i < y_pos+height; i++)
- {
- memcpy(data + i*width*getComponents(), /* Flawfinder: ignore */
- getData() + ((y_pos + i)*getWidth() + x_pos)*getComponents(), getComponents()*width);
- }
- return data;
-}
-
BOOL LLImageRaw::setSubImage(U32 x_pos, U32 y_pos, U32 width, U32 height,
const U8 *data, U32 stride, BOOL reverse_y)
{
@@ -426,7 +434,6 @@ void LLImageRaw::clear(U8 r, U8 g, U8 b, U8 a)
// Reverses the order of the rows in the image
void LLImageRaw::verticalFlip()
{
- LLMemType mt1(mMemType);
S32 row_bytes = getWidth() * getComponents();
llassert(row_bytes > 0);
std::vector<U8> line_buffer(row_bytes);
@@ -445,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 );
}
@@ -464,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 )
{
@@ -559,8 +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)
{
- LLMemType mt1(mMemType);
- llinfos << "compositeScaled4onto3" << llendl;
+ LL_INFOS() << "compositeScaled4onto3" << LL_ENDL;
LLImageRaw* dst = this; // Just for clarity.
@@ -637,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 )
{
@@ -663,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;
}
@@ -801,7 +835,6 @@ void LLImageRaw::copyUnscaled3onto4( LLImageRaw* src )
// Src and dst can be any size. Src and dst have same number of components.
void LLImageRaw::copyScaled( LLImageRaw* src )
{
- LLMemType mt1(mMemType);
LLImageRaw* dst = this; // Just for clarity.
llassert_always( (1 == src->getComponents()) || (3 == src->getComponents()) || (4 == src->getComponents()) );
@@ -830,55 +863,9 @@ void LLImageRaw::copyScaled( LLImageRaw* src )
}
}
-//scale down image by not blending a pixel with its neighbors.
-BOOL LLImageRaw::scaleDownWithoutBlending( S32 new_width, S32 new_height)
-{
- LLMemType mt1(mMemType);
-
- S8 c = getComponents() ;
- llassert((1 == c) || (3 == c) || (4 == c) );
-
- S32 old_width = getWidth();
- S32 old_height = getHeight();
-
- S32 new_data_size = old_width * new_height * c ;
- llassert_always(new_data_size > 0);
-
- F32 ratio_x = (F32)old_width / new_width ;
- F32 ratio_y = (F32)old_height / new_height ;
- if( ratio_x < 1.0f || ratio_y < 1.0f )
- {
- return TRUE; // Nothing to do.
- }
- ratio_x -= 1.0f ;
- ratio_y -= 1.0f ;
-
- U8* new_data = new U8[new_data_size] ;
- llassert_always(new_data != NULL) ;
-
- U8* old_data = getData() ;
- S32 i, j, k, s, t;
- for(i = 0, s = 0, t = 0 ; i < new_height ; i++)
- {
- for(j = 0 ; j < new_width ; j++)
- {
- for(k = 0 ; k < c ; k++)
- {
- new_data[s++] = old_data[t++] ;
- }
- t += (S32)(ratio_x * c + 0.1f) ;
- }
- t += (S32)(ratio_y * old_width * c + 0.1f) ;
- }
-
- setDataAndSize(new_data, new_width, new_height, c) ;
-
- return TRUE ;
-}
BOOL LLImageRaw::scale( S32 new_width, S32 new_height, BOOL scale_image_data )
{
- LLMemType mt1(mMemType);
llassert((1 == getComponents()) || (3 == getComponents()) || (4 == getComponents()) );
S32 old_width = getWidth();
@@ -1049,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));
}
}
}
@@ -1130,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 )
@@ -1185,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();
@@ -1232,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;
}
@@ -1250,32 +1237,11 @@ 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;
}
- LLPointer<LLImageFormatted> image;
- switch(codec)
- {
- //case IMG_CODEC_RGB:
- case IMG_CODEC_BMP:
- image = new LLImageBMP();
- break;
- case IMG_CODEC_TGA:
- image = new LLImageTGA();
- break;
- case IMG_CODEC_JPEG:
- image = new LLImageJPEG();
- break;
- case IMG_CODEC_J2C:
- image = new LLImageJ2C();
- break;
- case IMG_CODEC_DXT:
- image = new LLImageDXT();
- break;
- default:
- return false;
- }
+ LLPointer<LLImageFormatted> image = LLImageFormatted::createFromType(codec);
llassert(image.notNull());
U8 *buffer = image->allocateData(length);
@@ -1307,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;
}
@@ -1326,9 +1292,9 @@ LLImageFormatted::LLImageFormatted(S8 codec)
mCodec(codec),
mDecoding(0),
mDecoded(0),
- mDiscardLevel(-1)
+ mDiscardLevel(-1),
+ mLevels(0)
{
- mMemType = LLMemType::MTYPE_IMAGEFORMATTED;
}
// virtual
@@ -1412,11 +1378,11 @@ void LLImageFormatted::dump()
{
LLImageBase::dump();
- llinfos << "LLImageFormatted"
+ LL_INFOS() << "LLImageFormatted"
<< " mDecoding " << mDecoding
<< " mCodec " << S32(mCodec)
<< " mDecoded " << mDecoded
- << llendl;
+ << LL_ENDL;
}
//----------------------------------------------------------------------------
@@ -1499,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;
}
}
@@ -1527,6 +1493,7 @@ void LLImageFormatted::setData(U8 *data, S32 size)
{
deleteData();
setDataAndSize(data, size); // Access private LLImageBase members
+
sGlobalFormattedMemory += getDataSize();
}
}
@@ -1545,14 +1512,14 @@ void LLImageFormatted::appendData(U8 *data, S32 size)
S32 newsize = cursize + size;
reallocateData(newsize);
memcpy(getData() + cursize, data, size);
- delete[] data;
+ FREE_MEM(LLImageBase::getPrivatePool(), data);
}
}
}
//----------------------------------------------------------------------------
-BOOL LLImageFormatted::load(const std::string &filename)
+BOOL LLImageFormatted::load(const std::string &filename, int load_size)
{
resetLastError();
@@ -1571,14 +1538,19 @@ BOOL LLImageFormatted::load(const std::string &filename)
return FALSE;
}
+ // Constrain the load size to acceptable values
+ if ((load_size == 0) || (load_size > file_size))
+ {
+ load_size = file_size;
+ }
BOOL res;
- U8 *data = allocateData(file_size);
- apr_size_t bytes_read = file_size;
+ U8 *data = allocateData(load_size);
+ apr_size_t bytes_read = load_size;
apr_status_t s = apr_file_read(apr_file, data, &bytes_read); // modifies bytes_read
- if (s != APR_SUCCESS || (S32) bytes_read != file_size)
+ if (s != APR_SUCCESS || (S32) bytes_read != load_size)
{
deleteData();
- setLastError("Unable to read entire file",filename);
+ setLastError("Unable to read file",filename);
res = FALSE;
}
else
@@ -1641,6 +1613,15 @@ static void avg4_colors2(const U8* a, const U8* b, const U8* c, const U8* d, U8*
dst[1] = (U8)(((U32)(a[1]) + b[1] + c[1] + d[1])>>2);
}
+void LLImageBase::setDataAndSize(U8 *data, S32 size)
+{
+ ll_assert_aligned(data, 16);
+ mData = data;
+ disclaimMem(mDataSize);
+ mDataSize = size;
+ claimMem(mDataSize);
+}
+
//static
void LLImageBase::generateMip(const U8* indata, U8* mipdata, S32 width, S32 height, S32 nchannels)
{
@@ -1666,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;
@@ -1723,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 825b9aab1a..cd3f76f1fd 100644..100755
--- a/indra/llimage/llimage.h
+++ b/indra/llimage/llimage.h
@@ -29,14 +29,28 @@
#include "lluuid.h"
#include "llstring.h"
-//#include "llmemory.h"
-#include "llthread.h"
-#include "llmemtype.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
+
+// *TODO : Use MAX_IMAGE_MIP as max discard level and modify j2c management so that the number
+// of levels is read from the header's file, not inferred from its size.
const S32 MAX_DISCARD_LEVEL = 5;
+// JPEG2000 size constraints
+// Those are declared here as they are germane to other image constraints used in the viewer
+// and declared right here. Some come from the JPEG2000 spec, some conventions specific to SL.
+const S32 MAX_DECOMPOSITION_LEVELS = 32; // Number of decomposition levels cannot exceed 32 according to jpeg2000 spec
+const S32 MIN_DECOMPOSITION_LEVELS = 5; // the SL viewer will *crash* trying to decode images with fewer than 5 decomposition levels (unless image is small that is)
+const S32 MAX_PRECINCT_SIZE = 2048; // No reason to be bigger than MAX_IMAGE_SIZE
+const S32 MIN_PRECINCT_SIZE = 4; // Can't be smaller than MIN_BLOCK_SIZE
+const S32 MAX_BLOCK_SIZE = 64; // Max total block size is 4096, hence 64x64 when using square blocks
+const S32 MIN_BLOCK_SIZE = 4; // Min block dim is 4 according to jpeg2000 spec
+const S32 MIN_LAYER_SIZE = 2000; // Size of the first quality layer (after header). Must be > to FIRST_PACKET_SIZE!!
+const S32 MAX_NB_LAYERS = 64; // Max number of layers we'll entertain in SL (practical limit)
+
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;
@@ -48,6 +62,7 @@ const S32 MAX_IMAGE_DATA_SIZE = MAX_IMAGE_AREA * MAX_IMAGE_COMPONENTS; //2048 *
// *TODO: change both to 1024 when SIM texture fetching is deprecated
const S32 FIRST_PACKET_SIZE = 600;
const S32 MAX_IMG_PACKET_SIZE = 1000;
+const S32 HTTP_PACKET_SIZE = 1496;
// Base classes for images.
// There are two major parts for the image:
@@ -56,6 +71,7 @@ const S32 MAX_IMG_PACKET_SIZE = 1000;
class LLImageFormatted;
class LLImageRaw;
class LLColor4U;
+class LLPrivateMemoryPool;
typedef enum e_image_codec
{
@@ -76,21 +92,28 @@ typedef enum e_image_codec
class LLImage
{
public:
- static void initClass();
+ static void initClass(bool use_new_byte_range = false, S32 minimal_reverse_byte_range_percent = 75);
static void cleanupClass();
static const std::string& getLastError();
static void setLastError(const std::string& message);
+ static bool useNewByteRange() { return sUseNewByteRange; }
+ static S32 getReverseByteRangePercent() { return sMinimalReverseByteRangePercent; }
+
protected:
static LLMutex* sMutex;
static std::string sLastErrorMessage;
+ static bool sUseNewByteRange;
+ static S32 sMinimalReverseByteRangePercent;
};
//============================================================================
// Image base class
-class LLImageBase : public LLThreadSafeRefCount
+class LLImageBase
+: public LLThreadSafeRefCount,
+ public LLTrace::MemTrackable<LLImageBase>
{
protected:
virtual ~LLImageBase();
@@ -127,7 +150,7 @@ public:
protected:
// special accessor to allow direct setting of mData and mDataSize by LLImageFormatted
- void setDataAndSize(U8 *data, S32 size) { mData = data; mDataSize = size; }
+ void setDataAndSize(U8 *data, S32 size);
public:
static void generateMip(const U8 *indata, U8* mipdata, int width, int height, S32 nchannels);
@@ -138,6 +161,12 @@ public:
static EImageCodec getCodecFromExtension(const std::string& exten);
+ static void createPrivatePool() ;
+ static void destroyPrivatePool() ;
+ static LLPrivateMemoryPool* getPrivatePool() {return sPrivatePoolp;}
+
+ //static LLTrace::MemStatHandle sMemStat;
+
private:
U8 *mData;
S32 mDataSize;
@@ -149,8 +178,8 @@ private:
bool mBadBufferAllocation ;
bool mAllowOverSize ;
-public:
- LLMemType::DeclareMemType& mMemType; // debug
+
+ static LLPrivateMemoryPool* sPrivatePoolp ;
};
// Raw representation of an image (used for textures, and other uncompressed formats
@@ -162,7 +191,7 @@ protected:
public:
LLImageRaw();
LLImageRaw(U16 width, U16 height, S8 components);
- LLImageRaw(U8 *data, U16 width, U16 height, S8 components);
+ LLImageRaw(U8 *data, U16 width, U16 height, S8 components, bool no_copy = false);
// Construct using createFromFile (used by tools)
//LLImageRaw(const std::string& filename, bool j2c_lowest_mip_only = false);
@@ -172,7 +201,7 @@ public:
BOOL resize(U16 width, U16 height, S8 components);
- U8 * getSubImage(U32 x_pos, U32 y_pos, U32 width, U32 height) const;
+ //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);
@@ -180,17 +209,22 @@ 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);
BOOL scale( S32 new_width, S32 new_height, BOOL scale_image = TRUE );
- BOOL scaleDownWithoutBlending( S32 new_width, S32 new_height) ;
-
+
// Fill the buffer with a constant color
void fill( const LLColor4U& color );
// 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 );
@@ -203,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 );
@@ -266,16 +305,16 @@ public:
// 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
+ // 0 indicates we don't have a header and have to read 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)
+ // getRawDiscardLevel() by default returns mDiscardLevel, but may be overridden (LLImageJ2C)
virtual S8 getRawDiscardLevel() { return mDiscardLevel; }
- BOOL load(const std::string& filename);
+ BOOL load(const std::string& filename, int load_size = 0);
BOOL save(const std::string& filename);
virtual BOOL updateData() = 0; // pure virtual
@@ -294,6 +333,8 @@ public:
BOOL isDecoded() const { return mDecoded ? TRUE : FALSE; }
void setDiscardLevel(S8 discard_level) { mDiscardLevel = discard_level; }
S8 getDiscardLevel() const { return mDiscardLevel; }
+ S8 getLevels() const { return mLevels; }
+ void setLevels(S8 nlevels) { mLevels = nlevels; }
// setLastError needs to be deferred for J2C images since it may be called from a DLL
virtual void resetLastError();
@@ -306,7 +347,8 @@ protected:
S8 mCodec;
S8 mDecoding;
S8 mDecoded; // unused, but changing LLImage layout requires recompiling static Mac/Linux libs. 2009-01-30 JC
- S8 mDiscardLevel;
+ S8 mDiscardLevel; // Current resolution level worked on. 0 = full res, 1 = half res, 2 = quarter res, etc...
+ S8 mLevels; // Number of resolution levels in that image. Min is 1. 0 means unknown.
public:
static S32 sGlobalFormattedMemory;
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 835664c60f..5bf3f29b3c 100644..100755
--- a/indra/llimage/llimagedimensionsinfo.cpp
+++ b/indra/llimage/llimagedimensionsinfo.cpp
@@ -73,9 +73,28 @@ bool LLImageDimensionsInfo::load(const std::string& src_filename,U32 codec)
bool LLImageDimensionsInfo::getImageDimensionsBmp()
{
- const S32 BMP_FILE_HEADER_SIZE = 14;
+ // Make sure the file is long enough.
+ const S32 DATA_LEN = 26; // BMP header (14) + DIB header size (4) + width (4) + height (4)
+ if (!checkFileLength(DATA_LEN))
+ {
+ LL_WARNS() << "Premature end of file" << LL_ENDL;
+ return false;
+ }
+
+ // Read BMP signature.
+ U8 signature[2];
+ mInfile.read((void*)signature, sizeof(signature)/sizeof(signature[0]));
+
+ // Make sure this is actually a BMP file.
+ // We only support Windows bitmaps (BM), according to LLImageBMP::updateData().
+ if (signature[0] != 'B' || signature[1] != 'M')
+ {
+ LL_WARNS() << "Not a BMP" << LL_ENDL;
+ return false;
+ }
- mInfile.seek(APR_CUR,BMP_FILE_HEADER_SIZE+4);
+ // Read image dimensions.
+ mInfile.seek(APR_CUR, 16);
mWidth = read_reverse_s32();
mHeight = read_reverse_s32();
@@ -86,6 +105,14 @@ bool LLImageDimensionsInfo::getImageDimensionsTga()
{
const S32 TGA_FILE_HEADER_SIZE = 12;
+ // Make sure the file is long enough.
+ if (!checkFileLength(TGA_FILE_HEADER_SIZE + 1 /* width */ + 1 /* height */))
+ {
+ LL_WARNS() << "Premature end of file" << LL_ENDL;
+ return false;
+ }
+
+ // *TODO: Detect non-TGA files somehow.
mInfile.seek(APR_CUR,TGA_FILE_HEADER_SIZE);
mWidth = read_byte() | read_byte() << 8;
mHeight = read_byte() | read_byte() << 8;
@@ -95,9 +122,29 @@ bool LLImageDimensionsInfo::getImageDimensionsTga()
bool LLImageDimensionsInfo::getImageDimensionsPng()
{
- const S32 PNG_FILE_MARKER_SIZE = 8;
+ const S32 PNG_MAGIC_SIZE = 8;
+
+ // Make sure the file is long enough.
+ if (!checkFileLength(PNG_MAGIC_SIZE + 8 + sizeof(S32) * 2 /* width, height */))
+ {
+ LL_WARNS() << "Premature end of file" << LL_ENDL;
+ return false;
+ }
- mInfile.seek(APR_CUR,PNG_FILE_MARKER_SIZE + 8/*header offset+chunk length+chunk type*/);
+ // Read PNG signature.
+ const U8 png_magic[PNG_MAGIC_SIZE] = {0x89, 0x50, 0x4E, 0x47, 0x0D, 0x0A, 0x1A, 0x0A};
+ U8 signature[PNG_MAGIC_SIZE];
+ mInfile.read((void*)signature, PNG_MAGIC_SIZE);
+
+ // Make sure it's a PNG file.
+ if (memcmp(signature, png_magic, PNG_MAGIC_SIZE) != 0)
+ {
+ LL_WARNS() << "Not a PNG" << LL_ENDL;
+ return false;
+ }
+
+ // Read image dimensions.
+ mInfile.seek(APR_CUR, 8 /* chunk length + chunk type */);
mWidth = read_s32();
mHeight = read_s32();
@@ -109,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()
@@ -122,6 +169,24 @@ bool LLImageDimensionsInfo::getImageDimensionsJpeg()
setLastError("Unable to open file for reading", mSrcFilename);
return false;
}
+
+ /* Make sure this is a JPEG file. */
+ const size_t JPEG_MAGIC_SIZE = 2;
+ const U8 jpeg_magic[JPEG_MAGIC_SIZE] = {0xFF, 0xD8};
+ U8 signature[JPEG_MAGIC_SIZE];
+
+ if (fread(signature, sizeof(signature), 1, fp) != 1)
+ {
+ LL_WARNS() << "Premature end of file" << LL_ENDL;
+ return false;
+ }
+ if (memcmp(signature, jpeg_magic, JPEG_MAGIC_SIZE) != 0)
+ {
+ LL_WARNS() << "Not a JPEG" << LL_ENDL;
+ return false;
+ }
+ fseek(fp, 0, SEEK_SET); // go back to start of the file
+
/* Init jpeg */
jpeg_error_mgr jerr;
jpeg_decompress_struct cinfo;
@@ -145,3 +210,13 @@ bool LLImageDimensionsInfo::getImageDimensionsJpeg()
return !sJpegErrorEncountered;
}
+bool LLImageDimensionsInfo::checkFileLength(S32 min_len)
+{
+ // Make sure the file is not shorter than min_len bytes.
+ // so that we don't have to check value returned by each read() or seek().
+ char* buf = new char[min_len];
+ int nread = mInfile.read(buf, min_len);
+ delete[] buf;
+ mInfile.seek(APR_SET, 0);
+ return nread == min_len;
+}
diff --git a/indra/llimage/llimagedimensionsinfo.h b/indra/llimage/llimagedimensionsinfo.h
index 5384faf3f4..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
@@ -119,6 +121,9 @@ protected:
return read_byte() << 8 | read_byte();
}
+ /// Check if the file is not shorter than min_len bytes.
+ bool checkFileLength(S32 min_len);
+
protected:
LLAPRFile mInfile ;
std::string mSrcFilename;
diff --git a/indra/llimage/llimagedxt.cpp b/indra/llimage/llimagedxt.cpp
index 4bd3efddaa..04e0e752eb 100644..100755
--- a/indra/llimage/llimagedxt.cpp
+++ b/indra/llimage/llimagedxt.cpp
@@ -26,6 +26,7 @@
#include "linden_common.h"
#include "llimagedxt.h"
+#include "llmemory.h"
//static
void LLImageDXT::checkMinWidthHeight(EFileFormat format, S32& width, S32& height)
@@ -51,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;
}
};
@@ -81,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;
}
};
@@ -206,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);
@@ -223,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);
@@ -250,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();
}
@@ -264,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;
}
@@ -302,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;
@@ -330,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;
}
@@ -421,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;
@@ -429,10 +430,10 @@ bool LLImageDXT::convertToDXR()
S32 nmips = calcNumMips(width,height);
S32 total_bytes = getDataSize();
U8* olddata = getData();
- U8* newdata = new U8[total_bytes];
+ 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);
@@ -465,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 cb2a85fa91..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 "llmemtype.h"
#include "lltimer.h"
#include "llmath.h"
+#include "llmemory.h"
+#include "llsd.h"
typedef LLImageJ2CImpl* (*CreateLLImageJ2CFunction)();
typedef void (*DestroyLLImageJ2CFunction)(LLImageJ2CImpl*);
@@ -55,11 +57,12 @@ std::string LLImageJ2C::getEngineInfo()
LLImageJ2C::LLImageJ2C() : LLImageFormatted(IMG_CODEC_J2C),
mMaxBytes(0),
mRawDiscardLevel(-1),
- mRate(0.0f),
+ mRate(DEFAULT_COMPRESSION_RATE),
mReversible(FALSE),
mAreaUsedForDataSizeCalcs(0)
{
mImpl = fallbackCreateLLImageJ2CImpl();
+ claimMem(mImpl);
// Clear data size table
for( S32 i = 0; i <= MAX_DISCARD_LEVEL; i++)
@@ -139,6 +142,16 @@ BOOL LLImageJ2C::updateData()
return res;
}
+BOOL LLImageJ2C::initDecode(LLImageRaw &raw_image, int discard_level, int* region)
+{
+ setDiscardLevel(discard_level != -1 ? discard_level : 0);
+ return mImpl->initDecode(*this,raw_image,discard_level,region);
+}
+
+BOOL LLImageJ2C::initEncode(LLImageRaw &raw_image, int blocks_size, int precincts_size, int levels)
+{
+ return mImpl->initEncode(*this,raw_image,blocks_size,precincts_size,levels);
+}
BOOL LLImageJ2C::decode(LLImageRaw *raw_imagep, F32 decode_time)
{
@@ -150,7 +163,6 @@ BOOL LLImageJ2C::decode(LLImageRaw *raw_imagep, F32 decode_time)
BOOL LLImageJ2C::decodeChannels(LLImageRaw *raw_imagep, F32 decode_time, S32 first_channel, S32 max_channel_count )
{
LLTimer elapsed;
- LLMemType mt1(mMemType);
BOOL res = TRUE;
@@ -216,7 +228,6 @@ BOOL LLImageJ2C::encode(const LLImageRaw *raw_imagep, F32 encode_time)
BOOL LLImageJ2C::encode(const LLImageRaw *raw_imagep, const char* comment_text, F32 encode_time)
{
LLTimer elapsed;
- LLMemType mt1(mMemType);
resetLastError();
BOOL res = mImpl->encodeImpl(*this, *raw_imagep, comment_text, encode_time, mReversible);
if (!mLastError.empty())
@@ -251,16 +262,34 @@ S32 LLImageJ2C::calcHeaderSizeJ2C()
//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)
+ // Note: This provides an estimation for the first to last quality layer of a given discard level
+ // This is however an efficient approximation, as the true discard level boundary would be
+ // in general too big for fast fetching.
+ // For details about the equation used here, see https://wiki.lindenlab.com/wiki/THX1138_KDU_Improvements#Byte_Range_Study
+
+ // Estimate the number of layers. This is consistent with what's done for j2c encoding in LLImageJ2CKDU::encodeImpl().
+ S32 nb_layers = 1;
+ S32 surface = w*h;
+ S32 s = 64*64;
+ while (surface > s)
{
- if (w < 1 || h < 1)
- break;
- w >>= 1;
- h >>= 1;
- discard_level--;
+ nb_layers++;
+ s *= 4;
}
- S32 bytes = (S32)((F32)(w*h*comp)*rate);
+ F32 layer_factor = 3.0f * (7 - llclamp(nb_layers,1,6));
+
+ // Compute w/pow(2,discard_level) and h/pow(2,discard_level)
+ w >>= discard_level;
+ h >>= discard_level;
+ w = llmax(w, 1);
+ h = llmax(h, 1);
+
+ // Temporary: compute both new and old range and pick one according to the settings TextureNewByteRange
+ // *TODO: Take the old code out once we have enough tests done
+ S32 bytes;
+ S32 new_bytes = (S32) (sqrt((F32)(w*h))*(F32)(comp)*rate*1000.f/layer_factor);
+ S32 old_bytes = (S32)((F32)(w*h*comp)*rate);
+ bytes = (LLImage::useNewByteRange() && (new_bytes < old_bytes) ? new_bytes : old_bytes);
bytes = llmax(bytes, calcHeaderSizeJ2C());
return bytes;
}
@@ -270,15 +299,12 @@ S32 LLImageJ2C::calcHeaderSize()
return calcHeaderSizeJ2C();
}
-
-// calcDataSize() returns how many bytes to read
-// to load discard_level (including header and higher discard levels)
+// calcDataSize() returns how many bytes to read to load discard_level (including header)
S32 LLImageJ2C::calcDataSize(S32 discard_level)
{
discard_level = llclamp(discard_level, 0, MAX_DISCARD_LEVEL);
-
if ( mAreaUsedForDataSizeCalcs != (getHeight() * getWidth())
- || mDataSizes[0] == 0)
+ || (mDataSizes[0] == 0))
{
mAreaUsedForDataSizeCalcs = getHeight() * getWidth();
@@ -288,25 +314,6 @@ S32 LLImageJ2C::calcDataSize(S32 discard_level)
mDataSizes[level] = calcDataSizeJ2C(getWidth(), getHeight(), getComponents(), level, mRate);
level--;
}
-
- /* This is technically a more correct way to calculate the size required
- for each discard level, since they should include the size needed for
- lower levels. Unfortunately, this doesn't work well and will lead to
- download stalls. The true correct way is to parse the header. This will
- all go away with http textures at some point.
-
- // Calculate the size for each discard level. Lower levels (higher quality)
- // contain the cumulative size of higher levels
- S32 total_size = calcHeaderSizeJ2C();
-
- S32 level = MAX_DISCARD_LEVEL; // Start at the highest discard
- while ( level >= 0 )
- { // Add in this discard level and all before it
- total_size += calcDataSizeJ2C(getWidth(), getHeight(), getComponents(), level, mRate);
- mDataSizes[level] = total_size;
- level--;
- }
- */
}
return mDataSizes[discard_level];
}
@@ -321,8 +328,9 @@ S32 LLImageJ2C::calcDiscardLevelBytes(S32 bytes)
}
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
+ S32 bytes_needed = calcDataSize(discard_level);
+ // Use TextureReverseByteRange percent (see settings.xml) of the optimal size to qualify as correct rendering for the given discard level
+ if (bytes >= (bytes_needed*LLImage::getReverseByteRangePercent()/100))
{
break;
}
@@ -335,11 +343,6 @@ S32 LLImageJ2C::calcDiscardLevelBytes(S32 bytes)
return discard_level;
}
-void LLImageJ2C::setRate(F32 rate)
-{
- mRate = rate;
-}
-
void LLImageJ2C::setMaxBytes(S32 max_bytes)
{
mMaxBytes = max_bytes;
@@ -373,14 +376,14 @@ BOOL LLImageJ2C::loadAndValidate(const std::string &filename)
}
else
{
- U8 *data = new U8[file_size];
+ U8 *data = (U8*)ALLOCATE_MEM(LLImageBase::getPrivatePool(), 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;
+ FREE_MEM(LLImageBase::getPrivatePool(), data);
setLastError("Unable to read entire file");
res = FALSE;
}
@@ -401,7 +404,6 @@ BOOL LLImageJ2C::loadAndValidate(const std::string &filename)
BOOL LLImageJ2C::validate(U8 *data, U32 file_size)
{
- LLMemType mt1(mMemType);
resetLastError();
@@ -461,6 +463,7 @@ LLImageCompressionTester::LLImageCompressionTester() : LLMetricPerformanceTester
addMetric("Perf Compression (kB/s)");
mRunBytesInDecompression = 0;
+ mRunBytesOutDecompression = 0;
mRunBytesInCompression = 0;
mTotalBytesInDecompression = 0;
@@ -470,10 +473,12 @@ LLImageCompressionTester::LLImageCompressionTester() : LLMetricPerformanceTester
mTotalTimeDecompression = 0.0f;
mTotalTimeCompression = 0.0f;
+ mRunTimeDecompression = 0.0f;
}
LLImageCompressionTester::~LLImageCompressionTester()
{
+ outputTestResults();
LLImageJ2C::sTesterp = NULL;
}
@@ -487,10 +492,10 @@ void LLImageCompressionTester::outputTestRecord(LLSD *sd)
F32 decompressionRate = 0.0f;
F32 compressionRate = 0.0f;
- F32 totalkBInDecompression = (F32)(mTotalBytesInDecompression) / 1000.0;
- F32 totalkBOutDecompression = (F32)(mTotalBytesOutDecompression) / 1000.0;
- F32 totalkBInCompression = (F32)(mTotalBytesInCompression) / 1000.0;
- F32 totalkBOutCompression = (F32)(mTotalBytesOutCompression) / 1000.0;
+ F32 totalkBInDecompression = (F32)(mTotalBytesInDecompression) / 1000.f;
+ F32 totalkBOutDecompression = (F32)(mTotalBytesOutDecompression) / 1000.f;
+ F32 totalkBInCompression = (F32)(mTotalBytesInCompression) / 1000.f;
+ F32 totalkBOutCompression = (F32)(mTotalBytesOutCompression) / 1000.f;
if (!is_approx_zero(mTotalTimeDecompression))
{
@@ -551,12 +556,17 @@ void LLImageCompressionTester::updateDecompressionStats(const S32 bytesIn, const
mTotalBytesInDecompression += bytesIn;
mRunBytesInDecompression += bytesIn;
mTotalBytesOutDecompression += bytesOut;
- if (mRunBytesInDecompression > (1000000))
+ mRunBytesOutDecompression += bytesOut;
+ //if (mRunBytesInDecompression > (1000000))
+ if (mRunBytesOutDecompression > (10000000))
+ //if ((mTotalTimeDecompression - mRunTimeDecompression) >= (5.0f))
{
// Output everything
outputTestResults();
// Reset the decompression data of the run
mRunBytesInDecompression = 0;
+ mRunBytesOutDecompression = 0;
+ mRunTimeDecompression = mTotalTimeDecompression;
}
}
diff --git a/indra/llimage/llimagej2c.h b/indra/llimage/llimagej2c.h
index dd5bec8b2e..ce8195940d 100644..100755
--- a/indra/llimage/llimagej2c.h
+++ b/indra/llimage/llimagej2c.h
@@ -31,6 +31,9 @@
#include "llassettype.h"
#include "llmetricperformancetester.h"
+// JPEG2000 : compression rate used in j2c conversion.
+const F32 DEFAULT_COMPRESSION_RATE = 1.f/8.f;
+
class LLImageJ2CImpl;
class LLImageCompressionTester ;
@@ -56,6 +59,8 @@ public:
/*virtual*/ void resetLastError();
/*virtual*/ void setLastError(const std::string& message, const std::string& filename = std::string());
+ BOOL initDecode(LLImageRaw &raw_image, int discard_level, int* region);
+ BOOL initEncode(LLImageRaw &raw_image, int blocks_size, int precincts_size, int levels);
// Encode with comment text
BOOL encode(const LLImageRaw *raw_imagep, const char* comment_text, F32 encode_time=0.0);
@@ -65,12 +70,11 @@ public:
// 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 S32 calcDataSizeJ2C(S32 w, S32 h, S32 comp, S32 discard_level, F32 rate = DEFAULT_COMPRESSION_RATE);
static std::string getEngineInfo();
@@ -117,6 +121,8 @@ protected:
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;
+ virtual BOOL initDecode(LLImageJ2C &base, LLImageRaw &raw_image, int discard_level = -1, int* region = NULL) = 0;
+ virtual BOOL initEncode(LLImageJ2C &base, LLImageRaw &raw_image, int blocks_size = -1, int precincts_size = -1, int levels = 0) = 0;
friend class LLImageJ2C;
};
@@ -150,13 +156,15 @@ class LLImageCompressionTester : public LLMetricPerformanceTesterBasic
U32 mTotalBytesOutDecompression; // Total bytes produced by decompressor
U32 mTotalBytesInCompression; // Total bytes fed to compressor
U32 mTotalBytesOutCompression; // Total bytes produced by compressor
- U32 mRunBytesInDecompression; // Bytes fed to decompressor in this run
+ U32 mRunBytesInDecompression; // Bytes fed to decompressor in this run
+ U32 mRunBytesOutDecompression; // Bytes produced by the decompressor in this run
U32 mRunBytesInCompression; // Bytes fed to compressor in this run
//
// Time
//
F32 mTotalTimeDecompression; // Total time spent in computing decompression
F32 mTotalTimeCompression; // Total time spent in computing compression
+ F32 mRunTimeDecompression; // Time in this run (we output every 5 sec in decompress)
};
#endif
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 8d493ecde0..7735dc1379 100644..100755
--- a/indra/llimage/llimagepng.cpp
+++ b/indra/llimage/llimagepng.cpp
@@ -60,8 +60,14 @@ BOOL LLImagePNG::updateData()
// Decode the PNG data and extract sizing information
LLPngWrapper pngWrapper;
+ if (!pngWrapper.isValidPng(getData()))
+ {
+ setLastError("LLImagePNG data does not have a valid PNG header!");
+ return FALSE;
+ }
+
LLPngWrapper::ImageInfo infop;
- if (! pngWrapper.readPng(getData(), NULL, &infop))
+ if (! pngWrapper.readPng(getData(), getDataSize(), NULL, &infop))
{
setLastError(pngWrapper.getErrorMessage());
return FALSE;
@@ -90,7 +96,13 @@ BOOL LLImagePNG::decode(LLImageRaw* raw_image, F32 decode_time)
// Decode the PNG data into the raw image
LLPngWrapper pngWrapper;
- if (! pngWrapper.readPng(getData(), raw_image))
+ if (!pngWrapper.isValidPng(getData()))
+ {
+ setLastError("LLImagePNG data does not have a valid PNG header!");
+ return FALSE;
+ }
+
+ 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 d1c74b6fa1..4875fe7001 100644..100755
--- a/indra/llimage/llimageworker.cpp
+++ b/indra/llimage/llimageworker.cpp
@@ -38,9 +38,15 @@ LLImageDecodeThread::LLImageDecodeThread(bool threaded)
mCreationMutex = new LLMutex(getAPRPool());
}
+//virtual
+LLImageDecodeThread::~LLImageDecodeThread()
+{
+ delete mCreationMutex ;
+}
+
// MAIN THREAD
// virtual
-S32 LLImageDecodeThread::update(U32 max_time_ms)
+S32 LLImageDecodeThread::update(F32 max_time_ms)
{
LLMutexLock lock(mCreationMutex);
for (creation_list_t::iterator iter = mCreationList.begin();
@@ -54,7 +60,7 @@ S32 LLImageDecodeThread::update(U32 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();
@@ -137,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())
{
@@ -149,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 c3c92ec832..1bfb0ddfd3 100644..100755
--- a/indra/llimage/llimageworker.h
+++ b/indra/llimage/llimageworker.h
@@ -73,10 +73,12 @@ public:
public:
LLImageDecodeThread(bool threaded = true);
+ virtual ~LLImageDecodeThread();
+
handle_t decodeImage(LLImageFormatted* image,
U32 priority, S32 discard, BOOL needs_aux,
Responder* responder);
- S32 update(U32 max_time_ms);
+ S32 update(F32 max_time_ms);
// Used by unit tests to check the consistency of the thread instance
S32 tut_size();
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 08476fb72c..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,14 +45,14 @@
// * 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),
mComponents(0),
mBadBufferAllocation(false),
-mAllowOverSize(false),
-mMemType(LLMemType::MTYPE_IMAGEBASE)
+mAllowOverSize(false)
{
}
LLImageBase::~LLImageBase() {}
@@ -65,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
// -------------------------------------------------------------------------------------------
@@ -111,7 +115,6 @@ namespace tut
{
// Instance to be tested
LLImageDecodeThread* mThread;
-
// Constructor and destructor of the test wrapper
imagedecodethread_test()
{
@@ -137,6 +140,7 @@ namespace tut
imagerequest_test()
{
done = false;
+
mRequest = new LLImageDecodeThread::ImageRequest(0, 0,
LLQueuedThread::PRIORITY_NORMAL, 0, FALSE,
new responder_test(&done));