From 74c8b028d42a8c5b080bb861e427f38cedd4ad7c Mon Sep 17 00:00:00 2001
From: Alexander Gavriliuk <alexandrgproductengine@lindenlab.com>
Date: Fri, 15 Dec 2023 18:26:14 +0100
Subject: SL-20743 Use LLMutex in LLImageBase for internal data thread-safety

---
 indra/llimage/llimage.cpp       | 131 ++++++++++++++++++++++++++++++++--------
 indra/llimage/llimage.h         |  73 +++++++++++++---------
 indra/llimage/llimagebmp.cpp    |  20 ++++--
 indra/llimage/llimagebmp.h      |   8 +--
 indra/llimage/llimagedxt.cpp    |  15 ++++-
 indra/llimage/llimagefilter.cpp |   4 +-
 indra/llimage/llimagej2c.cpp    |  35 ++++++-----
 indra/llimage/llimagejpeg.cpp   |  12 +++-
 indra/llimage/llimagejpeg.h     |   2 -
 indra/llimage/llimagepng.cpp    |   8 +++
 indra/llimage/llimagetga.cpp    |  15 ++++-
 indra/llimage/llimageworker.cpp |  13 +++-
 indra/llimage/llpngwrapper.cpp  |   2 +
 13 files changed, 251 insertions(+), 87 deletions(-)

(limited to 'indra/llimage')

diff --git a/indra/llimage/llimage.cpp b/indra/llimage/llimage.cpp
index 7ac80825b5..520d7b4fd9 100644
--- a/indra/llimage/llimage.cpp
+++ b/indra/llimage/llimage.cpp
@@ -614,7 +614,6 @@ const std::string& LLImage::getLastError()
 //static
 void LLImage::setLastError(const std::string& message)
 {
-	LLMutexLock m(sMutex);
 	sLastErrorMessage = message;
 }
 
@@ -754,7 +753,7 @@ U8* LLImageBase::reallocateData(S32 size)
 	return mData;
 }
 
