summaryrefslogtreecommitdiff
path: root/indra/llimage
diff options
context:
space:
mode:
Diffstat (limited to 'indra/llimage')
-rw-r--r--indra/llimage/llimage.cpp24
-rw-r--r--indra/llimage/llimage.h17
-rw-r--r--indra/llimage/llimagej2c.cpp85
-rw-r--r--indra/llimage/llimagej2c.h10
-rw-r--r--indra/llimage/llimagepng.cpp12
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());