diff --git a/indra/llimage/llimage.cpp b/indra/llimage/llimage.cpp
index cce03ff6e66307d4c3dcfabf4eba64d3a58d9f7f..7f95441075295981f9bbc52edc084e1e57823b5a 100644
--- a/indra/llimage/llimage.cpp
+++ b/indra/llimage/llimage.cpp
@@ -1337,8 +1337,7 @@ LLImageFormatted::LLImageFormatted(S8 codec)
 	  mDecoding(0),
 	  mDecoded(0),
 	  mDiscardLevel(-1),
-	  mLevels(0),
-      mLayers(0)
+	  mLevels(0)
 {
 	mMemType = LLMemType::MTYPE_IMAGEFORMATTED;
 }
diff --git a/indra/llimage/llimage.h b/indra/llimage/llimage.h
index c5a7d6262e1134036ed85a3ee1a9c700b85aba4f..a643e4d9f5f51d5b1d2fe257cc149ed42ef5f6a2 100644
--- a/indra/llimage/llimage.h
+++ b/indra/llimage/llimage.h
@@ -62,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:
@@ -320,8 +321,6 @@ class LLImageFormatted : public LLImageBase
 	S8 getDiscardLevel() const { return mDiscardLevel; }
 	S8 getLevels() const { return mLevels; }
 	void setLevels(S8 nlevels) { mLevels = nlevels; }
-	S32 getLayers() const { return mLayers; }
-	void setLayers(S32 nlayers) { mLayers = nlayers; }
 
 	// setLastError needs to be deferred for J2C images since it may be called from a DLL
 	virtual void resetLastError();
@@ -336,7 +335,6 @@ class LLImageFormatted : public LLImageBase
 	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.
-	S32 mLayers;		// Number of quality layers in that image. Min is 1. 0 means unknown.
 	
 public:
 	static S32 sGlobalFormattedMemory;
