diff options
Diffstat (limited to 'indra/llimage')
| -rw-r--r-- | indra/llimage/llimage.cpp | 24 | ||||
| -rw-r--r-- | indra/llimage/llimage.h | 17 | ||||
| -rw-r--r-- | indra/llimage/llimagej2c.cpp | 85 | ||||
| -rw-r--r-- | indra/llimage/llimagej2c.h | 10 | ||||
| -rw-r--r-- | indra/llimage/llimagepng.cpp | 12 | 
5 files changed, 91 insertions, 57 deletions
| diff --git a/indra/llimage/llimage.cpp b/indra/llimage/llimage.cpp index 56e01ac851..6775b005f4 100644 --- a/indra/llimage/llimage.cpp +++ b/indra/llimage/llimage.cpp @@ -48,11 +48,15 @@  //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() ; @@ -1334,7 +1338,8 @@ LLImageFormatted::LLImageFormatted(S8 codec)  	  mCodec(codec),  	  mDecoding(0),  	  mDecoded(0), -	  mDiscardLevel(-1) +	  mDiscardLevel(-1), +	  mLevels(0)  {  	mMemType = LLMemType::MTYPE_IMAGEFORMATTED;  } @@ -1561,7 +1566,7 @@ void LLImageFormatted::appendData(U8 *data, S32 size)  //---------------------------------------------------------------------------- -BOOL LLImageFormatted::load(const std::string &filename) +BOOL LLImageFormatted::load(const std::string &filename, int load_size)  {  	resetLastError(); @@ -1580,14 +1585,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 diff --git a/indra/llimage/llimage.h b/indra/llimage/llimage.h index 4469c9e860..46e6d1a901 100644 --- a/indra/llimage/llimage.h +++ b/indra/llimage/llimage.h @@ -48,6 +48,8 @@ const S32 MAX_PRECINCT_SIZE = 2048;			// No reason to be bigger than MAX_IMAGE_S  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 @@ -60,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: @@ -89,15 +92,20 @@ 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;  };  //============================================================================ @@ -294,7 +302,7 @@ public:  	// 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 @@ -313,6 +321,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(); @@ -325,7 +335,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/llimagej2c.cpp b/indra/llimage/llimagej2c.cpp index 8241746a74..452aad25cb 100644 --- a/indra/llimage/llimagej2c.cpp +++ b/indra/llimage/llimagej2c.cpp @@ -56,7 +56,7 @@ 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)  { @@ -142,6 +142,7 @@ BOOL LLImageJ2C::updateData()  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);  } @@ -261,19 +262,34 @@ S32 LLImageJ2C::calcHeaderSizeJ2C()  //static  S32 LLImageJ2C::calcDataSizeJ2C(S32 w, S32 h, S32 comp, S32 discard_level, F32 rate)  { -	// Note: this only provides an *estimate* of the size in bytes of an image level -	// *TODO: find a way to read the true size (when available) and convey the fact -	// that the result is an estimate in the other cases -	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;  } @@ -283,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(); @@ -301,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];  } @@ -334,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;  		} @@ -348,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; @@ -474,6 +464,7 @@ LLImageCompressionTester::LLImageCompressionTester() : LLMetricPerformanceTester  	addMetric("Perf Compression (kB/s)");  	mRunBytesInDecompression = 0; +	mRunBytesOutDecompression = 0;  	mRunBytesInCompression = 0;  	mTotalBytesInDecompression = 0; @@ -483,6 +474,7 @@ LLImageCompressionTester::LLImageCompressionTester() : LLMetricPerformanceTester  	mTotalTimeDecompression = 0.0f;  	mTotalTimeCompression = 0.0f; +	mRunTimeDecompression = 0.0f;  }  LLImageCompressionTester::~LLImageCompressionTester() @@ -565,12 +557,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 914174fc57..ce8195940d 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 @@ 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(); @@ -154,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/llimagepng.cpp b/indra/llimage/llimagepng.cpp index 8d493ecde0..294f68b122 100644 --- a/indra/llimage/llimagepng.cpp +++ b/indra/llimage/llimagepng.cpp @@ -60,6 +60,12 @@ 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))  	{ @@ -90,6 +96,12 @@ BOOL LLImagePNG::decode(LLImageRaw* raw_image, F32 decode_time)  	// Decode the PNG data into the raw image  	LLPngWrapper pngWrapper; +	if (!pngWrapper.isValidPng(getData())) +	{ +		setLastError("LLImagePNG data does not have a valid PNG header!"); +		return FALSE; +	} +  	if (! pngWrapper.readPng(getData(), raw_image))  	{  		setLastError(pngWrapper.getErrorMessage()); | 