-const U8* LLImageBase::getData() const	
+const U8* LLImageBase::getData() const
 { 
 	if(mBadBufferAllocation)
 	{
@@ -765,7 +764,7 @@ const U8* LLImageBase::getData() const
 	return mData; 
 } // read only
 
-U8* LLImageBase::getData()				
+U8* LLImageBase::getData()
 { 
 	if(mBadBufferAllocation)
 	{
@@ -778,7 +777,7 @@ U8* LLImageBase::getData()
 
 bool LLImageBase::isBufferInvalid() const
 {
-	return mBadBufferAllocation || mData == NULL ;
+	return mBadBufferAllocation || mData == NULL;
 }
 
 void LLImageBase::setSize(S32 width, S32 height, S32 ncomponents)
@@ -854,6 +853,8 @@ LLImageRaw::~LLImageRaw()
 // virtual
 U8* LLImageRaw::allocateData(S32 size)
 {
+	LLImageDataLock lock(this);
+
 	U8* res = LLImageBase::allocateData(size);
 	return res;
 }
@@ -861,12 +862,16 @@ U8* LLImageRaw::allocateData(S32 size)
 // virtual
 U8* LLImageRaw::reallocateData(S32 size)
 {
+	LLImageDataLock lock(this);
+
 	U8* res = LLImageBase::reallocateData(size);
 	return res;
 }
 
 void LLImageRaw::releaseData()
 {
+    LLImageDataLock lock(this);
+
     LLImageBase::setSize(0, 0, 0);
     LLImageBase::setDataAndSize(nullptr, 0);
 }
@@ -874,11 +879,15 @@ void LLImageRaw::releaseData()
 // virtual
 void LLImageRaw::deleteData()
 {
+	LLImageDataLock lock(this);
+
 	LLImageBase::deleteData();
 }
 
 void LLImageRaw::setDataAndSize(U8 *data, S32 width, S32 height, S8 components) 
-{ 
+{
+	LLImageDataLock lock(this);
+
 	if(data == getData())
 	{
 		return ;
@@ -892,6 +901,8 @@ void LLImageRaw::setDataAndSize(U8 *data, S32 width, S32 height, S8 components)
 
 bool LLImageRaw::resize(U16 width, U16 height, S8 components)
 {
+	LLImageDataLock lock(this);
+
 	if ((getWidth() == width) && (getHeight() == height) && (getComponents() == components) && !isBufferInvalid())
 	{
 		return true;
@@ -907,6 +918,8 @@ bool LLImageRaw::resize(U16 width, U16 height, S8 components)
 bool LLImageRaw::setSubImage(U32 x_pos, U32 y_pos, U32 width, U32 height,
 							 const U8 *data, U32 stride, bool reverse_y)
 {
+	LLImageDataLock lock(this);
+
 	if (!getData())
 	{
 		return false;
@@ -934,6 +947,9 @@ bool LLImageRaw::setSubImage(U32 x_pos, U32 y_pos, U32 width, U32 height,
 void LLImageRaw::clear(U8 r, U8 g, U8 b, U8 a)
 {
 	llassert( getComponents() <= 4 );
+
+	LLImageDataLock lock(this);
+
 	// This is fairly bogus, but it'll do for now.
 	if (isBufferInvalid())
 	{
@@ -974,6 +990,8 @@ void LLImageRaw::clear(U8 r, U8 g, U8 b, U8 a)
 // Reverses the order of the rows in the image
 void LLImageRaw::verticalFlip()
 {
+	LLImageDataLock lock(this);
+
 	S32 row_bytes = getWidth() * getComponents();
 	llassert(row_bytes > 0);
 	std::vector<U8> line_buffer(row_bytes);
@@ -991,6 +1009,8 @@ void LLImageRaw::verticalFlip()
 
 bool LLImageRaw::optimizeAwayAlpha()
 {
+    LLImageDataLock lock(this);
+
     if (getComponents() == 4)
     {
         U8* data = getData();
@@ -1028,6 +1048,8 @@ bool LLImageRaw::optimizeAwayAlpha()
 
 void LLImageRaw::expandToPowerOfTwo(S32 max_dim, bool scale_image)
 {
+	LLImageDataLock lock(this);
+
 	// Find new sizes
 	S32 new_width  = expandDimToPowerOfTwo(getWidth(), max_dim);
 	S32 new_height = expandDimToPowerOfTwo(getHeight(), max_dim);
@@ -1037,6 +1059,8 @@ void LLImageRaw::expandToPowerOfTwo(S32 max_dim, bool scale_image)
 
 void LLImageRaw::contractToPowerOfTwo(S32 max_dim, bool scale_image)
 {
+	LLImageDataLock lock(this);
+
 	// Find new sizes
 	S32 new_width  = contractDimToPowerOfTwo(getWidth(), MIN_IMAGE_SIZE);
 	S32 new_height = contractDimToPowerOfTwo(getHeight(), MIN_IMAGE_SIZE);
@@ -1086,6 +1110,8 @@ S32 LLImageRaw::contractDimToPowerOfTwo(S32 curr_dim, S32 min_dim)
 
 void LLImageRaw::biasedScaleToPowerOfTwo(S32 max_dim)
 {
+	LLImageDataLock lock(this);
+
 	// Find new sizes
 	S32 new_width  = biasedDimToPowerOfTwo(getWidth(),max_dim);
 	S32 new_height = biasedDimToPowerOfTwo(getHeight(),max_dim);
@@ -1093,6 +1119,7 @@ void LLImageRaw::biasedScaleToPowerOfTwo(S32 max_dim)
 	scale( new_width, new_height );
 }
 
+// static
 // Calculates (U8)(255*(a/255.f)*(b/255.f) + 0.5f).  Thanks, Jim Blinn!
 inline U8 LLImageRaw::fastFractionalMult( U8 a, U8 b )
 {
@@ -1101,10 +1128,13 @@ inline U8 LLImageRaw::fastFractionalMult( U8 a, U8 b )
 }
 
 
-void LLImageRaw::composite( LLImageRaw* src )
+void LLImageRaw::composite( const LLImageRaw* src )
 {
 	LLImageRaw* dst = this;  // Just for clarity.
 
+	LLImageDataSharedLock lockIn(src);
+	LLImageDataLock lockOut(this);
+
 	if (!validateSrcAndDst("LLImageRaw::composite", src, dst))
 	{
 		return;
@@ -1143,12 +1173,14 @@ 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)
+void LLImageRaw::compositeScaled4onto3(const LLImageRaw* src)
 {
 	LL_INFOS() << "compositeScaled4onto3" << LL_ENDL;
 
 	LLImageRaw* dst = this;  // Just for clarity.
 
+	LLImageDataLock lock(this);
+
 	llassert( (4 == src->getComponents()) && (3 == dst->getComponents()) );
 
 	S32 temp_data_size = src->getWidth() * dst->getHeight() * src->getComponents();
@@ -1170,14 +1202,16 @@ void LLImageRaw::compositeScaled4onto3(LLImageRaw* src)
 
 
 // Src and dst are same size.  Src has 4 components.  Dst has 3 components.
-void LLImageRaw::compositeUnscaled4onto3( LLImageRaw* src )
+void LLImageRaw::compositeUnscaled4onto3( const LLImageRaw* src )
 {
 	LLImageRaw* dst = this;  // Just for clarity.
 
+	LLImageDataLock lock(this);
+
 	llassert( (3 == src->getComponents()) || (4 == src->getComponents()) );
 	llassert( (src->getWidth() == dst->getWidth()) && (src->getHeight() == dst->getHeight()) );
 
-	U8* src_data = src->getData();
+	const U8* src_data = src->getData();
 	U8* dst_data = dst->getData();
 	S32 pixels = getWidth() * getHeight();
 	while( pixels-- )
@@ -1207,10 +1241,13 @@ void LLImageRaw::compositeUnscaled4onto3( LLImageRaw* src )
 }
 
 
-void LLImageRaw::copyUnscaledAlphaMask( LLImageRaw* src, const LLColor4U& fill)
+void LLImageRaw::copyUnscaledAlphaMask( const LLImageRaw* src, const LLColor4U& fill)
 {
 	LLImageRaw* dst = this;  // Just for clarity.
 
+	LLImageDataSharedLock lockIn(src);
+	LLImageDataLock lockOut(this);
+
 	if (!validateSrcAndDst("LLImageRaw::copyUnscaledAlphaMask", src, dst))
 	{
 		return;
@@ -1221,7 +1258,7 @@ void LLImageRaw::copyUnscaledAlphaMask( LLImageRaw* src, const LLColor4U& fill)
 	llassert( (src->getWidth() == dst->getWidth()) && (src->getHeight() == dst->getHeight()) );
 
 	S32 pixels = getWidth() * getHeight();
-	U8* src_data = src->getData();
+	const U8* src_data = src->getData();
 	U8* dst_data = dst->getData();
 	for ( S32 i = 0; i < pixels; i++ )
 	{
@@ -1238,6 +1275,8 @@ void LLImageRaw::copyUnscaledAlphaMask( LLImageRaw* src, const LLColor4U& fill)
 // Fill the buffer with a constant color
 void LLImageRaw::fill( const LLColor4U& color )
 {
+	LLImageDataLock lock(this);
+
 	if (isBufferInvalid())
 	{
 		LL_WARNS() << "Invalid image buffer" << LL_ENDL;
@@ -1275,16 +1314,21 @@ LLPointer<LLImageRaw> LLImageRaw::duplicate()
 		return this; //nobody else refences to this image, no need to duplicate.
 	}
 
+	LLImageDataSharedLock lock(this);
+
 	//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)
+void LLImageRaw::copy(const LLImageRaw* src)
 {
 	LLImageRaw* dst = this;  // Just for clarity.
 
+	LLImageDataSharedLock lockIn(src);
+	LLImageDataLock lockOut(this);
+
 	if (!validateSrcAndDst("LLImageRaw::copy", src, dst))
 	{
 		return;
@@ -1330,10 +1374,12 @@ void LLImageRaw::copy(LLImageRaw* src)
 }
 
 // Src and dst are same size.  Src and dst have same number of components.
-void LLImageRaw::copyUnscaled(LLImageRaw* src)
+void LLImageRaw::copyUnscaled(const LLImageRaw* src)
 {
 	LLImageRaw* dst = this;  // Just for clarity.
 
+	LLImageDataLock lock(this);
+
 	llassert( (1 == src->getComponents()) || (3 == src->getComponents()) || (4 == src->getComponents()) );
 	llassert( src->getComponents() == dst->getComponents() );
 	llassert( (src->getWidth() == dst->getWidth()) && (src->getHeight() == dst->getHeight()) );
@@ -1343,7 +1389,7 @@ void LLImageRaw::copyUnscaled(LLImageRaw* src)
 
 
 // Src and dst can be any size.  Src has 3 components.  Dst has 4 components.
-void LLImageRaw::copyScaled3onto4(LLImageRaw* src)
+void LLImageRaw::copyScaled3onto4(const LLImageRaw* src)
 {
 	llassert( (3 == src->getComponents()) && (4 == getComponents()) );
 
@@ -1355,7 +1401,7 @@ void LLImageRaw::copyScaled3onto4(LLImageRaw* src)
 
 
 // Src and dst can be any size.  Src has 4 components.  Dst has 3 components.
-void LLImageRaw::copyScaled4onto3(LLImageRaw* src)
+void LLImageRaw::copyScaled4onto3(const LLImageRaw* src)
 {
 	llassert( (4 == src->getComponents()) && (3 == getComponents()) );
 
@@ -1367,15 +1413,17 @@ void LLImageRaw::copyScaled4onto3(LLImageRaw* src)
 
 
 // Src and dst are same size.  Src has 4 components.  Dst has 3 components.
-void LLImageRaw::copyUnscaled4onto3( LLImageRaw* src )
+void LLImageRaw::copyUnscaled4onto3( const LLImageRaw* src )
 {
 	LLImageRaw* dst = this;  // Just for clarity.
 
+	LLImageDataLock lock(this);
+
 	llassert( (3 == dst->getComponents()) && (4 == src->getComponents()) );
 	llassert( (src->getWidth() == dst->getWidth()) && (src->getHeight() == dst->getHeight()) );
 
 	S32 pixels = getWidth() * getHeight();
-	U8* src_data = src->getData();
+	const U8* src_data = src->getData();
 	U8* dst_data = dst->getData();
 	for( S32 i=0; i<pixels; i++ )
 	{
@@ -1389,15 +1437,18 @@ void LLImageRaw::copyUnscaled4onto3( LLImageRaw* src )
 
 
 // Src and dst are same size.  Src has 3 components.  Dst has 4 components.
-void LLImageRaw::copyUnscaled3onto4( LLImageRaw* src )
+void LLImageRaw::copyUnscaled3onto4( const LLImageRaw* src )
 {
 	LLImageRaw* dst = this;  // Just for clarity.
+
+	LLImageDataLock lock(this);
+
 	llassert( 3 == src->getComponents() );
 	llassert( 4 == dst->getComponents() );
 	llassert( (src->getWidth() == dst->getWidth()) && (src->getHeight() == dst->getHeight()) );
 
 	S32 pixels = getWidth() * getHeight();
-	U8* src_data = src->getData();
+	const U8* src_data = src->getData();
 	U8* dst_data = dst->getData();
 	for( S32 i=0; i<pixels; i++ )
 	{
@@ -1412,10 +1463,13 @@ 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 )
+void LLImageRaw::copyScaled( const LLImageRaw* src )
 {
 	LLImageRaw* dst = this;  // Just for clarity.
 
+	LLImageDataSharedLock lockIn(src);
+	LLImageDataLock lockOut(this);
+
 	if (!validateSrcAndDst("LLImageRaw::copyScaled", src, dst))
 	{
 		return;
@@ -1457,6 +1511,8 @@ void LLImageRaw::copyScaled( LLImageRaw* src )
 
 bool LLImageRaw::scale( S32 new_width, S32 new_height, bool scale_image_data )
 {
+    LLImageDataLock lock(this);
+
     S32 components = getComponents();
     if (components != 1 && components != 3 && components != 4)
     {
@@ -1543,6 +1599,8 @@ LLPointer<LLImageRaw> LLImageRaw::scaled(S32 new_width, S32 new_height)
 {
     LLPointer<LLImageRaw> result;
 
+    LLImageDataLock lock(this);
+
     S32 components = getComponents();
     if (components != 1 && components != 3 && components != 4)
     {
@@ -1588,7 +1646,7 @@ LLPointer<LLImageRaw> LLImageRaw::scaled(S32 new_width, S32 new_height)
     return result;
 }
 
-void LLImageRaw::copyLineScaled( U8* in, U8* out, S32 in_pixel_len, S32 out_pixel_len, S32 in_pixel_step, S32 out_pixel_step )
+void LLImageRaw::copyLineScaled( const U8* in, U8* out, S32 in_pixel_len, S32 out_pixel_len, S32 in_pixel_step, S32 out_pixel_step )
 {
 	const S32 components = getComponents();
 	llassert( components >= 1 && components <= 4 );
@@ -1615,7 +1673,7 @@ void LLImageRaw::copyLineScaled( U8* in, U8* out, S32 in_pixel_len, S32 out_pixe
 			S32 t0 = x * out_pixel_step * components;
 			S32 t1 = index0 * in_pixel_step * components;
 			U8* outp = out + t0;
-			U8* inp = in + t1;
+			const U8* inp = in + t1;
 			for (S32 i = 0; i < components; ++i)
 			{
 				*outp = *inp;
@@ -1703,7 +1761,7 @@ void LLImageRaw::copyLineScaled( U8* in, U8* out, S32 in_pixel_len, S32 out_pixe
 	}
 }
 
-void LLImageRaw::compositeRowScaled4onto3( U8* in, U8* out, S32 in_pixel_len, S32 out_pixel_len )
+void LLImageRaw::compositeRowScaled4onto3( const U8* in, U8* out, S32 in_pixel_len, S32 out_pixel_len )
 {
 	llassert( getComponents() == 3 );
 
@@ -1799,8 +1857,12 @@ void LLImageRaw::compositeRowScaled4onto3( U8* in, U8* out, S32 in_pixel_len, S3
 	}
 }
 
-bool LLImageRaw::validateSrcAndDst(std::string func, LLImageRaw* src, LLImageRaw* dst)
+// static
+bool LLImageRaw::validateSrcAndDst(std::string func, const LLImageRaw* src, const LLImageRaw* dst)
 {
+	LLImageDataSharedLock lockIn(src);
+	LLImageDataLock lockOut(dst);
+
 	if (!src || !dst || src->isBufferInvalid() || dst->isBufferInvalid())
 	{
 		LL_WARNS() << func << ": Source: ";
@@ -2113,6 +2175,8 @@ bool LLImageFormatted::decodeChannels(LLImageRaw* raw_image,F32  decode_time, S3
 // virtual
 U8* LLImageFormatted::allocateData(S32 size)
 {
+	LLImageDataLock lock(this);
+
 	U8* res = LLImageBase::allocateData(size); // calls deleteData()
 	sGlobalFormattedMemory += getDataSize();
 	return res;
@@ -2121,6 +2185,8 @@ U8* LLImageFormatted::allocateData(S32 size)
 // virtual
 U8* LLImageFormatted::reallocateData(S32 size)
 {
+	LLImageDataLock lock(this);
+
 	sGlobalFormattedMemory -= getDataSize();
 	U8* res = LLImageBase::reallocateData(size);
 	sGlobalFormattedMemory += getDataSize();
@@ -2130,6 +2196,8 @@ U8* LLImageFormatted::reallocateData(S32 size)
 // virtual
 void LLImageFormatted::deleteData()
 {
+    LLImageDataLock lock(this);
+
     if (mDecoding)
     {
         LL_ERRS() << "LLImageFormatted::deleteData() is called during decoding" << LL_ENDL;
@@ -2159,6 +2227,8 @@ void LLImageFormatted::sanityCheck()
 
 bool LLImageFormatted::copyData(U8 *data, S32 size)
 {
+	LLImageDataLock lock(this);
+
 	if ( data && ((data != getData()) || (size != getDataSize())) )
 	{
 		deleteData();
@@ -2171,6 +2241,8 @@ bool LLImageFormatted::copyData(U8 *data, S32 size)
 // LLImageFormatted becomes the owner of data
 void LLImageFormatted::setData(U8 *data, S32 size)
 {
+	LLImageDataLock lock(this);
+
 	if (data && data != getData())
 	{
 		deleteData();
@@ -2184,6 +2256,8 @@ void LLImageFormatted::appendData(U8 *data, S32 size)
 {
 	if (data)
 	{
+		LLImageDataLock lock(this);
+
 		if (!getData())
 		{
 			setData(data, size);
@@ -2225,6 +2299,9 @@ bool LLImageFormatted::load(const std::string &filename, int load_size)
 	{
 		load_size = file_size;
 	}
+
+	LLImageDataLock lock(this);
+
 	bool res;
 	U8 *data = allocateData(load_size);
 	if (data)
@@ -2262,8 +2339,10 @@ bool LLImageFormatted::save(const std::string &filename)
 		setLastError("Unable to open file for writing", filename);
 		return false;
 	}
-	
-	outfile.write(getData(), 	getDataSize());
+
+	LLImageDataSharedLock lock(this);
+
+	outfile.write(getData(), getDataSize());
 	outfile.close() ;
 	return true;
 }
diff --git a/indra/llimage/llimage.h b/indra/llimage/llimage.h
index 8f9e1b3c54..cc6a58f417 100644
--- a/indra/llimage/llimage.h
+++ b/indra/llimage/llimage.h
@@ -116,7 +116,11 @@ class LLImageBase
 {
 protected:
 	virtual ~LLImageBase();
-	
+
+	virtual void deleteData();
+	virtual U8* allocateData(S32 size = -1);
+	virtual U8* reallocateData(S32 size = -1);
+
 public:
 	LLImageBase();
 
@@ -126,10 +130,6 @@ public:
 		TYPE_AVATAR_BAKE = 1,
 	};
 
-	virtual void deleteData();
-	virtual U8* allocateData(S32 size = -1);
-	virtual U8* reallocateData(S32 size = -1);
-
 	virtual void dump();
 	virtual void sanityCheck();
 
@@ -171,10 +171,27 @@ private:
 
 	S8 mComponents;
 
-	bool mBadBufferAllocation ;
-	bool mAllowOverSize ;
+	bool mBadBufferAllocation;
+	bool mAllowOverSize;
+
+private:
+    mutable LLSharedMutex mDataMutex;
+
+public:
+    template<bool SHARED>
+    class DataLock : LLSharedMutexLockTemplate<SHARED>
+    {
+    public:
+        DataLock(const LLImageBase* image)
+        : LLSharedMutexLockTemplate<SHARED>(image ? &image->mDataMutex : nullptr)
+        {
+        }
+    };
 };
 
+using LLImageDataLock = LLImageBase::DataLock<false>;
+using LLImageDataSharedLock = LLImageBase::DataLock<true>;
+
 // Raw representation of an image (used for textures, and other uncompressed formats
 class LLImageRaw : public LLImageBase
 {
@@ -231,51 +248,51 @@ public:
 	LLPointer<LLImageRaw> duplicate();
 
 	// Src and dst can be any size.  Src and dst can each have 3 or 4 components.
-	void copy( LLImageRaw* src );
+	void copy( const LLImageRaw* src );
 
 	// Src and dst are same size.  Src and dst have same number of components.
-	void copyUnscaled( LLImageRaw* src );
+	void copyUnscaled( const LLImageRaw* src );
 	
 	// Src and dst are same size.  Src has 4 components.  Dst has 3 components.
-	void copyUnscaled4onto3( LLImageRaw* src );
+	void copyUnscaled4onto3( const LLImageRaw* src );
 
 	// Src and dst are same size.  Src has 3 components.  Dst has 4 components.
-	void copyUnscaled3onto4( LLImageRaw* src );
+	void copyUnscaled3onto4( const 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);
+	void copyUnscaledAlphaMask( const LLImageRaw* src, const LLColor4U& fill);
 
 	// Src and dst can be any size.  Src and dst have same number of components.
-	void copyScaled( LLImageRaw* src );
-
-	// Src and dst can be any size.  Src has 3 components.  Dst has 4 components.
-	void copyScaled3onto4( LLImageRaw* src );
-
-	// Src and dst can be any size.  Src has 4 components.  Dst has 3 components.
-	void copyScaled4onto3( LLImageRaw* src );
+	void copyScaled( const LLImageRaw* src );
 
 
 	// Composite operations
 
 	// Src and dst can be any size.  Src and dst can each have 3 or 4 components.
-	void composite( LLImageRaw* src );
+	void composite( const LLImageRaw* src );
 
+protected:
 	// Src and dst can be any size.  Src has 4 components.  Dst has 3 components.
-	void compositeScaled4onto3( LLImageRaw* src );
+	void compositeScaled4onto3( const LLImageRaw* src );
 
 	// Src and dst are same size.  Src has 4 components.  Dst has 3 components.
-	void compositeUnscaled4onto3( LLImageRaw* src );
+	void compositeUnscaled4onto3( const LLImageRaw* src );
+
+	// Src and dst can be any size.  Src has 3 components.  Dst has 4 components.
+	void copyScaled3onto4( const LLImageRaw* src );
+
+	// Src and dst can be any size.  Src has 4 components.  Dst has 3 components.
+	void copyScaled4onto3( const LLImageRaw* src );
 
-protected:
 	// Create an image from a local file (generally used in tools)
 	//bool createFromFile(const std::string& filename, bool j2c_lowest_mip_only = false);
 
-	void copyLineScaled( U8* in, U8* out, S32 in_pixel_len, S32 out_pixel_len, S32 in_pixel_step, S32 out_pixel_step );
-	void compositeRowScaled4onto3( U8* in, U8* out, S32 in_pixel_len, S32 out_pixel_len );
+	void copyLineScaled( const U8* in, U8* out, S32 in_pixel_len, S32 out_pixel_len, S32 in_pixel_step, S32 out_pixel_step );
+	void compositeRowScaled4onto3( const U8* in, U8* out, S32 in_pixel_len, S32 out_pixel_len );
 
-	U8	fastFractionalMult(U8 a,U8 b);
+	static U8 fastFractionalMult(U8 a, U8 b);
 
 	void setDataAndSize(U8 *data, S32 width, S32 height, S8 components) ;
 
@@ -283,7 +300,7 @@ public:
 	static S32 sRawImageCount;
 
 private:
-	bool validateSrcAndDst(std::string func, LLImageRaw* src, LLImageRaw* dst);
+	static bool validateSrcAndDst(std::string func, const LLImageRaw* src, const LLImageRaw* dst);
 };
 
 // Compressed representation of image.
@@ -356,7 +373,7 @@ protected:
 	S8 mDecoded;  // unused, but changing LLImage layout requires recompiling static Mac/Linux libs. 2009-01-30 JC
 	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 90b7272efa..d26d160537 100644
--- a/indra/llimage/llimagebmp.cpp
+++ b/indra/llimage/llimagebmp.cpp
@@ -96,6 +96,8 @@ bool LLImageBMP::updateData()
 {
 	resetLastError();
 
+	LLImageDataLock lock(this);
+
 	// Check to make sure that this instance has been initialized with data
 	U8* mdata = getData();
 	if (!mdata || (0 == getDataSize()))
@@ -336,8 +338,11 @@ bool LLImageBMP::decode(LLImageRaw* raw_image, F32 decode_time)
 	
 	resetLastError();
 
+	LLImageDataLock lockIn(this);
+	LLImageDataLock lockOut(raw_image);
+
 	// Check to make sure that this instance has been initialized with data
-	U8* mdata = getData();
+	const U8* mdata = getData();
 	if (!mdata || (0 == getDataSize()))
 	{
 		setLastError("llimagebmp trying to decode an image with no data!");
@@ -350,7 +355,7 @@ bool LLImageBMP::decode(LLImageRaw* raw_image, F32 decode_time)
 		return false;
 	}
 
-	U8* src = mdata + mBitmapOffset;
+	const U8* src = mdata + mBitmapOffset;
 	U8* dst = raw_image->getData();
 
 	bool success = false;
@@ -397,7 +402,7 @@ U32 LLImageBMP::countTrailingZeros( U32 m )
 }
 
 
-bool LLImageBMP::decodeColorMask16( U8* dst, U8* src )
+bool LLImageBMP::decodeColorMask16( U8* dst, const U8* src )
 {
 	llassert( 16 == mBitsPerPixel );
 
@@ -433,7 +438,7 @@ bool LLImageBMP::decodeColorMask16( U8* dst, U8* src )
 	return true;
 }
 
-bool LLImageBMP::decodeColorMask32( U8* dst, U8* src )
+bool LLImageBMP::decodeColorMask32( U8* dst, const U8* src )
 {
 	// Note: alpha is not supported
 
@@ -477,7 +482,7 @@ bool LLImageBMP::decodeColorMask32( U8* dst, U8* src )
 }
 
 
-bool LLImageBMP::decodeColorTable8( U8* dst, U8* src )
+bool LLImageBMP::decodeColorTable8( U8* dst, const U8* src )
 {
 	llassert( (8 == mBitsPerPixel) && (mColorPaletteColors >= 256) );
 
@@ -507,7 +512,7 @@ bool LLImageBMP::decodeColorTable8( U8* dst, U8* src )
 }
 
 
-bool LLImageBMP::decodeTruecolor24( U8* dst, U8* src )
+bool LLImageBMP::decodeTruecolor24( U8* dst, const U8* src )
 {
 	llassert( 24 == mBitsPerPixel );
 	llassert( 3 == getComponents() );
@@ -541,6 +546,9 @@ bool LLImageBMP::encode(const LLImageRaw* raw_image, F32 encode_time)
 	
 	resetLastError();
 
+	LLImageDataSharedLock lockIn(raw_image);
+	LLImageDataLock lockOut(this);
+
 	S32 src_components = raw_image->getComponents();
 	S32 dst_components =  ( src_components < 3 ) ? 1 : 3;
 
diff --git a/indra/llimage/llimagebmp.h b/indra/llimage/llimagebmp.h
index 6a5fa4697d..295f96e541 100644
--- a/indra/llimage/llimagebmp.h
+++ b/indra/llimage/llimagebmp.h
@@ -45,10 +45,10 @@ public:
 	/*virtual*/ bool encode(const LLImageRaw* raw_image, F32 encode_time);
 
 protected:
-	bool		decodeColorTable8( U8* dst, U8* src );
-	bool		decodeColorMask16( U8* dst, U8* src );
-	bool		decodeTruecolor24( U8* dst, U8* src );
-	bool		decodeColorMask32( U8* dst, U8* src );
+	bool		decodeColorTable8( U8* dst, const U8* src );
+	bool		decodeColorMask16( U8* dst, const U8* src );
+	bool		decodeTruecolor24( U8* dst, const U8* src );
+	bool		decodeColorMask32( U8* dst, const U8* src );
 
 	U32			countTrailingZeros( U32 m );
 
diff --git a/indra/llimage/llimagedxt.cpp b/indra/llimage/llimagedxt.cpp
index 36317a5ba8..f4bb3bb120 100644
--- a/indra/llimage/llimagedxt.cpp
+++ b/indra/llimage/llimagedxt.cpp
@@ -176,6 +176,8 @@ bool LLImageDXT::updateData()
 {
 	resetLastError();
 
+	LLImageDataLock lock(this);
+
 	U8* data = getData();
 	S32 data_size = getDataSize();
 	
@@ -268,7 +270,10 @@ bool LLImageDXT::decode(LLImageRaw* raw_image, F32 time)
 		LL_WARNS() << "Attempt to decode compressed LLImageDXT to Raw (unsupported)" << LL_ENDL;
 		return false;
 	}
-	
+
+	LLImageDataSharedLock lockIn(this);
+	LLImageDataLock lockOut(raw_image);
+
 	S32 width = getWidth(), height = getHeight();
 	S32 ncomponents = getComponents();
 	U8* data = NULL;
@@ -309,6 +314,9 @@ bool LLImageDXT::getMipData(LLPointer<LLImageRaw>& raw, S32 discard)
 	{
 		LL_ERRS() << "Request for invalid discard level" << LL_ENDL;
 	}
+
+	LLImageDataSharedLock lock(this);
+
 	U8* data = getData() + getMipOffset(discard);
 	S32 width = 0;
 	S32 height = 0;
@@ -339,6 +347,8 @@ bool LLImageDXT::encodeDXT(const LLImageRaw* raw_image, F32 time, bool explicit_
 		return 0;
 	}
 
+	LLImageDataLock lock(this);
+
 	S32 width = raw_image->getWidth();
 	S32 height = raw_image->getHeight();
 
@@ -430,6 +440,9 @@ bool LLImageDXT::convertToDXR()
 		return false;
 	}
 	mFileFormat = newformat;
+
+	LLImageDataLock lock(this);
+
 	S32 width = getWidth(), height = getHeight();
 	S32 nmips = calcNumMips(width,height);
 	S32 total_bytes = getDataSize();
diff --git a/indra/llimage/llimagefilter.cpp b/indra/llimage/llimagefilter.cpp
index 41adc7be9a..61c2e1d742 100644
--- a/indra/llimage/llimagefilter.cpp
+++ b/indra/llimage/llimagefilter.cpp
@@ -87,7 +87,9 @@ LLImageFilter::~LLImageFilter()
 void LLImageFilter::executeFilter(LLPointer<LLImageRaw> raw_image)
 {
     mImage = raw_image;
-    
+
+    LLImageDataLock lock(mImage);
+
 	//std::cout << "Filter : size = " << mFilterData.size() << std::endl;
 	for (S32 i = 0; i < mFilterData.size(); ++i)
 	{
diff --git a/indra/llimage/llimagej2c.cpp b/indra/llimage/llimagej2c.cpp
index a4957466d4..a06c461107 100644
--- a/indra/llimage/llimagej2c.cpp
+++ b/indra/llimage/llimagej2c.cpp
@@ -107,6 +107,8 @@ bool LLImageJ2C::updateData()
 	bool res = true;
 	resetLastError();
 
+	LLImageDataLock lock(this);
+
 	// Check to make sure that this instance has been initialized with data
 	if (!getData() || (getDataSize() < 16))
 	{
@@ -158,22 +160,26 @@ bool LLImageJ2C::decodeChannels(LLImageRaw *raw_imagep, F32 decode_time, S32 fir
 	LLTimer elapsed;
 
 	resetLastError();
-	mDecoding = true;
 
 	bool res;
-	// Check to make sure that this instance has been initialized with data
-	if (!getData() || (getDataSize() < 16))
 	{
-		setLastError("LLImageJ2C uninitialized");
-		res = true; // done
-	}
-	else
-	{
-		// Update the raw discard level
-		updateRawDiscardLevel();
-		res = mImpl->decodeImpl(*this, *raw_imagep, decode_time, first_channel, max_channel_count);
+		LLImageDataLock lock(this);
+
+		mDecoding = true;
+		// Check to make sure that this instance has been initialized with data
+		if (!getData() || (getDataSize() < 16))
+		{
+			setLastError("LLImageJ2C uninitialized");
+			res = true; // done
+		}
+		else
+		{
+			// Update the raw discard level
+			updateRawDiscardLevel();
+			res = mImpl->decodeImpl(*this, *raw_imagep, decode_time, first_channel, max_channel_count);
+		}
 	}
-	
+
 	if (res)
 	{
 		if (!mDecoding)
@@ -414,9 +420,10 @@ bool LLImageJ2C::loadAndValidate(const std::string &filename)
 
 bool LLImageJ2C::validate(U8 *data, U32 file_size)
 {
-
 	resetLastError();
-	
+
+	LLImageDataLock lock(this);
+
 	setData(data, file_size);
 
 	bool res = updateData();
diff --git a/indra/llimage/llimagejpeg.cpp b/indra/llimage/llimagejpeg.cpp
index 32a5472ec8..a35171601a 100644
--- a/indra/llimage/llimagejpeg.cpp
+++ b/indra/llimage/llimagejpeg.cpp
@@ -50,6 +50,8 @@ bool LLImageJPEG::updateData()
 {
 	resetLastError();
 
+	LLImageDataLock lock(this);
+
 	// Check to make sure that this instance has been initialized with data
 	if (!getData() || (0 == getDataSize()))
 	{
@@ -188,7 +190,10 @@ bool LLImageJPEG::decode(LLImageRaw* raw_image, F32 decode_time)
 	llassert_always(raw_image);
 	
 	resetLastError();
-	
+
+	LLImageDataLock lockIn(this);
+	LLImageDataLock lockOut(raw_image);
+
 	// Check to make sure that this instance has been initialized with data
 	if (!getData() || (0 == getDataSize()))
 	{
@@ -408,6 +413,8 @@ void LLImageJPEG::encodeTermDestination( j_compress_ptr cinfo )
 {
 	LLImageJPEG* self = (LLImageJPEG*) cinfo->client_data;
 
+	LLImageDataLock lock(self);
+
 	S32 file_bytes = (S32)(self->mOutputBufferSize - cinfo->dest->free_in_buffer);
 	self->allocateData(file_bytes);
 
@@ -484,6 +491,9 @@ bool LLImageJPEG::encode( const LLImageRaw* raw_image, F32 encode_time )
 	
 	resetLastError();
 
+	LLImageDataSharedLock lockIn(raw_image);
+	LLImageDataLock lockOut(this);
+
 	switch( raw_image->getComponents() )
 	{
 	case 1:
diff --git a/indra/llimage/llimagejpeg.h b/indra/llimage/llimagejpeg.h
index 7a849a8421..d674b40b8f 100644
--- a/indra/llimage/llimagejpeg.h
+++ b/indra/llimage/llimagejpeg.h
@@ -73,8 +73,6 @@ public:
 	static void		errorEmitMessage(j_common_ptr cinfo, int msg_level);
 	static void		errorOutputMessage(j_common_ptr cinfo);
 
-	static bool		decompress(LLImageJPEG* imagep);
-
 protected:
 	U8*				mOutputBuffer;		// temp buffer used during encoding
 	S32				mOutputBufferSize;	// bytes in mOuputBuffer
diff --git a/indra/llimage/llimagepng.cpp b/indra/llimage/llimagepng.cpp
index c4b98d8260..5d956bfb4e 100644
--- a/indra/llimage/llimagepng.cpp
+++ b/indra/llimage/llimagepng.cpp
@@ -51,6 +51,8 @@ bool LLImagePNG::updateData()
 {
     resetLastError();
 
+    LLImageDataLock lock(this);
+
     // Check to make sure that this instance has been initialized with data
     if (!getData() || (0 == getDataSize()))
     {
@@ -87,6 +89,9 @@ bool LLImagePNG::decode(LLImageRaw* raw_image, F32 decode_time)
 
     resetLastError();
 
+    LLImageDataSharedLock lockIn(this);
+    LLImageDataLock lockOut(raw_image);
+
     // Check to make sure that this instance has been initialized with data
     if (!getData() || (0 == getDataSize()))
     {
@@ -119,6 +124,9 @@ bool LLImagePNG::encode(const LLImageRaw* raw_image, F32 encode_time)
 
     resetLastError();
 
+	LLImageDataSharedLock lockIn(raw_image);
+	LLImageDataLock lockOut(this);
+
 	// Image logical size
 	setSize(raw_image->getWidth(), raw_image->getHeight(), raw_image->getComponents());
 
diff --git a/indra/llimage/llimagetga.cpp b/indra/llimage/llimagetga.cpp
index 88bdae9b80..290c0da4bf 100644
--- a/indra/llimage/llimagetga.cpp
+++ b/indra/llimage/llimagetga.cpp
@@ -108,6 +108,8 @@ bool LLImageTGA::updateData()
 {
 	resetLastError();
 
+	LLImageDataLock lock(this);
+
 	// Check to make sure that this instance has been initialized with data
 	if (!getData() || (0 == getDataSize()))
 	{
@@ -326,7 +328,10 @@ bool LLImageTGA::updateData()
 bool LLImageTGA::decode(LLImageRaw* raw_image, F32 decode_time)
 {
 	llassert_always(raw_image);
-	
+
+	LLImageDataSharedLock lockIn(this);
+	LLImageDataLock lockOut(raw_image);
+
 	// Check to make sure that this instance has been initialized with data
 	if (!getData() || (0 == getDataSize()))
 	{
@@ -642,7 +647,10 @@ bool LLImageTGA::decodeColorMap( LLImageRaw* raw_image, bool rle, bool flipped )
 bool LLImageTGA::encode(const LLImageRaw* raw_image, F32 encode_time)
 {
 	llassert_always(raw_image);
-	
+
+	LLImageDataSharedLock lockIn(raw_image);
+	LLImageDataLock lockOut(this);
+
 	deleteData();
 
 	setSize(raw_image->getWidth(), raw_image->getHeight(), raw_image->getComponents());
@@ -1061,6 +1069,9 @@ bool LLImageTGA::decodeAndProcess( LLImageRaw* raw_image, F32 domain, F32 weight
 	// --+---Input--------------------------------
 	//   |
 
+	LLImageDataSharedLock lockIn(this);
+	LLImageDataLock lockOut(raw_image);
+
 	if (!getData() || (0 == getDataSize()))
 	{
 		setLastError("LLImageTGA trying to decode an image with no data!");
diff --git a/indra/llimage/llimageworker.cpp b/indra/llimage/llimageworker.cpp
index c1ee052997..44749343e1 100644
--- a/indra/llimage/llimageworker.cpp
+++ b/indra/llimage/llimageworker.cpp
@@ -149,9 +149,18 @@ ImageRequest::~ImageRequest()
 bool ImageRequest::processRequest()
 {
     LL_PROFILE_ZONE_SCOPED_CATEGORY_TEXTURE;
+
+	if (mFormattedImage.isNull())
+		return true;
+
 	const F32 decode_time_slice = 0.f; //disable time slicing
 	bool done = true;
-	if (!mDecodedRaw && mFormattedImage.notNull())
+
+	LLImageDataLock lockFormatted(mFormattedImage);
+	LLImageDataLock lockDecodedRaw(mDecodedImageRaw);
+	LLImageDataLock lockDecodedAux(mDecodedImageAux);
+
+	if (!mDecodedRaw)
 	{
 		// Decode primary channels
 		if (mDecodedImageRaw.isNull())
@@ -177,7 +186,7 @@ bool ImageRequest::processRequest()
 		// some decoders are removing data when task is complete and there were errors
 		mDecodedRaw = done && mDecodedImageRaw->getData();
 	}
-	if (done && mNeedsAux && !mDecodedAux && mFormattedImage.notNull())
+	if (done && mNeedsAux && !mDecodedAux)
 	{
 		// Decode aux channel
 		if (!mDecodedImageAux)
diff --git a/indra/llimage/llpngwrapper.cpp b/indra/llimage/llpngwrapper.cpp
index cad7c00042..27e23b577b 100644
--- a/indra/llimage/llpngwrapper.cpp
+++ b/indra/llimage/llpngwrapper.cpp
@@ -173,6 +173,8 @@ BOOL LLPngWrapper::readPng(U8* src, S32 dataSize, LLImageRaw* rawImage, ImageInf
 		// data space
 		if (rawImage != NULL)
 		{
+			LLImageDataLock lock(rawImage);
+
 			if (!rawImage->resize(static_cast<U16>(mWidth),
 				static_cast<U16>(mHeight), mChannels))
 			{
-- 
cgit v1.2.3