diff --git a/indra/llimage/llimagej2c.cpp b/indra/llimage/llimagej2c.cpp
index dc5bd8b5d5b7bf1446e96fff4b076f5fc892cf1a..7894de0de504d2b1ab28067e702758774b8ac9f5 100644
--- a/indra/llimage/llimagej2c.cpp
+++ b/indra/llimage/llimagej2c.cpp
@@ -56,10 +56,9 @@ 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),
-							mLayersUsedForDataSizeCalcs(0)
+							mAreaUsedForDataSizeCalcs(0)
 {
 	mImpl = fallbackCreateLLImageJ2CImpl();
 
@@ -257,33 +256,40 @@ BOOL LLImageJ2C::encode(const LLImageRaw *raw_imagep, const char* comment_text,
 //static
 S32 LLImageJ2C::calcHeaderSizeJ2C()
 {
-	return FIRST_PACKET_SIZE; // Hack. just needs to be >= actual header size...
+	return HTTP_PACKET_SIZE; // Hack. just needs to be >= actual header size...
 }
 
 //static
-S32 LLImageJ2C::calcDataSizeJ2C(S32 w, S32 h, S32 comp, S32 discard_level, S32 nb_layers, F32 rate)
+S32 LLImageJ2C::calcDataSizeJ2C(S32 w, S32 h, S32 comp, S32 discard_level, F32 rate)
 {
-	// Note: This provides an estimation for the first quality layer of a given discard level
+	// 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
-	if (rate <= 0.f) rate = 1.f/8.f;
-	// Compute w/pow(2,discard_level) and h/pow(2,discard_level)
-	while (discard_level > 0)
+
+	// Estimate the number of layers. This is consistent with what's done in j2c encoding
+	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;
 	}
+	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;
-	F32 layer_factor = ((nb_layers > 0) && (nb_layers < 7) ? 3.0f * (7 - nb_layers): 3.0f);
 	S32 new_bytes = sqrt((F32)(w*h))*(F32)(comp)*rate*1000.f/layer_factor;
 	S32 old_bytes = (S32)((F32)(w*h*comp)*rate);
-	llinfos << "Merov debug : calcDataSizeJ2C, layers = " << nb_layers << ", old = " << old_bytes << ", new = " << new_bytes << llendl;
+	//llinfos << "Merov debug : w = " << w << ", h = " << h << ", c = " << comp << ", r = " << rate << ", d = " << discard_level << ", l = " << nb_layers << ", old = " << old_bytes << ", new = " << new_bytes << llendl;
 	bytes = (LLImage::useNewByteRange() ? new_bytes : old_bytes);
 	bytes = llmax(bytes, calcHeaderSizeJ2C());
 	return bytes;
@@ -298,26 +304,22 @@ S32 LLImageJ2C::calcHeaderSize()
 S32 LLImageJ2C::calcDataSize(S32 discard_level)
 {
 	discard_level = llclamp(discard_level, 0, MAX_DISCARD_LEVEL);
-
+	return calcDataSizeJ2C(getWidth(), getHeight(), getComponents(), discard_level, mRate);
+	/*
 	if ( mAreaUsedForDataSizeCalcs != (getHeight() * getWidth()) 
-		|| (mLayersUsedForDataSizeCalcs != getLayers())
 		|| (mDataSizes[0] == 0))
 	{
-		if (mLayersUsedForDataSizeCalcs != getLayers())
-		{
-			llinfos << "Merov debug : recomputing data size because " << mLayersUsedForDataSizeCalcs << " != " << getLayers() << llendl;
-		}
 		mAreaUsedForDataSizeCalcs = getHeight() * getWidth();
-		mLayersUsedForDataSizeCalcs = getLayers();
 		
 		S32 level = MAX_DISCARD_LEVEL;	// Start at the highest discard
 		while ( level >= 0 )
 		{
-			mDataSizes[level] = calcDataSizeJ2C(getWidth(), getHeight(), getComponents(), level, getLayers(), mRate);
+			mDataSizes[level] = calcDataSizeJ2C(getWidth(), getHeight(), getComponents(), level, mRate);
 			level--;
 		}
 	}
 	return mDataSizes[discard_level];
+	*/
 }
 
 S32 LLImageJ2C::calcDiscardLevelBytes(S32 bytes)
@@ -344,11 +346,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;
diff --git a/indra/llimage/llimagej2c.h b/indra/llimage/llimagej2c.h
index 28e3026aac22b1a6eeef37d67925a82b0c13ec24..91c344d12f2ec580ab134a13f7d1bae8b7dda2ae 100644
--- 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 ;
 
@@ -67,12 +70,11 @@ class LLImageJ2C : public LLImageFormatted
 
 	// 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, S32 nb_layers = 0, 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();
 
@@ -88,7 +90,6 @@ class LLImageJ2C : public LLImageFormatted
 	
 	S32 mDataSizes[MAX_DISCARD_LEVEL+1];		// Size of data required to reach a given level
 	U32 mAreaUsedForDataSizeCalcs;				// Height * width used to calculate mDataSizes
-	S32 mLayersUsedForDataSizeCalcs;			// Numbers of layers used to calculate mDataSizes
 
 	S8  mRawDiscardLevel;
 	F32 mRate;
diff --git a/indra/llkdu/llimagej2ckdu.cpp b/indra/llkdu/llimagej2ckdu.cpp
index 08d840917df13ba83595cefcecad0bbe4bc19cf0..31db9d48e3b324f5c8f4032b9ef393fadbad0975 100644
--- a/indra/llkdu/llimagej2ckdu.cpp
+++ b/indra/llkdu/llimagej2ckdu.cpp
@@ -195,8 +195,7 @@ mRawImagep(NULL),
 mDecodeState(NULL),
 mBlocksSize(-1),
 mPrecinctsSize(-1),
-mLevels(0),
-mLayers(0)
+mLevels(0)
 {
 }
 
@@ -472,13 +471,6 @@ BOOL LLImageJ2CKDU::decodeImpl(LLImageJ2C &base, LLImageRaw &raw_image, F32 deco
 				{
 					kdu_tile tile = mCodeStreamp->open_tile(*(mTPosp)+mTileIndicesp->pos);
 
-					int layers = mCodeStreamp->get_max_tile_layers();
-					if (layers > mLayers)
-					{
-						mLayers = layers;
-						base.setLayers(mLayers);
-					}
-
 					// Find the region of the buffer occupied by this
 					// tile.  Note that we have no control over
 					// sub-sampling factors which might have been used
@@ -608,21 +600,14 @@ BOOL LLImageJ2CKDU::encodeImpl(LLImageJ2C &base, const LLImageRaw &raw_image, co
 		}
 
 		// Set codestream options
-		mLayers = 0;
+		int nb_layers = 0;
 		kdu_long layer_bytes[MAX_NB_LAYERS];
 		U32 max_bytes = (U32)(base.getWidth() * base.getHeight() * base.getComponents());
 
-		// Rate is the argument passed into the LLImageJ2C which
-		// specifies the target compression rate.  The default is 8:1.
-		// *TODO: mRate is actually always 8:1 in the viewer. Test different values. Also force to reversible for small (< 500 bytes) textures.
-		if (base.mRate != 0.f)
-		{
-			max_bytes = (U32)((F32)(max_bytes) * base.mRate);
-		}
-		else
-		{
-			max_bytes = (U32)((F32)(max_bytes) / 8.0f);
-		}
+		// Rate is the argument passed into the LLImageJ2C which specifies the target compression rate. The default is 8:1.
+		// *TODO: mRate is actually always 8:1 in the viewer. Test different values.
+		llassert (base.mRate > 0.f);
+		max_bytes = (U32)((F32)(max_bytes) * base.mRate);
 		
 		// If the image is very small, code it in a lossless way.
 		// Note: it'll also have only 1 layer which is fine as there's no point reordering blocks in that case.
@@ -635,15 +620,15 @@ BOOL LLImageJ2CKDU::encodeImpl(LLImageJ2C &base, const LLImageRaw &raw_image, co
 		// We're using a logarithmic spacing rule that fits with our way of fetching texture data.
 		// Note: For more info on this layers business, read kdu_codestream::flush() doc in kdu_compressed.h
 		U32 i = FIRST_PACKET_SIZE;
-		while ((i < max_bytes) && (mLayers < (MAX_NB_LAYERS-1)))
+		while ((i < max_bytes) && (nb_layers < (MAX_NB_LAYERS-1)))
 		{
 			if (i == FIRST_PACKET_SIZE * 4)
 			{
 				// That really just means that the first layer is FIRST_PACKET_SIZE and the second is MIN_LAYER_SIZE
 				i = MIN_LAYER_SIZE;
 			}
-			layer_bytes[mLayers] = i;
-			mLayers++;
+			layer_bytes[nb_layers] = i;
+			nb_layers++;
 			i *= 4;
 		}
 
@@ -656,18 +641,17 @@ BOOL LLImageJ2CKDU::encodeImpl(LLImageJ2C &base, const LLImageRaw &raw_image, co
 			//codestream.access_siz()->parse_string("Cycc=no");
 			// In the reversible case, set the last entry of that table to 0 so that all generated bits will
 			// indeed be output by the time the last quality layer is encountered.
-			layer_bytes[mLayers] = 0;
+			layer_bytes[nb_layers] = 0;
 		}
 		else
 		{
 			// Truncate the last quality layer if necessary so to fit the set compression ratio
-			layer_bytes[mLayers] = max_bytes;
+			layer_bytes[nb_layers] = max_bytes;
 		}
-		mLayers++;
+		nb_layers++;
 
-		std::string layer_string = llformat("Clayers=%d",mLayers);
+		std::string layer_string = llformat("Clayers=%d",nb_layers);
 		codestream.access_siz()->parse_string(layer_string.c_str());
-		base.setLayers(mLayers);
 		
 		// Set up data ordering, markers, etc... if precincts or blocks specified
 		// Note: This code is *not* used in the encoding made by the viewer. It is currently used only
@@ -720,7 +704,7 @@ BOOL LLImageJ2CKDU::encodeImpl(LLImageJ2C &base, const LLImageRaw &raw_image, co
 		}
 
 		// Produce the compressed output
-		codestream.flush(layer_bytes,mLayers);
+		codestream.flush(layer_bytes,nb_layers);
 
 		// Cleanup
 		delete tile;
diff --git a/indra/llkdu/llimagej2ckdu.h b/indra/llkdu/llimagej2ckdu.h
index fab97326d4c3713022a5fd91caa03d16a9723713..9ab0b9e4a7bcfe9402ebb81956c3cb9b8e50a83a 100644
--- a/indra/llkdu/llimagej2ckdu.h
+++ b/indra/llkdu/llimagej2ckdu.h
@@ -75,7 +75,6 @@ class LLImageJ2CKDU : public LLImageJ2CImpl
 	int mBlocksSize;
 	int mPrecinctsSize;
 	int mLevels;
-	int mLayers;
 
 	// Temporary variables for in-progress decodes...
 	LLImageRaw *mRawImagep;
diff --git a/indra/newview/lltexlayer.cpp b/indra/newview/lltexlayer.cpp
index 1693cfc9e25bb417eb57baf30115fc137b87896a..467115c92849b6ad9bc2c8e7d637c752ba9d6b8e 100644
--- a/indra/newview/lltexlayer.cpp
+++ b/indra/newview/lltexlayer.cpp
@@ -497,7 +497,6 @@ void LLTexLayerSetBuffer::doUpload()
 	}
 	
 	LLPointer<LLImageJ2C> compressedImage = new LLImageJ2C;
-	compressedImage->setRate(0.f);
 	const char* comment_text = LINDEN_J2C_COMMENT_PREFIX "RGBHM"; // writes into baked_color_data. 5 channels (rgb, heightfield/alpha, mask)
 	if (compressedImage->encode(baked_image, comment_text))
 	{
diff --git a/indra/newview/llviewertexturelist.cpp b/indra/newview/llviewertexturelist.cpp
index 089f45ca899ae58eea2c7f36a0b4f81bd8d351ed..54ae5194228e02e39378893e7a0c71bae68d57a8 100644
--- a/indra/newview/llviewertexturelist.cpp
+++ b/indra/newview/llviewertexturelist.cpp
@@ -1030,7 +1030,6 @@ LLPointer<LLImageJ2C> LLViewerTextureList::convertToUploadFile(LLPointer<LLImage
 {
 	raw_image->biasedScaleToPowerOfTwo(LLViewerFetchedTexture::MAX_IMAGE_SIZE_DEFAULT);
 	LLPointer<LLImageJ2C> compressedImage = new LLImageJ2C();
-	compressedImage->setRate(0.f);
 	
 	if (gSavedSettings.getBOOL("LosslessJ2CUpload") &&
 		(raw_image->getWidth() * raw_image->getHeight() <= LL_IMAGE_REZ_LOSSLESS_CUTOFF * LL_IMAGE_REZ_LOSSLESS_CUTOFF))