diff options
Diffstat (limited to 'indra/llimage')
-rw-r--r-- | indra/llimage/llimage.cpp | 65 | ||||
-rw-r--r-- | indra/llimage/llimage.h | 31 | ||||
-rw-r--r-- | indra/llimage/llimagedimensionsinfo.cpp | 83 | ||||
-rw-r--r-- | indra/llimage/llimagedimensionsinfo.h | 3 | ||||
-rw-r--r-- | indra/llimage/llimagedxt.cpp | 2 | ||||
-rw-r--r-- | indra/llimage/llimagej2c.cpp | 17 | ||||
-rw-r--r-- | indra/llimage/llimagej2c.h | 4 |
7 files changed, 164 insertions, 41 deletions
diff --git a/indra/llimage/llimage.cpp b/indra/llimage/llimage.cpp index 39211bf7fa..c239e3df88 100644 --- a/indra/llimage/llimage.cpp +++ b/indra/llimage/llimage.cpp @@ -39,6 +39,7 @@ #include "llimagepng.h" #include "llimagedxt.h" #include "llimageworker.h" +#include "llmemory.h" //--------------------------------------------------------------------------- // LLImage @@ -47,11 +48,14 @@ //static std::string LLImage::sLastErrorMessage; LLMutex* LLImage::sMutex = NULL; +LLPrivateMemoryPool* LLImageBase::sPrivatePoolp = NULL ; //static void LLImage::initClass() { sMutex = new LLMutex(NULL); + + LLImageBase::createPrivatePool() ; } //static @@ -59,6 +63,8 @@ void LLImage::cleanupClass() { delete sMutex; sMutex = NULL; + + LLImageBase::destroyPrivatePool() ; } //static @@ -97,6 +103,25 @@ 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() { @@ -130,7 +155,7 @@ void LLImageBase::sanityCheck() // virtual void LLImageBase::deleteData() { - delete[] mData; + FREE_MEM(sPrivatePoolp, mData) ; mData = NULL; mDataSize = 0; } @@ -167,7 +192,7 @@ U8* LLImageBase::allocateData(S32 size) { deleteData(); // virtual mBadBufferAllocation = false ; - mData = new U8[size]; + mData = (U8*)ALLOCATE_MEM(sPrivatePoolp, size); if (!mData) { llwarns << "allocate image data: " << size << llendl; @@ -185,7 +210,7 @@ U8* LLImageBase::allocateData(S32 size) 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; @@ -195,7 +220,7 @@ U8* LLImageBase::reallocateData(S32 size) { S32 bytes = llmin(mDataSize, size); memcpy(new_datap, mData, bytes); /* Flawfinder: ignore */ - delete[] mData; + FREE_MEM(sPrivatePoolp, mData) ; } mData = new_datap; mDataSize = size; @@ -341,6 +366,7 @@ BOOL LLImageRaw::resize(U16 width, U16 height, S8 components) return TRUE; } +#if 0 U8 * LLImageRaw::getSubImage(U32 x_pos, U32 y_pos, U32 width, U32 height) const { LLMemType mt1(mMemType); @@ -361,6 +387,7 @@ U8 * LLImageRaw::getSubImage(U32 x_pos, U32 y_pos, U32 width, U32 height) const } return data; } +#endif BOOL LLImageRaw::setSubImage(U32 x_pos, U32 y_pos, U32 width, U32 height, const U8 *data, U32 stride, BOOL reverse_y) @@ -830,6 +857,7 @@ void LLImageRaw::copyScaled( LLImageRaw* src ) } } +#if 0 //scale down image by not blending a pixel with its neighbors. BOOL LLImageRaw::scaleDownWithoutBlending( S32 new_width, S32 new_height) { @@ -853,7 +881,7 @@ BOOL LLImageRaw::scaleDownWithoutBlending( S32 new_width, S32 new_height) ratio_x -= 1.0f ; ratio_y -= 1.0f ; - U8* new_data = new U8[new_data_size] ; + U8* new_data = allocateMemory(new_data_size) ; llassert_always(new_data != NULL) ; U8* old_data = getData() ; @@ -875,6 +903,7 @@ BOOL LLImageRaw::scaleDownWithoutBlending( S32 new_width, S32 new_height) return TRUE ; } +#endif BOOL LLImageRaw::scale( S32 new_width, S32 new_height, BOOL scale_image_data ) { @@ -1254,28 +1283,7 @@ bool LLImageRaw::createFromFile(const std::string &filename, bool j2c_lowest_mip 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); @@ -1527,6 +1535,7 @@ void LLImageFormatted::setData(U8 *data, S32 size) { deleteData(); setDataAndSize(data, size); // Access private LLImageBase members + sGlobalFormattedMemory += getDataSize(); } } @@ -1545,7 +1554,7 @@ 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); } } } diff --git a/indra/llimage/llimage.h b/indra/llimage/llimage.h index 825b9aab1a..4469c9e860 100644 --- a/indra/llimage/llimage.h +++ b/indra/llimage/llimage.h @@ -29,14 +29,26 @@ #include "lluuid.h" #include "llstring.h" -//#include "llmemory.h" #include "llthread.h" #include "llmemtype.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_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; @@ -56,6 +68,7 @@ const S32 MAX_IMG_PACKET_SIZE = 1000; class LLImageFormatted; class LLImageRaw; class LLColor4U; +class LLPrivateMemoryPool; typedef enum e_image_codec { @@ -127,7 +140,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) { mData = data; mDataSize = size; } public: static void generateMip(const U8 *indata, U8* mipdata, int width, int height, S32 nchannels); @@ -138,6 +151,10 @@ public: static EImageCodec getCodecFromExtension(const std::string& exten); + static void createPrivatePool() ; + static void destroyPrivatePool() ; + static LLPrivateMemoryPool* getPrivatePool() {return sPrivatePoolp;} + private: U8 *mData; S32 mDataSize; @@ -149,6 +166,8 @@ private: bool mBadBufferAllocation ; bool mAllowOverSize ; + + static LLPrivateMemoryPool* sPrivatePoolp ; public: LLMemType::DeclareMemType& mMemType; // debug }; @@ -172,7 +191,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); @@ -184,7 +203,7 @@ public: 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) ; + //BOOL scaleDownWithoutBlending( S32 new_width, S32 new_height) ; // Fill the buffer with a constant color void fill( const LLColor4U& color ); @@ -266,13 +285,13 @@ 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); diff --git a/indra/llimage/llimagedimensionsinfo.cpp b/indra/llimage/llimagedimensionsinfo.cpp index 835664c60f..c6bfa50b40 100644 --- 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)) + { + llwarns << "Premature end of file" << llendl; + 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') + { + llwarns << "Not a BMP" << llendl; + 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 */)) + { + llwarns << "Premature end of file" << llendl; + 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 */)) + { + llwarns << "Premature end of file" << llendl; + 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) + { + llwarns << "Not a PNG" << llendl; + return false; + } + + // Read image dimensions. + mInfile.seek(APR_CUR, 8 /* chunk length + chunk type */); mWidth = read_s32(); mHeight = read_s32(); @@ -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 uint8_t jpeg_magic[JPEG_MAGIC_SIZE] = {0xFF, 0xD8}; + uint8_t signature[JPEG_MAGIC_SIZE]; + + if (fread(signature, sizeof(signature), 1, fp) != 1) + { + llwarns << "Premature end of file" << llendl; + return false; + } + if (memcmp(signature, jpeg_magic, JPEG_MAGIC_SIZE) != 0) + { + llwarns << "Not a JPEG" << llendl; + 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..382fdb2a0e 100644 --- a/indra/llimage/llimagedimensionsinfo.h +++ b/indra/llimage/llimagedimensionsinfo.h @@ -119,6 +119,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..2867f5e6f0 100644 --- a/indra/llimage/llimagedxt.cpp +++ b/indra/llimage/llimagedxt.cpp @@ -429,7 +429,7 @@ 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; diff --git a/indra/llimage/llimagej2c.cpp b/indra/llimage/llimagej2c.cpp index cb2a85fa91..c44a66f552 100644 --- a/indra/llimage/llimagej2c.cpp +++ b/indra/llimage/llimagej2c.cpp @@ -139,6 +139,15 @@ BOOL LLImageJ2C::updateData() return res; } +BOOL LLImageJ2C::initDecode(LLImageRaw &raw_image, int discard_level, int* region) +{ + 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) { @@ -251,6 +260,9 @@ 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) { @@ -373,14 +385,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; } @@ -474,6 +486,7 @@ LLImageCompressionTester::LLImageCompressionTester() : LLMetricPerformanceTester LLImageCompressionTester::~LLImageCompressionTester() { + outputTestResults(); LLImageJ2C::sTesterp = NULL; } diff --git a/indra/llimage/llimagej2c.h b/indra/llimage/llimagej2c.h index dd5bec8b2e..914174fc57 100644 --- a/indra/llimage/llimagej2c.h +++ b/indra/llimage/llimagej2c.h @@ -56,6 +56,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); @@ -117,6 +119,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; }; |