diff options
| author | Andrey Lihatskiy <alihatskiy@productengine.com> | 2024-05-22 20:51:58 +0300 | 
|---|---|---|
| committer | GitHub <noreply@github.com> | 2024-05-22 20:51:58 +0300 | 
| commit | 6cc7dd09d5e69cf57e6de7fb568a0ad2693f9c9a (patch) | |
| tree | fab23811a5cedc1ebf01479c852ee92ff62b636c /indra/llkdu | |
| parent | ef8f4819822288e044ea719feb6af7a1f4df4c4e (diff) | |
| parent | 7bb5afc11ee5a6af78302a8d76a9a619e2baaab2 (diff) | |
Merge pull request #1545 from Ansariel/DRTVWR-600-maint-A
Merge main into DRTVWR-600-maint-a
Diffstat (limited to 'indra/llkdu')
| -rw-r--r-- | indra/llkdu/include_kdu_xxxx.h | 4 | ||||
| -rw-r--r-- | indra/llkdu/llimagej2ckdu.cpp | 2976 | ||||
| -rw-r--r-- | indra/llkdu/llimagej2ckdu.h | 158 | ||||
| -rw-r--r-- | indra/llkdu/llkdumem.cpp | 306 | ||||
| -rw-r--r-- | indra/llkdu/llkdumem.h | 180 | ||||
| -rw-r--r-- | indra/llkdu/tests/llimagej2ckdu_test.cpp | 164 | 
6 files changed, 1894 insertions, 1894 deletions
| diff --git a/indra/llkdu/include_kdu_xxxx.h b/indra/llkdu/include_kdu_xxxx.h index 61204b5689..3db417a6cb 100644 --- a/indra/llkdu/include_kdu_xxxx.h +++ b/indra/llkdu/include_kdu_xxxx.h @@ -2,8 +2,8 @@   * @file   include_kdu_xxxx.h   * @author Nat Goodspeed   * @date   2016-04-25 - * @brief   - *  + * @brief + *   * $LicenseInfo:firstyear=2016&license=viewerlgpl$   * Copyright (c) 2016, Linden Research, Inc.   * $/LicenseInfo$ diff --git a/indra/llkdu/llimagej2ckdu.cpp b/indra/llkdu/llimagej2ckdu.cpp index 8b34592535..1b4d76a035 100644 --- a/indra/llkdu/llimagej2ckdu.cpp +++ b/indra/llkdu/llimagej2ckdu.cpp @@ -1,1488 +1,1488 @@ -/**  - * @file llimagej2ckdu.cpp - * @brief This is an implementation of JPEG2000 encode/decode using Kakadu - * - * $LicenseInfo:firstyear=2010&license=viewerlgpl$ - * Second Life Viewer Source Code - * Copyright (C) 2010, Linden Research, Inc. - *  - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; - * version 2.1 of the License only. - *  - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU - * Lesser General Public License for more details. - *  - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA - *  - * Linden Research, Inc., 945 Battery Street, San Francisco, CA  94111  USA - * $/LicenseInfo$ - */ - -#include "linden_common.h" - -#include "llimagej2ckdu.h" - -#include "lltimer.h" -#include "llpointer.h" -#include "llmath.h" -#include "llkdumem.h" - -#define kdu_xxxx "kdu_block_coding.h" -#include "include_kdu_xxxx.h" - -// Avoid ubiquitous necessity of kdu_core:: qualification -using namespace kdu_core; - -#include "llexception.h" -#include <boost/exception/diagnostic_information.hpp> -#include <sstream> -#include <iomanip> - -// Turns out this must NOT be in the anonymous namespace! -namespace kdu_core -{ -// stream kdu_dims to std::ostream -inline -std::ostream& operator<<(std::ostream& out, const kdu_dims& dims) -{ -	return out << "(" << dims.pos.x << "," << dims.pos.y << ")," -				  "[" << dims.size.x << "x" << dims.size.y << "]"; -} -} // namespace kdu_core - -// operator<<(std::ostream&, const kdu_dims&) must precede #include "stringize.h" -#include "stringize.h" - -namespace { -// Failure to load an image shouldn't crash the whole viewer. -struct KDUError: public LLContinueError -{ -    KDUError(const std::string& msg): LLContinueError(msg) {} -}; - -// KDU defines int error codes as hex values, so we should log them in hex -// so we can grep KDU headers for the hex. However those hex values -// generally "happen" to encode big-endian multibyte character sequences, -// e.g. KDU_ERROR_EXCEPTION is 0x6b647545: 'kduE' -// But beware because KDU_NULL_EXCEPTION is simply 0 -- which doesn't -// preclude somebody from throwing it. -std::string report_kdu_exception(kdu_exception mb) -{ -    std::ostringstream out; -    // always report mb in hex -    out << "kdu_exception " << std::hex << mb; - -    // Also display as many chars as are encoded in the kdu_exception -    // value. Make a char array; reserve 1 extra byte for nul terminator. -    char bytes[sizeof(kdu_exception) + 1]; -    // Back up through 'bytes' -    char *bptr = bytes + sizeof(bytes); -    *(--bptr) = '\0'; -    while (mb) -    { -        // store low-order byte of mb in next-left char -        *(--bptr) = char(mb & 0xFF); -        // then shift mb right by one byte -        mb >>= 8; -    } -    // did that produce any characters? -    if (*bptr) -    { -        out << " (" << bptr << ')'; -    } - -    return out.str(); -} -} // anonymous namespace - - -class kdc_flow_control { -	 -public: -	kdc_flow_control(kdu_supp::kdu_image_in_base *img_in, kdu_codestream codestream); -	~kdc_flow_control(); -	bool advance_components(); -	void process_components(); -	 -private: -	 -	struct kdc_component_flow_control { -	public: -		kdu_supp::kdu_image_in_base *reader; -		int vert_subsampling; -		int ratio_counter;  /*  Initialized to 0, decremented by `count_delta'; -                                when < 0, a new line must be processed, after -                                which it is incremented by `vert_subsampling'.  */ -		int initial_lines; -		int remaining_lines; -		kdu_line_buf *line; -	}; -	 -	kdu_codestream codestream; -	kdu_dims valid_tile_indices; -	kdu_coords tile_idx; -	kdu_tile tile; -	int num_components; -	kdc_component_flow_control *components; -	int count_delta; // Holds the minimum of the `vert_subsampling' fields -	kdu_multi_analysis engine; -	kdu_long max_buffer_memory; -}; - -// -// Kakadu specific implementation -// -void set_default_colour_weights(kdu_params *siz); - -// Factory function: see declaration in llimagej2c.cpp -LLImageJ2CImpl* fallbackCreateLLImageJ2CImpl() -{ -	return new LLImageJ2CKDU(); -} - -std::string LLImageJ2CKDU::getEngineInfo() const -{ -	return llformat("KDU %s", KDU_CORE_VERSION); -} - -class LLKDUDecodeState -{ -public: -	LLKDUDecodeState(kdu_tile tile, kdu_byte *buf, S32 row_gap, -					 kdu_codestream* codestreamp); -	~LLKDUDecodeState(); -	bool processTileDecode(F32 decode_time, bool limit_time = true); - -private: -	S32 mNumComponents; -	bool mUseYCC; -	kdu_dims mDims; -	kdu_sample_allocator mAllocator; -	kdu_tile_comp mComps[4]; -	kdu_line_buf mLines[4]; -	kdu_pull_ifc mEngines[4]; -	bool mReversible[4]; // Some components may be reversible and others not -	int mBitDepths[4];   // Original bit-depth may be quite different from 8 -	 -	kdu_tile mTile; -	kdu_byte *mBuf; -	S32 mRowGap; -}; - -// Stuff for new kdu error handling -class LLKDUMessage: public kdu_message -{ -public: -	LLKDUMessage(const std::string& type): -		mType(type) -	{} - -	virtual void put_text(const char *s) -	{ -		LL_INFOS() << "KDU " << mType << ": " << s << LL_ENDL; -	} - -	virtual void put_text(const kdu_uint16 *s) -	{ -		// The previous implementation simply streamed 's' to the log. So -		// either this put_text() override was never called -- or it produced -		// some baffling log messages -- because I assert that streaming a -		// const kdu_uint16* to a std::ostream will display only the hex value -		// of the pointer. -		LL_INFOS() << "KDU " << mType << ": " -				   << utf16str_to_utf8str(llutf16string(s)) << LL_ENDL; -	} - -private: -	std::string mType; -}; - -struct LLKDUMessageWarning : public LLKDUMessage -{ -	LLKDUMessageWarning(): -		LLKDUMessage("Warning") -	{ -		kdu_customize_warnings(this); -	} -}; -// Instantiating LLKDUMessageWarning calls kdu_customize_warnings() with the -// new instance. Make it static so this only happens once. -static LLKDUMessageWarning sWarningHandler; - -struct LLKDUMessageError : public LLKDUMessage -{ -	LLKDUMessageError(): -		LLKDUMessage("Error") -	{ -		kdu_customize_errors(this); -	} - -	virtual void flush(bool end_of_message = false) -	{ -		// According to the documentation nat found: -		// http://pirlwww.lpl.arizona.edu/resources/guide/software/Kakadu/html_pages/globals__kdu$mize_errors.html -		// "If a kdu_error object is destroyed, handler→flush will be called with -		// an end_of_message argument equal to true and the process will -		// subsequently be terminated through exit. The termination may be -		// avoided, however, by throwing an exception from within the message -		// terminating handler→flush call." -		// So throwing an exception here isn't arbitrary: we MUST throw an -		// exception if we want to recover from a KDU error. -		// Because this confused me: the above quote specifically refers to -		// the kdu_error class, which is constructed internally within KDU at -		// the point where a fatal error is discovered and reported. It is NOT -		// talking about the kdu_message subclass passed to -		// kdu_customize_errors(). Destroying this static object at program -		// shutdown will NOT engage the behavior described above. -		if (end_of_message)  -		{ -			LLTHROW(KDUError("LLKDUMessageError::flush()")); -		} -	} -}; -// Instantiating LLKDUMessageError calls kdu_customize_errors() with the new -// instance. Make it static so this only happens once. -static LLKDUMessageError sErrorHandler; - -LLImageJ2CKDU::LLImageJ2CKDU() : LLImageJ2CImpl(), -	mInputp(), -	mCodeStreamp(), -	mTPosp(), -	mTileIndicesp(), -	mRawImagep(NULL), -	mDecodeState(), -	mBlocksSize(-1), -	mPrecinctsSize(-1), -	mLevels(0) -{ -} - -LLImageJ2CKDU::~LLImageJ2CKDU() -{ -	cleanupCodeStream(); // in case destroyed before decode completed -} - -// Stuff for new simple decode -void transfer_bytes(kdu_byte *dest, kdu_line_buf &src, int gap, int precision); - -// This is called by the real (private) initDecode() (keep_codestream true) -// and getMetadata() methods (keep_codestream false). As far as nat can tell, -// mode is always MODE_FAST. It was called by findDiscardLevelsBoundaries() -// as well, when that still existed, with keep_codestream true and MODE_FAST. -void LLImageJ2CKDU::setupCodeStream(LLImageJ2C &base, bool keep_codestream, ECodeStreamMode mode) -{ -	LLImageDataLock lock(&base); - -	S32 data_size = base.getDataSize(); -	S32 max_bytes = (base.getMaxBytes() ? base.getMaxBytes() : data_size); - -	// -	//  Initialization -	// -	mCodeStreamp.reset(); - -	// It's not clear to nat under what circumstances we would reuse a -	// pre-existing LLKDUMemSource instance. As of 2016-08-05, it consists of -	// two U32s and a pointer, so it's not as if it would be a huge overhead -	// to allocate a new one every time. -	// Also -- why is base.getData() tested specifically here? If that returns -	// NULL, shouldn't we bail out of the whole method? -	if (!mInputp && base.getData()) -	{ -		// The compressed data has been loaded -		// Setup the source for the codestream -		mInputp.reset(new LLKDUMemSource(base.getData(), data_size)); -	} - -	if (mInputp) -	{ -		// This is LLKDUMemSource::reset(), not boost::scoped_ptr::reset(). -		mInputp->reset(); -	} - -	mCodeStreamp->create(mInputp.get()); - -	// Set the maximum number of bytes to use from the codestream -	// *TODO: This seems to be wrong. The base class should have no idea of -	// how j2c compression works so no good way of computing what's the byte -	// range to be used. -	mCodeStreamp->set_max_bytes(max_bytes,true); - -	//	If you want to flip or rotate the image for some reason, change -	// the resolution, or identify a restricted region of interest, this is -	// the place to do it.  You may use "kdu_codestream::change_appearance" -	// and "kdu_codestream::apply_input_restrictions" for this purpose. -	//	If you wish to truncate the code-stream prior to decompression, you -	// may use "kdu_codestream::set_max_bytes". -	//	If you wish to retain all compressed data so that the material -	// can be decompressed multiple times, possibly with different appearance -	// parameters, you should call "kdu_codestream::set_persistent" here. -	//	There are a variety of other features which must be enabled at -	// this point if you want to take advantage of them.  See the -	// descriptions appearing with the "kdu_codestream" interface functions -	// in "kdu_compressed.h" for an itemized account of these capabilities. - -	switch (mode) -	{ -	case MODE_FAST: -		mCodeStreamp->set_fast(); -		break; -	case MODE_RESILIENT: -		mCodeStreamp->set_resilient(); -		break; -	case MODE_FUSSY: -		mCodeStreamp->set_fussy(); -		break; -	default: -		llassert(0); -		mCodeStreamp->set_fast(); -	} - -	kdu_dims dims; -	mCodeStreamp->get_dims(0,dims); - -	S32 components = mCodeStreamp->get_num_components(); - -	// Check that components have consistent dimensions (for PPM file) -	for (int idx = 1; idx < components; ++idx) -	{ -		kdu_dims other_dims; -		mCodeStreamp->get_dims(idx, other_dims); -		if (other_dims != dims) -		{ -			// This method is only called from methods that catch KDUError. -			// We want to fail the image load, not crash the viewer. -			LLTHROW(KDUError(STRINGIZE("Component " << idx << " dimensions " -									   << stringize(other_dims) -									   << " do not match component 0 dimensions " -									   << stringize(dims) << "!"))); -		} -	} - -	// Get the number of resolution levels in that image -	mLevels = mCodeStreamp->get_min_dwt_levels(); -	 -	// Set the base dimensions -	base.setSize(dims.size.x, dims.size.y, components); -	base.setLevels(mLevels); -	 -	if (!keep_codestream) -	{ -		mCodeStreamp.reset(); -		mInputp.reset(); -	} -} - -void LLImageJ2CKDU::cleanupCodeStream() -{ -    LL_PROFILE_ZONE_SCOPED_CATEGORY_TEXTURE; -	mInputp.reset(); -	mDecodeState.reset(); -	mCodeStreamp.reset(); -	mTPosp.reset(); -	mTileIndicesp.reset(); -} - -// This is the protected virtual method called by LLImageJ2C::initDecode(). -// However, as far as nat can tell, LLImageJ2C::initDecode() is called only by -// llimage_libtest.cpp's load_image() function. No detectable production use. -bool LLImageJ2CKDU::initDecode(LLImageJ2C &base, LLImageRaw &raw_image, int discard_level, int* region) -{ -	return initDecode(base,raw_image,0.0f,MODE_FAST,0,4,discard_level,region); -} - -bool LLImageJ2CKDU::initEncode(LLImageJ2C &base, LLImageRaw &raw_image, int blocks_size, int precincts_size, int levels) -{ -	mPrecinctsSize = precincts_size; -	if (mPrecinctsSize != -1) -	{ -		mPrecinctsSize = get_lower_power_two(mPrecinctsSize,MAX_PRECINCT_SIZE); -		mPrecinctsSize = llmax(mPrecinctsSize,MIN_PRECINCT_SIZE); -	} -	mBlocksSize = blocks_size; -	if (mBlocksSize != -1) -	{ -		mBlocksSize = get_lower_power_two(mBlocksSize,MAX_BLOCK_SIZE); -		mBlocksSize = llmax(mBlocksSize,MIN_BLOCK_SIZE); -		if (mPrecinctsSize != -1) -		{ -			mBlocksSize = llmin(mBlocksSize,mPrecinctsSize);	// blocks *must* be smaller than precincts -		} -	} -	mLevels = levels; -	if (mLevels != 0) -	{ -		mLevels = llclamp(mLevels,MIN_DECOMPOSITION_LEVELS,MAX_DECOMPOSITION_LEVELS); -		base.setLevels(mLevels); -	} -	return true; -} - -// This is the real (private) initDecode() called both by the protected -// initDecode() method and by decodeImpl(). As far as nat can tell, only the -// decodeImpl() usage matters for production. -bool LLImageJ2CKDU::initDecode(LLImageJ2C &base, LLImageRaw &raw_image, F32 decode_time, ECodeStreamMode mode, S32 first_channel, S32 max_channel_count, int discard_level, int* region) -{ -    LL_PROFILE_ZONE_SCOPED_CATEGORY_TEXTURE; -	base.resetLastError(); - -	// *FIX: kdu calls our callback function if there's an error, and then bombs. -	// To regain control, we throw an exception, and catch it here. -	try -	{ -		// Merov : Test!! DO NOT COMMIT!! -		//findDiscardLevelsBoundaries(base); - -		base.updateRawDiscardLevel(); -		setupCodeStream(base, true, mode); - -		mRawImagep = &raw_image; -		mCodeStreamp->change_appearance(false, true, false); - -		// Apply loading discard level and cropping if required -		kdu_dims* region_kdu = NULL; -		if (region != NULL) -		{ -			region_kdu = new kdu_dims; -			region_kdu->pos.x  = region[0]; -			region_kdu->pos.y  = region[1]; -			region_kdu->size.x = region[2] - region[0]; -			region_kdu->size.y = region[3] - region[1]; -		} -		int discard = (discard_level != -1 ? discard_level : base.getRawDiscardLevel()); -		//LL_INFOS() << "Merov debug : initDecode, discard used = " << discard << ", asked = " << discard_level << LL_ENDL; -		// Apply loading restrictions -		mCodeStreamp->apply_input_restrictions( first_channel, max_channel_count, discard, 0, region_kdu); -		 -		// Clean-up -		if (region_kdu) -		{ -			delete region_kdu; -			region_kdu = NULL; -		} - -		// Resize raw_image according to the image to be decoded -		kdu_dims dims; mCodeStreamp->get_dims(0,dims); -		S32 channels = base.getComponents() - first_channel; -		channels = llmin(channels,max_channel_count); -		raw_image.resize(dims.size.x, dims.size.y, channels); - -		if (!mTileIndicesp) -		{ -			mTileIndicesp.reset(new kdu_dims); -		} -		mCodeStreamp->get_valid_tiles(*mTileIndicesp); -		if (!mTPosp) -		{ -			mTPosp.reset(new kdu_coords); -			mTPosp->y = 0; -			mTPosp->x = 0; -		} -	} -	catch (const KDUError& msg) -	{ -		base.setLastError(msg.what()); -		return false; -	} -	catch (kdu_exception kdu_value) -	{ -		// KDU internally throws kdu_exception. It's possible that such an -		// exception might leak out into our code. Catch kdu_exception -		// specially because boost::current_exception_diagnostic_information() -		// could do nothing with it. -		base.setLastError(report_kdu_exception(kdu_value)); -		return false; -	} -	catch (...) -	{ -		base.setLastError("Unknown J2C error: " + -						  boost::current_exception_diagnostic_information()); -		return false; -	} - -	return true; -} - - -// Returns true to mean done, whether successful or not. -bool LLImageJ2CKDU::decodeImpl(LLImageJ2C &base, LLImageRaw &raw_image, F32 decode_time, S32 first_channel, S32 max_channel_count) -{ -    LL_PROFILE_ZONE_SCOPED_CATEGORY_TEXTURE; - -	LLImageDataLock lockIn(&base); -	LLImageDataLock lockOut(&raw_image); - -	ECodeStreamMode mode = MODE_FAST; - -	bool limit_time = decode_time > 0.0f; -	LLTimer decode_timer; - -	if (!mCodeStreamp->exists()) -	{ -		if (!initDecode(base, raw_image, decode_time, mode, first_channel, max_channel_count)) -		{ -			// Initializing the J2C decode failed, bail out. -			cleanupCodeStream(); -			return true; // done -		} -	} - -	// These can probably be grabbed from what's saved in the class. -	kdu_dims dims; -	mCodeStreamp->get_dims(0, dims); - -	// Now we are ready to walk through the tiles processing them one-by-one. -	kdu_byte *buffer = raw_image.getData(); -	if (!buffer) -	{ -		base.setLastError("Memory error"); -		base.decodeFailed(); -		cleanupCodeStream(); -		return true; // done -	} - -	while (mTPosp->y < mTileIndicesp->size.y) -	{ -		while (mTPosp->x < mTileIndicesp->size.x) -		{ -			try -			{ -				if (!mDecodeState) -				{ -					kdu_tile tile = mCodeStreamp->open_tile(*(mTPosp)+mTileIndicesp->pos); - -					// 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 -					// during compression and so it can happen that tiles -					// (at the image component level) actually have -					// different dimensions.  For this reason, we cannot -					// figure out the buffer region occupied by a tile -					// directly from the tile indices.  Instead, we query -					// the highest resolution of the first tile-component -					// concerning its location and size on the canvas -- -					// the `dims' object already holds the location and -					// size of the entire image component on the same -					// canvas coordinate system.  Comparing the two tells -					// us where the current tile is in the buffer. -					S32 channels = base.getComponents() - first_channel; -					if (channels > max_channel_count) -					{ -						channels = max_channel_count; -					} -					kdu_resolution res = tile.access_component(0).access_resolution(); -					kdu_dims tile_dims; res.get_dims(tile_dims); -					kdu_coords offset = tile_dims.pos - dims.pos; -					int row_gap = channels*dims.size.x; // inter-row separation -					kdu_byte *buf = buffer + offset.y*row_gap + offset.x*channels; -					mDecodeState.reset(new LLKDUDecodeState(tile, buf, row_gap, -															mCodeStreamp.get())); -				} -				// Do the actual processing -				F32 remaining_time = limit_time ? decode_time - decode_timer.getElapsedTimeF32().value() : 0.0f; -				// This is where we do the actual decode.  If we run out of time, return false. -				if (mDecodeState->processTileDecode(remaining_time, limit_time)) -				{ -					mDecodeState.reset(); -				} -				else -				{ -					// Not finished decoding yet. -					base.setLastError("Ran out of time while decoding"); -					base.decodeFailed(); -					cleanupCodeStream(); -					return false; -				} -			} -			catch (const KDUError& msg) -			{ -				base.setLastError(msg.what()); -				base.decodeFailed(); -				cleanupCodeStream(); -				return true; // done -			} -			catch (kdu_exception kdu_value) -			{ -				// KDU internally throws kdu_exception. It's possible that such an -				// exception might leak out into our code. Catch kdu_exception -				// specially because boost::current_exception_diagnostic_information() -				// could do nothing with it. -				base.setLastError(report_kdu_exception(kdu_value)); -				base.decodeFailed(); -				cleanupCodeStream(); -				return true; // done -			} -			catch (...) -			{ -				base.setLastError("Unknown J2C error: " + -								  boost::current_exception_diagnostic_information()); -				base.decodeFailed(); -				cleanupCodeStream(); -				return true; // done -			} - - -			mTPosp->x++; -		} -		mTPosp->y++; -		mTPosp->x = 0; -	} - -	cleanupCodeStream(); - -	return true; -} - - -bool LLImageJ2CKDU::encodeImpl(LLImageJ2C &base, const LLImageRaw &raw_image, const char* comment_text, F32 encode_time, bool reversible) -{ -	// Declare and set simple arguments -	bool transpose = false; -	bool vflip = true; -	bool hflip = false; - -	try -	{ -		// Set up input image files -		siz_params siz; -		 -		// Should set rate someplace here -		LLKDUMemIn mem_in(raw_image.getData(), -			raw_image.getDataSize(), -			raw_image.getWidth(), -			raw_image.getHeight(), -			raw_image.getComponents(), -			&siz); - -		base.setSize(raw_image.getWidth(), raw_image.getHeight(), raw_image.getComponents()); - -		int num_components = raw_image.getComponents(); - -		siz.set(Scomponents,0,0,num_components); -		siz.set(Sdims,0,0,base.getHeight());  // Height of first image component -		siz.set(Sdims,0,1,base.getWidth());   // Width of first image component -		siz.set(Sprecision,0,0,8);  // Image samples have original bit-depth of 8 -		siz.set(Ssigned,0,0,false); // Image samples are originally unsigned - -		kdu_params *siz_ref = &siz;  -		siz_ref->finalize(); -		siz_params transformed_siz; // Use this one to construct code-stream -		transformed_siz.copy_from(&siz,-1,-1,-1,0,transpose,false,false); - -		// Construct the `kdu_codestream' object and parse all remaining arguments -		U32 max_output_size = base.getWidth()*base.getHeight()*base.getComponents(); -		max_output_size = (max_output_size < 1000 ? 1000 : max_output_size); -		U8 *output_buffer = new U8[max_output_size]; -		U32 output_size = 0; // Address updated by LLKDUMemTarget to give the final compressed buffer size -		LLKDUMemTarget output(output_buffer, output_size, max_output_size); - -		kdu_codestream codestream; -		codestream.create(&transformed_siz,&output); - -		if (comment_text) -		{ -			// Set the comments for the codestream -			kdu_codestream_comment comment = codestream.add_comment(); -			comment.put_text(comment_text); -		} - -		if (num_components >= 3) -		{ -			// Note that we always use YCC and not YUV -			// *TODO: Verify this doesn't screws up reversible textures (like sculpties) as YCC is not reversible but YUV is... -			set_default_colour_weights(codestream.access_siz()); -		} - -		// Set codestream options -		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. -		llassert (base.mRate > 0.f); -		max_bytes = (U32)((F32)(max_bytes) * base.mRate); -		 -		// This code is where we specify the target number of bytes for each quality layer. -		// 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 -		layer_bytes[nb_layers++] = FIRST_PACKET_SIZE; -		U32 i = MIN_LAYER_SIZE; -		while ((i < max_bytes) && (nb_layers < (MAX_NB_LAYERS-1))) -		{ -			layer_bytes[nb_layers++] = i; -			i *= 4; -		} -		// Note: for small images, we can have (max_bytes < FIRST_PACKET_SIZE), hence the test -		if (layer_bytes[nb_layers-1] < max_bytes) -		{ -			// Set the last quality layer so to fit the preset compression ratio -			layer_bytes[nb_layers++] = max_bytes; -		} - -		if (reversible) -		{ -			// Use 0 for a last quality layer for reversible images so all remaining code blocks will be flushed -			// Hack: KDU encoding for reversible images has a bug for small images that leads to j2c images that  -			// cannot be open or are very blurry. Avoiding that last layer prevents the problem to happen. -			if ((base.getWidth() >= 32) || (base.getHeight() >= 32)) -			{ -				layer_bytes[nb_layers++] = 0; -			} -			codestream.access_siz()->parse_string("Creversible=yes"); -			// *TODO: we should use yuv in reversible mode -			// Don't turn this on now though as it creates problems on decoding for the moment -			//codestream.access_siz()->parse_string("Cycc=no"); -		} -		 -		std::string layer_string = llformat("Clayers=%d",nb_layers); -		codestream.access_siz()->parse_string(layer_string.c_str()); -		 -		// Set up data ordering, markers, etc... if precincts or blocks specified -		if ((mBlocksSize != -1) || (mPrecinctsSize != -1)) -		{ -			if (mPrecinctsSize != -1) -			{ -				std::string precincts_string = llformat("Cprecincts={%d,%d}",mPrecinctsSize,mPrecinctsSize); -				codestream.access_siz()->parse_string(precincts_string.c_str()); -			} -			if (mBlocksSize != -1) -			{ -				std::string blocks_string = llformat("Cblk={%d,%d}",mBlocksSize,mBlocksSize); -				codestream.access_siz()->parse_string(blocks_string.c_str()); -			} -			std::string ordering_string = llformat("Corder=LRCP"); -			codestream.access_siz()->parse_string(ordering_string.c_str()); -			std::string PLT_string = llformat("ORGgen_plt=yes"); -			codestream.access_siz()->parse_string(PLT_string.c_str()); -			std::string Parts_string = llformat("ORGtparts=R"); -			codestream.access_siz()->parse_string(Parts_string.c_str()); -		} -		 -		// Set the number of wavelets subresolutions (aka levels)  -		if (mLevels != 0) -		{ -			std::string levels_string = llformat("Clevels=%d",mLevels); -			codestream.access_siz()->parse_string(levels_string.c_str()); -		} -		 -		// Complete the encode settings -		codestream.access_siz()->finalize_all(); -		codestream.change_appearance(transpose,vflip,hflip); - -		// Now we are ready for sample data processing -		kdc_flow_control *tile = new kdc_flow_control(&mem_in,codestream); -		bool done = false; -		while (!done) -		{  -			// Process line by line -			if (tile->advance_components()) -			{ -				tile->process_components(); -			} -			else -			{ -				done = true; -			} -		} - -		// Produce the compressed output -		codestream.flush(layer_bytes,nb_layers); - -		// Cleanup -		delete tile; -		codestream.destroy(); - -		// Now that we're done encoding, create the new data buffer for the compressed -		// image and stick it there. -		base.copyData(output_buffer, output_size); -		base.updateData(); // set width, height -		delete[] output_buffer; -	} -	catch(const KDUError& msg) -	{ -		base.setLastError(msg.what()); -		return false; -	} -	catch (kdu_exception kdu_value) -	{ -		// KDU internally throws kdu_exception. It's possible that such an -		// exception might leak out into our code. Catch kdu_exception -		// specially because boost::current_exception_diagnostic_information() -		// could do nothing with it. -		base.setLastError(report_kdu_exception(kdu_value)); -		return false; -	} -	catch( ... ) -	{ -		base.setLastError("Unknown J2C error: " + -						  boost::current_exception_diagnostic_information()); -		return false; -	} - -	return true; -} - -bool LLImageJ2CKDU::getMetadata(LLImageJ2C &base) -{ -	// *FIX: kdu calls our callback function if there's an error, and -	// then bombs. To regain control, we throw an exception, and -	// catch it here. -	try -	{ -		setupCodeStream(base, false, MODE_FAST); -		return true; -	} -	catch (const KDUError& msg) -	{ -		base.setLastError(msg.what()); -		return false; -	} -	catch (kdu_exception kdu_value) -	{ -		// KDU internally throws kdu_exception. It's possible that such an -		// exception might leak out into our code. Catch kdu_exception -		// specially because boost::current_exception_diagnostic_information() -		// could do nothing with it. -		base.setLastError(report_kdu_exception(kdu_value)); -		return false; -	} -	catch (...) -	{ -		base.setLastError("Unknown J2C error: " + -						  boost::current_exception_diagnostic_information()); -		return false; -	} -} - -/*****************************************************************************/ -/* STATIC                        copy_block                                  */ -/*****************************************************************************/ - -/*==========================================================================*| -// Only called by copy_tile(), which is itself commented out -static void copy_block(kdu_block *in, kdu_block *out) -{ -	if (in->K_max_prime != out->K_max_prime) -    {  -		std::cout << "Cannot copy blocks belonging to subbands with different quantization parameters." << std::endl;  -		return; -	} -	if ((in->size.x != out->size.x) || (in->size.y != out->size.y))   -    {  -		std::cout << "Cannot copy code-blocks with different dimensions." << std::endl;  -		return; -	} -	out->missing_msbs = in->missing_msbs; -	if (out->max_passes < (in->num_passes+2))        // Gives us enough to round up -		out->set_max_passes(in->num_passes+2,false); // to the next whole bit-plane -	out->num_passes = in->num_passes; -	int num_bytes = 0; -	for (int z=0; z < in->num_passes; z++) -    { -		num_bytes += (out->pass_lengths[z] = in->pass_lengths[z]); -		out->pass_slopes[z] = in->pass_slopes[z]; -    } -	 -    // Just copy compressed code-bytes. Block transcoding not supported. -	if (out->max_bytes < num_bytes) -		out->set_max_bytes(num_bytes,false); -	memcpy(out->byte_buffer,in->byte_buffer,(size_t) num_bytes); -} -|*==========================================================================*/ - -/*****************************************************************************/ -/* STATIC                        copy_tile                                   */ -/*****************************************************************************/ - -/*==========================================================================*| -// Only called by findDiscardLevelsBoundaries(), which is itself commented out -static void -copy_tile(kdu_tile tile_in, kdu_tile tile_out, int tnum_in, int tnum_out, -		  kdu_params *siz_in, kdu_params *siz_out, int skip_components, -		  int &num_blocks) -{ -	int num_components = tile_out.get_num_components(); -	int new_tpart=0, next_tpart = 1; -	 -	for (int c=0; c < num_components; c++) -    { -		kdu_tile_comp comp_in, comp_out; -		comp_in = tile_in.access_component(c); -		comp_out = tile_out.access_component(c); -		int num_resolutions = comp_out.get_num_resolutions(); -		//std::cout << "    Copying tile : num_resolutions = " << num_resolutions << std::endl; -		for (int r=0; r < num_resolutions; r++) -        { -			kdu_resolution res_in;  res_in = comp_in.access_resolution(r); -			kdu_resolution res_out; res_out = comp_out.access_resolution(r); -			int b, min_band; -			int num_bands = res_in.get_valid_band_indices(min_band); -			std::cout << "        Copying tile : num_bands = " << num_bands << std::endl; -			for (b=min_band; num_bands > 0; num_bands--, b++) -            { -				kdu_subband band_in;  band_in = res_in.access_subband(b); -				kdu_subband band_out; band_out = res_out.access_subband(b); -				kdu_dims blocks_in;  band_in.get_valid_blocks(blocks_in); -				kdu_dims blocks_out; band_out.get_valid_blocks(blocks_out); -				if ((blocks_in.size.x != blocks_out.size.x) || -					(blocks_in.size.y != blocks_out.size.y)) -                {  -					std::cout << "Transcoding operation cannot proceed: Code-block partitions for the input and output code-streams do not agree." << std::endl; -					return; -				} -				kdu_coords idx; -				//std::cout << "            Copying tile : block indices, x = " << blocks_out.size.x << " and y = " << blocks_out.size.y << std::endl; -				for (idx.y=0; idx.y < blocks_out.size.y; idx.y++) -				{ -					for (idx.x=0; idx.x < blocks_out.size.x; idx.x++) -					{ -						kdu_block *in = -						band_in.open_block(idx+blocks_in.pos,&new_tpart); -						for (; next_tpart <= new_tpart; next_tpart++) -							siz_out->copy_from(siz_in,tnum_in,tnum_out,next_tpart, -											   skip_components); -						kdu_block *out = band_out.open_block(idx+blocks_out.pos); -						copy_block(in,out); -						band_in.close_block(in); -						band_out.close_block(out); -						num_blocks++; -					} -				} -            } -        } -    } -} -|*==========================================================================*/ - -// Find the block boundary for each discard level in the input image. -// We parse the input blocks and copy them in a temporary output stream. -// For the moment, we do nothing more that parsing the raw list of blocks and outputing result. -/*==========================================================================*| -// See comments in header file for why this is commented out. -void LLImageJ2CKDU::findDiscardLevelsBoundaries(LLImageJ2C &base) -{ -	// We need the number of levels in that image before starting. -	getMetadata(base); -	 -	for (int discard_level = 0; discard_level < mLevels; discard_level++) -	{ -		//std::cout << "Parsing discard level = " << discard_level << std::endl; -		// Create the input codestream object. -		setupCodeStream(base, true, MODE_FAST); -		mCodeStreamp->apply_input_restrictions(0, 4, discard_level, 0, NULL); -		mCodeStreamp->set_max_bytes(KDU_LONG_MAX,true); -		siz_params *siz_in = mCodeStreamp->access_siz(); -	 -		// Create the output codestream object. -		siz_params siz; -		siz.copy_from(siz_in,-1,-1,-1,0,discard_level,false,false,false); -		siz.set(Scomponents,0,0,mCodeStreamp->get_num_components()); -	 -		U32 max_output_size = base.getWidth()*base.getHeight()*base.getComponents(); -		max_output_size = (max_output_size < 1000 ? 1000 : max_output_size); -		U8 *output_buffer = new U8[max_output_size]; -		U32 output_size = 0; // Address updated by LLKDUMemTarget to give the final compressed buffer size -		LLKDUMemTarget output(output_buffer, output_size, max_output_size); -		kdu_codestream codestream_out;  -		codestream_out.create(&siz,&output); -		//codestream_out.share_buffering(*mCodeStreamp); -		siz_params *siz_out = codestream_out.access_siz(); -		siz_out->copy_from(siz_in,-1,-1,-1,0,discard_level,false,false,false); -		codestream_out.access_siz()->finalize_all(-1); -	 -		// Set up rate control variables -		kdu_long max_bytes = KDU_LONG_MAX; -		kdu_params *cod = siz_out->access_cluster(COD_params); -		int total_layers;  cod->get(Clayers,0,0,total_layers); -		kdu_long *layer_bytes = new kdu_long[total_layers]; -		int nel, non_empty_layers = 0; -	 -		// Now ready to perform the transfer of compressed data between streams -		int flush_counter = INT_MAX; -		kdu_dims tile_indices_in;   -		mCodeStreamp->get_valid_tiles(tile_indices_in); -		kdu_dims tile_indices_out;  -		codestream_out.get_valid_tiles(tile_indices_out); -		assert((tile_indices_in.size.x == tile_indices_out.size.x) && -			   (tile_indices_in.size.y == tile_indices_out.size.y)); -		int num_blocks=0; -	 -		kdu_coords idx; -		//std::cout << "Parsing tiles : x = " << tile_indices_out.size.x << " to y = " << tile_indices_out.size.y << std::endl; -		for (idx.y=0; idx.y < tile_indices_out.size.y; idx.y++) -		{ -			for (idx.x=0; idx.x < tile_indices_out.size.x; idx.x++) -			{ -				kdu_tile tile_in = mCodeStreamp->open_tile(idx+tile_indices_in.pos); -				int tnum_in = tile_in.get_tnum(); -				int tnum_out = idx.x + idx.y*tile_indices_out.size.x; -				siz_out->copy_from(siz_in,tnum_in,tnum_out,0,0,discard_level,false,false,false); -				siz_out->finalize_all(tnum_out); -				// Note: do not open the output tile without first copying any tile-specific code-stream parameters -				kdu_tile tile_out = codestream_out.open_tile(idx+tile_indices_out.pos); -				assert(tnum_out == tile_out.get_tnum()); -				copy_tile(tile_in,tile_out,tnum_in,tnum_out,siz_in,siz_out,0,num_blocks); -				tile_in.close(); -				tile_out.close(); -				flush_counter--; -				if ((flush_counter <= 0) && codestream_out.ready_for_flush()) -				{ -					flush_counter = INT_MAX; -					nel = codestream_out.trans_out(max_bytes,layer_bytes,total_layers); -					non_empty_layers = (nel > non_empty_layers)?nel:non_empty_layers; -				} -			} -		} -	 -		// Generate the output code-stream -		if (codestream_out.ready_for_flush()) -		{ -			nel = codestream_out.trans_out(max_bytes,layer_bytes,total_layers); -			non_empty_layers = (nel > non_empty_layers)?nel:non_empty_layers; -		} -		if (non_empty_layers > total_layers) -			non_empty_layers = total_layers; // Can happen if a tile has more layers -	 -		// Print out stats -		std::cout << "Code stream parsing for discard level = " << discard_level << std::endl; -		std::cout << "    Total compressed memory in  = " << mCodeStreamp->get_compressed_data_memory() << " bytes" << std::endl; -		std::cout << "    Total compressed memory out = " << codestream_out.get_compressed_data_memory() << " bytes" << std::endl; -		//std::cout << "    Output contains " << total_layers << " quality layers" << std::endl;		 -		std::cout << "    Transferred " << num_blocks << " code-blocks from in to out" << std::endl; -		//std::cout << "    Read " << mCodeStreamp->get_num_tparts() << " tile-part(s) from a total of " << (int) tile_indices_in.area() << " tile(s)" << std::endl; -		std::cout << "    Total bytes read = " << mCodeStreamp->get_total_bytes() << std::endl; -		//std::cout << "    Wrote " << codestream_out.get_num_tparts() << " tile-part(s) in a total of " << (int) tile_indices_out.area() << " tile(s)" << std::endl; -		std::cout << "    Total bytes written = " << codestream_out.get_total_bytes() << std::endl; -		std::cout << "-------------" << std::endl; -	 -		// Clean-up -		cleanupCodeStream(); -		codestream_out.destroy(); -		delete[] output_buffer;	 -	} -	return; -} -|*==========================================================================*/ - -void set_default_colour_weights(kdu_params *siz) -{ -	kdu_params *cod = siz->access_cluster(COD_params); -	assert(cod != NULL); - -	bool can_use_ycc = true; -	bool rev0 = false; -	int depth0 = 0, sub_x0 = 1, sub_y0 = 1; -	for (int c = 0; c < 3; c++) -	{ -		int depth = 0; siz->get(Sprecision,c,0,depth); -		int sub_y = 1; siz->get(Ssampling,c,0,sub_y); -		int sub_x = 1; siz->get(Ssampling,c,1,sub_x); -		kdu_params *coc = cod->access_relation(-1,c); -		bool rev = false; coc->get(Creversible,0,0,rev); -		if (c == 0) -		{ -			rev0 = rev; depth0 = depth; sub_x0 = sub_x; sub_y0 = sub_y; -		} -		else if ((rev != rev0) || (depth != depth0) ||  -				 (sub_x != sub_x0) || (sub_y != sub_y0)) -		{ -			can_use_ycc = false; -		} -	} -	if (!can_use_ycc) -	{ -		return; -	} - -	bool use_ycc; -	if (!cod->get(Cycc,0,0,use_ycc)) -	{ -		cod->set(Cycc,0,0,use_ycc=true); -	} -	if (!use_ycc) -	{ -		return; -	} -	float weight; -	if (cod->get(Clev_weights,0,0,weight) || cod->get(Cband_weights,0,0,weight)) -	{ -		// Weights already specified explicitly -> nothing to do -		return;  -	} - -	// These example weights are adapted from numbers generated by Marcus Nadenau -	// at EPFL, for a viewing distance of 15 cm and a display resolution of -	// 300 DPI. - -	cod->parse_string("Cband_weights:C0=" -		"{0.0901},{0.2758},{0.2758}," -		"{0.7018},{0.8378},{0.8378},{1}"); -	cod->parse_string("Cband_weights:C1=" -		"{0.0263},{0.0863},{0.0863}," -		"{0.1362},{0.2564},{0.2564}," -		"{0.3346},{0.4691},{0.4691}," -		"{0.5444},{0.6523},{0.6523}," -		"{0.7078},{0.7797},{0.7797},{1}"); -	cod->parse_string("Cband_weights:C2=" -		"{0.0773},{0.1835},{0.1835}," -		"{0.2598},{0.4130},{0.4130}," -		"{0.5040},{0.6464},{0.6464}," -		"{0.7220},{0.8254},{0.8254}," -		"{0.8769},{0.9424},{0.9424},{1}"); -} - -/******************************************************************************/ -/*                              transfer_bytes                                */ -/******************************************************************************/ - -void transfer_bytes(kdu_byte *dest, kdu_line_buf &src, int gap, int precision) -/* Transfers source samples from the supplied line buffer into the output -byte buffer, spacing successive output samples apart by `gap' bytes -(to allow for interleaving of colour components).  The function performs -all necessary level shifting, type conversion, rounding and truncation. */ -{ -	int width = src.get_width(); -	if (src.get_buf32() != NULL) -	{ // Decompressed samples have a 32-bit representation (integer or float) -		assert(precision >= 8); // Else would have used 16 bit representation -		kdu_sample32 *sp = src.get_buf32(); -		if (!src.is_absolute()) -		{ // Transferring normalized floating point data. -			float scale16 = (float)(1<<16); -			kdu_int32 val; - -			for (; width > 0; width--, sp++, dest+=gap) -			{ -				val = (kdu_int32)(sp->fval*scale16); -				val = (val+128)>>8; // May be faster than true rounding -				val += 128; -				if (val & ((0xffffffffU)<<8)) -				{ -					val = (val < 0 ? 0 : 255); -				} -				*dest = (kdu_byte) val; -			} -		} -		else -		{ // Transferring 32-bit absolute integers. -			kdu_int32 val; -			kdu_int32 downshift = precision-8; -			kdu_int32 offset = (1<<downshift)>>1; - -			for (; width > 0; width--, sp++, dest+=gap) -			{ -				val = sp->ival; -				val = (val+offset)>>downshift; -				val += 128; -				if (val & ((0xffffffffU)<<8)) -				{ -					val = (val < 0 ? 0 : 255); -				} -				*dest = (kdu_byte) val; -			} -		} -	} -	else -	{ // Source data is 16 bits. -		kdu_sample16 *sp = src.get_buf16(); -		if (!src.is_absolute()) -		{ // Transferring 16-bit fixed point quantities -			kdu_int16 val; - -			if (precision >= 8) -			{ // Can essentially ignore the bit-depth. -				for (; width > 0; width--, sp++, dest+=gap) -				{ -					val = sp->ival; -					val += (1<<(KDU_FIX_POINT-8))>>1; -					val >>= (KDU_FIX_POINT-8); -					val += 128; -					if (val & ((0xffffffffU)<<8)) -					{ -						val = (val < 0 ? 0 : 255); -					} -					*dest = (kdu_byte) val; -				} -			} -			else -			{ // Need to force zeros into one or more least significant bits. -				kdu_int16 downshift = KDU_FIX_POINT-precision; -				kdu_int16 upshift = 8-precision; -				kdu_int16 offset = 1<<(downshift-1); - -				for (; width > 0; width--, sp++, dest+=gap) -				{ -					val = sp->ival; -					val = (val+offset)>>downshift; -					val <<= upshift; -					val += 128; -					if (val & ((0xffffffffU)<<8)) -					{ -						val = (val < 0 ? 0 : 256 - (1<<upshift)); -					} -					*dest = (kdu_byte) val; -				} -			} -		} -		else -		{ // Transferring 16-bit absolute integers. -			kdu_int16 val; - -			if (precision >= 8) -			{ -				kdu_int16 downshift = precision-8; -				kdu_int16 offset = (1<<downshift)>>1; - -				for (; width > 0; width--, sp++, dest+=gap) -				{ -					val = sp->ival; -					val = (val+offset)>>downshift; -					val += 128; -					if (val & ((0xffffffffU)<<8)) -					{ -						val = (val < 0 ? 0 : 255); -					} -					*dest = (kdu_byte) val; -				} -			} -			else -			{ -				kdu_int16 upshift = 8-precision; - -				for (; width > 0; width--, sp++, dest+=gap) -				{ -					val = sp->ival; -					val <<= upshift; -					val += 128; -					if (val & ((0xffffffffU)<<8)) -					{ -						val = (val < 0 ? 0 : 256 - (1<<upshift)); -					} -					*dest = (kdu_byte) val; -				} -			} -		} -	} -} - -LLKDUDecodeState::LLKDUDecodeState(kdu_tile tile, kdu_byte *buf, S32 row_gap, -								   kdu_codestream* codestreamp) -{ -	S32 c; - -	mTile = tile; -	mBuf = buf; -	mRowGap = row_gap; - -	mNumComponents = tile.get_num_components(); - -	llassert(mNumComponents <= 4); -	mUseYCC = tile.get_ycc(); - -	for (c = 0; c < 4; ++c) -	{ -		mReversible[c] = false; -		mBitDepths[c] = 0; -	} - -	// Open tile-components and create processing engines and resources -	for (c = 0; c < mNumComponents; c++) -	{ -		mComps[c] = mTile.access_component(c); -		mReversible[c] = mComps[c].get_reversible(); -		mBitDepths[c] = mComps[c].get_bit_depth(); -		kdu_resolution res = mComps[c].access_resolution(); // Get top resolution -		kdu_dims comp_dims; res.get_dims(comp_dims); -		if (c == 0) -		{ -			mDims = comp_dims; -		} -		else -		{ -			llassert(mDims == comp_dims); // Safety check; the caller has ensured this -		} -		bool use_shorts = (mComps[c].get_bit_depth(true) <= 16); -		mLines[c].pre_create(&mAllocator,mDims.size.x,mReversible[c],use_shorts,0,0); -		if (res.which() == 0) // No DWT levels used -		{ -			mEngines[c] = kdu_decoder(res.access_subband(LL_BAND),&mAllocator,use_shorts); -		} -		else -		{ -			mEngines[c] = kdu_synthesis(res,&mAllocator,use_shorts); -		} -	} -	mAllocator.finalize(*codestreamp); // Actually creates buffering resources -	for (c = 0; c < mNumComponents; c++) -	{ -		mLines[c].create(); // Grabs resources from the allocator. -	} -} - -LLKDUDecodeState::~LLKDUDecodeState() -{ -	// Cleanup -	for (S32 c = 0; c < mNumComponents; c++) -	{ -		mEngines[c].destroy(); // engines are interfaces; no default destructors -	} -	mTile.close(); -} - -bool LLKDUDecodeState::processTileDecode(F32 decode_time, bool limit_time) -/* Decompresses a tile, writing the data into the supplied byte buffer. -The buffer contains interleaved image components, if there are any. -Although you may think of the buffer as belonging entirely to this tile, -the `buf' pointer may actually point into a larger buffer representing -multiple tiles.  For this reason, `row_gap' is needed to identify the -separation between consecutive rows in the real buffer. */ -{ -    LL_PROFILE_ZONE_SCOPED_CATEGORY_TEXTURE; -	S32 c; -	// Now walk through the lines of the buffer, recovering them from the -	// relevant tile-component processing engines. - -	LLTimer decode_timer; -	while (mDims.size.y--) -	{ -        { -            LL_PROFILE_ZONE_NAMED_CATEGORY_TEXTURE("kduptc - pull"); -            for (c = 0; c < mNumComponents; c++) -            { -                mEngines[c].pull(mLines[c]); -            } -        } - -		if ((mNumComponents >= 3) && mUseYCC) -		{ -            LL_PROFILE_ZONE_NAMED_CATEGORY_TEXTURE("kduptc - convert"); -			kdu_convert_ycc_to_rgb(mLines[0],mLines[1],mLines[2]); -		} - -        { -            LL_PROFILE_ZONE_NAMED_CATEGORY_TEXTURE("kduptc - transfer"); -            for (c = 0; c < mNumComponents; c++) -            { -                transfer_bytes(mBuf + c, mLines[c], mNumComponents, mBitDepths[c]); -            } -        } -		mBuf += mRowGap; -		if (mDims.size.y % 10) -		{ -			if (limit_time && decode_timer.getElapsedTimeF32() > decode_time) -			{ -				return false; -			} -		} -	} -	return true; -} - -// kdc_flow_control  - -kdc_flow_control::kdc_flow_control (kdu_supp::kdu_image_in_base *img_in, kdu_codestream codestream) -{ -	int n; -	 -	this->codestream = codestream; -	codestream.get_valid_tiles(valid_tile_indices); -	tile_idx = valid_tile_indices.pos; -	tile = codestream.open_tile(tile_idx,NULL); -	 -	// Set up the individual components -	num_components = codestream.get_num_components(true); -	components = new kdc_component_flow_control[num_components]; -	count_delta = 0; -	kdc_component_flow_control *comp = components; -	for (n = 0; n < num_components; n++, comp++) -	{ -		comp->line = NULL; -		comp->reader = img_in; -		kdu_coords subsampling;   -		codestream.get_subsampling(n,subsampling,true); -		kdu_dims dims;   -		codestream.get_tile_dims(tile_idx,n,dims,true); -		comp->vert_subsampling = subsampling.y; -		if ((n == 0) || (comp->vert_subsampling < count_delta)) -		{ -			count_delta = comp->vert_subsampling; -		} -		comp->ratio_counter = 0; -		comp->remaining_lines = comp->initial_lines = dims.size.y; -	} -	assert(num_components >= 0); -	 -	tile.set_components_of_interest(num_components); -	max_buffer_memory = engine.create(codestream,tile,false,NULL,false,1,NULL,NULL,false); -} - -kdc_flow_control::~kdc_flow_control() -{ -	if (components != NULL) -	{ -		delete[] components; -	} -	if (engine.exists()) -	{ -		engine.destroy(); -	} -} - -bool kdc_flow_control::advance_components() -{ -	bool found_line = false; -	while (!found_line) -	{ -		bool all_done = true; -		kdc_component_flow_control *comp = components; -		for (int n = 0; n < num_components; n++, comp++) -		{ -			assert(comp->ratio_counter >= 0); -			if (comp->remaining_lines > 0) -			{ -				all_done = false; -				comp->ratio_counter -= count_delta; -				if (comp->ratio_counter < 0) -				{ -					found_line = true; -					comp->line = engine.exchange_line(n,NULL,NULL); -					assert(comp->line != NULL); -					if (comp->line->get_width()) -					{ -						comp->reader->get(n,*(comp->line),0); -					} -				} -			} -		} -		if (all_done) -		{ -			return false; -		} -	} -	return true; -} - -void kdc_flow_control::process_components() -{ -	kdc_component_flow_control *comp = components; -	for (int n = 0; n < num_components; n++, comp++) -	{ -		if (comp->ratio_counter < 0) -		{ -			comp->ratio_counter += comp->vert_subsampling; -			assert(comp->ratio_counter >= 0); -			assert(comp->remaining_lines > 0); -			comp->remaining_lines--; -			assert(comp->line != NULL); -			engine.exchange_line(n,comp->line,NULL); -			comp->line = NULL; -		} -	} -} +/**
 + * @file llimagej2ckdu.cpp
 + * @brief This is an implementation of JPEG2000 encode/decode using Kakadu
 + *
 + * $LicenseInfo:firstyear=2010&license=viewerlgpl$
 + * Second Life Viewer Source Code
 + * Copyright (C) 2010, Linden Research, Inc.
 + *
 + * This library is free software; you can redistribute it and/or
 + * modify it under the terms of the GNU Lesser General Public
 + * License as published by the Free Software Foundation;
 + * version 2.1 of the License only.
 + *
 + * This library is distributed in the hope that it will be useful,
 + * but WITHOUT ANY WARRANTY; without even the implied warranty of
 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 + * Lesser General Public License for more details.
 + *
 + * You should have received a copy of the GNU Lesser General Public
 + * License along with this library; if not, write to the Free Software
 + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
 + *
 + * Linden Research, Inc., 945 Battery Street, San Francisco, CA  94111  USA
 + * $/LicenseInfo$
 + */
 +
 +#include "linden_common.h"
 +
 +#include "llimagej2ckdu.h"
 +
 +#include "lltimer.h"
 +#include "llpointer.h"
 +#include "llmath.h"
 +#include "llkdumem.h"
 +
 +#define kdu_xxxx "kdu_block_coding.h"
 +#include "include_kdu_xxxx.h"
 +
 +// Avoid ubiquitous necessity of kdu_core:: qualification
 +using namespace kdu_core;
 +
 +#include "llexception.h"
 +#include <boost/exception/diagnostic_information.hpp>
 +#include <sstream>
 +#include <iomanip>
 +
 +// Turns out this must NOT be in the anonymous namespace!
 +namespace kdu_core
 +{
 +// stream kdu_dims to std::ostream
 +inline
 +std::ostream& operator<<(std::ostream& out, const kdu_dims& dims)
 +{
 +    return out << "(" << dims.pos.x << "," << dims.pos.y << "),"
 +                  "[" << dims.size.x << "x" << dims.size.y << "]";
 +}
 +} // namespace kdu_core
 +
 +// operator<<(std::ostream&, const kdu_dims&) must precede #include "stringize.h"
 +#include "stringize.h"
 +
 +namespace {
 +// Failure to load an image shouldn't crash the whole viewer.
 +struct KDUError: public LLContinueError
 +{
 +    KDUError(const std::string& msg): LLContinueError(msg) {}
 +};
 +
 +// KDU defines int error codes as hex values, so we should log them in hex
 +// so we can grep KDU headers for the hex. However those hex values
 +// generally "happen" to encode big-endian multibyte character sequences,
 +// e.g. KDU_ERROR_EXCEPTION is 0x6b647545: 'kduE'
 +// But beware because KDU_NULL_EXCEPTION is simply 0 -- which doesn't
 +// preclude somebody from throwing it.
 +std::string report_kdu_exception(kdu_exception mb)
 +{
 +    std::ostringstream out;
 +    // always report mb in hex
 +    out << "kdu_exception " << std::hex << mb;
 +
 +    // Also display as many chars as are encoded in the kdu_exception
 +    // value. Make a char array; reserve 1 extra byte for nul terminator.
 +    char bytes[sizeof(kdu_exception) + 1];
 +    // Back up through 'bytes'
 +    char *bptr = bytes + sizeof(bytes);
 +    *(--bptr) = '\0';
 +    while (mb)
 +    {
 +        // store low-order byte of mb in next-left char
 +        *(--bptr) = char(mb & 0xFF);
 +        // then shift mb right by one byte
 +        mb >>= 8;
 +    }
 +    // did that produce any characters?
 +    if (*bptr)
 +    {
 +        out << " (" << bptr << ')';
 +    }
 +
 +    return out.str();
 +}
 +} // anonymous namespace
 +
 +
 +class kdc_flow_control {
 +
 +public:
 +    kdc_flow_control(kdu_supp::kdu_image_in_base *img_in, kdu_codestream codestream);
 +    ~kdc_flow_control();
 +    bool advance_components();
 +    void process_components();
 +
 +private:
 +
 +    struct kdc_component_flow_control {
 +    public:
 +        kdu_supp::kdu_image_in_base *reader;
 +        int vert_subsampling;
 +        int ratio_counter;  /*  Initialized to 0, decremented by `count_delta';
 +                                when < 0, a new line must be processed, after
 +                                which it is incremented by `vert_subsampling'.  */
 +        int initial_lines;
 +        int remaining_lines;
 +        kdu_line_buf *line;
 +    };
 +
 +    kdu_codestream codestream;
 +    kdu_dims valid_tile_indices;
 +    kdu_coords tile_idx;
 +    kdu_tile tile;
 +    int num_components;
 +    kdc_component_flow_control *components;
 +    int count_delta; // Holds the minimum of the `vert_subsampling' fields
 +    kdu_multi_analysis engine;
 +    kdu_long max_buffer_memory;
 +};
 +
 +//
 +// Kakadu specific implementation
 +//
 +void set_default_colour_weights(kdu_params *siz);
 +
 +// Factory function: see declaration in llimagej2c.cpp
 +LLImageJ2CImpl* fallbackCreateLLImageJ2CImpl()
 +{
 +    return new LLImageJ2CKDU();
 +}
 +
 +std::string LLImageJ2CKDU::getEngineInfo() const
 +{
 +    return llformat("KDU %s", KDU_CORE_VERSION);
 +}
 +
 +class LLKDUDecodeState
 +{
 +public:
 +    LLKDUDecodeState(kdu_tile tile, kdu_byte *buf, S32 row_gap,
 +                     kdu_codestream* codestreamp);
 +    ~LLKDUDecodeState();
 +    bool processTileDecode(F32 decode_time, bool limit_time = true);
 +
 +private:
 +    S32 mNumComponents;
 +    bool mUseYCC;
 +    kdu_dims mDims;
 +    kdu_sample_allocator mAllocator;
 +    kdu_tile_comp mComps[4];
 +    kdu_line_buf mLines[4];
 +    kdu_pull_ifc mEngines[4];
 +    bool mReversible[4]; // Some components may be reversible and others not
 +    int mBitDepths[4];   // Original bit-depth may be quite different from 8
 +
 +    kdu_tile mTile;
 +    kdu_byte *mBuf;
 +    S32 mRowGap;
 +};
 +
 +// Stuff for new kdu error handling
 +class LLKDUMessage: public kdu_message
 +{
 +public:
 +    LLKDUMessage(const std::string& type):
 +        mType(type)
 +    {}
 +
 +    virtual void put_text(const char *s)
 +    {
 +        LL_INFOS() << "KDU " << mType << ": " << s << LL_ENDL;
 +    }
 +
 +    virtual void put_text(const kdu_uint16 *s)
 +    {
 +        // The previous implementation simply streamed 's' to the log. So
 +        // either this put_text() override was never called -- or it produced
 +        // some baffling log messages -- because I assert that streaming a
 +        // const kdu_uint16* to a std::ostream will display only the hex value
 +        // of the pointer.
 +        LL_INFOS() << "KDU " << mType << ": "
 +                   << utf16str_to_utf8str(llutf16string(s)) << LL_ENDL;
 +    }
 +
 +private:
 +    std::string mType;
 +};
 +
 +struct LLKDUMessageWarning : public LLKDUMessage
 +{
 +    LLKDUMessageWarning():
 +        LLKDUMessage("Warning")
 +    {
 +        kdu_customize_warnings(this);
 +    }
 +};
 +// Instantiating LLKDUMessageWarning calls kdu_customize_warnings() with the
 +// new instance. Make it static so this only happens once.
 +static LLKDUMessageWarning sWarningHandler;
 +
 +struct LLKDUMessageError : public LLKDUMessage
 +{
 +    LLKDUMessageError():
 +        LLKDUMessage("Error")
 +    {
 +        kdu_customize_errors(this);
 +    }
 +
 +    virtual void flush(bool end_of_message = false)
 +    {
 +        // According to the documentation nat found:
 +        // http://pirlwww.lpl.arizona.edu/resources/guide/software/Kakadu/html_pages/globals__kdu$mize_errors.html
 +        // "If a kdu_error object is destroyed, handler→flush will be called with
 +        // an end_of_message argument equal to true and the process will
 +        // subsequently be terminated through exit. The termination may be
 +        // avoided, however, by throwing an exception from within the message
 +        // terminating handler→flush call."
 +        // So throwing an exception here isn't arbitrary: we MUST throw an
 +        // exception if we want to recover from a KDU error.
 +        // Because this confused me: the above quote specifically refers to
 +        // the kdu_error class, which is constructed internally within KDU at
 +        // the point where a fatal error is discovered and reported. It is NOT
 +        // talking about the kdu_message subclass passed to
 +        // kdu_customize_errors(). Destroying this static object at program
 +        // shutdown will NOT engage the behavior described above.
 +        if (end_of_message)
 +        {
 +            LLTHROW(KDUError("LLKDUMessageError::flush()"));
 +        }
 +    }
 +};
 +// Instantiating LLKDUMessageError calls kdu_customize_errors() with the new
 +// instance. Make it static so this only happens once.
 +static LLKDUMessageError sErrorHandler;
 +
 +LLImageJ2CKDU::LLImageJ2CKDU() : LLImageJ2CImpl(),
 +    mInputp(),
 +    mCodeStreamp(),
 +    mTPosp(),
 +    mTileIndicesp(),
 +    mRawImagep(NULL),
 +    mDecodeState(),
 +    mBlocksSize(-1),
 +    mPrecinctsSize(-1),
 +    mLevels(0)
 +{
 +}
 +
 +LLImageJ2CKDU::~LLImageJ2CKDU()
 +{
 +    cleanupCodeStream(); // in case destroyed before decode completed
 +}
 +
 +// Stuff for new simple decode
 +void transfer_bytes(kdu_byte *dest, kdu_line_buf &src, int gap, int precision);
 +
 +// This is called by the real (private) initDecode() (keep_codestream true)
 +// and getMetadata() methods (keep_codestream false). As far as nat can tell,
 +// mode is always MODE_FAST. It was called by findDiscardLevelsBoundaries()
 +// as well, when that still existed, with keep_codestream true and MODE_FAST.
 +void LLImageJ2CKDU::setupCodeStream(LLImageJ2C &base, bool keep_codestream, ECodeStreamMode mode)
 +{
 +    LLImageDataLock lock(&base);
 +
 +    S32 data_size = base.getDataSize();
 +    S32 max_bytes = (base.getMaxBytes() ? base.getMaxBytes() : data_size);
 +
 +    //
 +    //  Initialization
 +    //
 +    mCodeStreamp.reset();
 +
 +    // It's not clear to nat under what circumstances we would reuse a
 +    // pre-existing LLKDUMemSource instance. As of 2016-08-05, it consists of
 +    // two U32s and a pointer, so it's not as if it would be a huge overhead
 +    // to allocate a new one every time.
 +    // Also -- why is base.getData() tested specifically here? If that returns
 +    // NULL, shouldn't we bail out of the whole method?
 +    if (!mInputp && base.getData())
 +    {
 +        // The compressed data has been loaded
 +        // Setup the source for the codestream
 +        mInputp.reset(new LLKDUMemSource(base.getData(), data_size));
 +    }
 +
 +    if (mInputp)
 +    {
 +        // This is LLKDUMemSource::reset(), not boost::scoped_ptr::reset().
 +        mInputp->reset();
 +    }
 +
 +    mCodeStreamp->create(mInputp.get());
 +
 +    // Set the maximum number of bytes to use from the codestream
 +    // *TODO: This seems to be wrong. The base class should have no idea of
 +    // how j2c compression works so no good way of computing what's the byte
 +    // range to be used.
 +    mCodeStreamp->set_max_bytes(max_bytes,true);
 +
 +    //  If you want to flip or rotate the image for some reason, change
 +    // the resolution, or identify a restricted region of interest, this is
 +    // the place to do it.  You may use "kdu_codestream::change_appearance"
 +    // and "kdu_codestream::apply_input_restrictions" for this purpose.
 +    //  If you wish to truncate the code-stream prior to decompression, you
 +    // may use "kdu_codestream::set_max_bytes".
 +    //  If you wish to retain all compressed data so that the material
 +    // can be decompressed multiple times, possibly with different appearance
 +    // parameters, you should call "kdu_codestream::set_persistent" here.
 +    //  There are a variety of other features which must be enabled at
 +    // this point if you want to take advantage of them.  See the
 +    // descriptions appearing with the "kdu_codestream" interface functions
 +    // in "kdu_compressed.h" for an itemized account of these capabilities.
 +
 +    switch (mode)
 +    {
 +    case MODE_FAST:
 +        mCodeStreamp->set_fast();
 +        break;
 +    case MODE_RESILIENT:
 +        mCodeStreamp->set_resilient();
 +        break;
 +    case MODE_FUSSY:
 +        mCodeStreamp->set_fussy();
 +        break;
 +    default:
 +        llassert(0);
 +        mCodeStreamp->set_fast();
 +    }
 +
 +    kdu_dims dims;
 +    mCodeStreamp->get_dims(0,dims);
 +
 +    S32 components = mCodeStreamp->get_num_components();
 +
 +    // Check that components have consistent dimensions (for PPM file)
 +    for (int idx = 1; idx < components; ++idx)
 +    {
 +        kdu_dims other_dims;
 +        mCodeStreamp->get_dims(idx, other_dims);
 +        if (other_dims != dims)
 +        {
 +            // This method is only called from methods that catch KDUError.
 +            // We want to fail the image load, not crash the viewer.
 +            LLTHROW(KDUError(STRINGIZE("Component " << idx << " dimensions "
 +                                       << stringize(other_dims)
 +                                       << " do not match component 0 dimensions "
 +                                       << stringize(dims) << "!")));
 +        }
 +    }
 +
 +    // Get the number of resolution levels in that image
 +    mLevels = mCodeStreamp->get_min_dwt_levels();
 +
 +    // Set the base dimensions
 +    base.setSize(dims.size.x, dims.size.y, components);
 +    base.setLevels(mLevels);
 +
 +    if (!keep_codestream)
 +    {
 +        mCodeStreamp.reset();
 +        mInputp.reset();
 +    }
 +}
 +
 +void LLImageJ2CKDU::cleanupCodeStream()
 +{
 +    LL_PROFILE_ZONE_SCOPED_CATEGORY_TEXTURE;
 +    mInputp.reset();
 +    mDecodeState.reset();
 +    mCodeStreamp.reset();
 +    mTPosp.reset();
 +    mTileIndicesp.reset();
 +}
 +
 +// This is the protected virtual method called by LLImageJ2C::initDecode().
 +// However, as far as nat can tell, LLImageJ2C::initDecode() is called only by
 +// llimage_libtest.cpp's load_image() function. No detectable production use.
 +bool LLImageJ2CKDU::initDecode(LLImageJ2C &base, LLImageRaw &raw_image, int discard_level, int* region)
 +{
 +    return initDecode(base,raw_image,0.0f,MODE_FAST,0,4,discard_level,region);
 +}
 +
 +bool LLImageJ2CKDU::initEncode(LLImageJ2C &base, LLImageRaw &raw_image, int blocks_size, int precincts_size, int levels)
 +{
 +    mPrecinctsSize = precincts_size;
 +    if (mPrecinctsSize != -1)
 +    {
 +        mPrecinctsSize = get_lower_power_two(mPrecinctsSize,MAX_PRECINCT_SIZE);
 +        mPrecinctsSize = llmax(mPrecinctsSize,MIN_PRECINCT_SIZE);
 +    }
 +    mBlocksSize = blocks_size;
 +    if (mBlocksSize != -1)
 +    {
 +        mBlocksSize = get_lower_power_two(mBlocksSize,MAX_BLOCK_SIZE);
 +        mBlocksSize = llmax(mBlocksSize,MIN_BLOCK_SIZE);
 +        if (mPrecinctsSize != -1)
 +        {
 +            mBlocksSize = llmin(mBlocksSize,mPrecinctsSize);    // blocks *must* be smaller than precincts
 +        }
 +    }
 +    mLevels = levels;
 +    if (mLevels != 0)
 +    {
 +        mLevels = llclamp(mLevels,MIN_DECOMPOSITION_LEVELS,MAX_DECOMPOSITION_LEVELS);
 +        base.setLevels(mLevels);
 +    }
 +    return true;
 +}
 +
 +// This is the real (private) initDecode() called both by the protected
 +// initDecode() method and by decodeImpl(). As far as nat can tell, only the
 +// decodeImpl() usage matters for production.
 +bool LLImageJ2CKDU::initDecode(LLImageJ2C &base, LLImageRaw &raw_image, F32 decode_time, ECodeStreamMode mode, S32 first_channel, S32 max_channel_count, int discard_level, int* region)
 +{
 +    LL_PROFILE_ZONE_SCOPED_CATEGORY_TEXTURE;
 +    base.resetLastError();
 +
 +    // *FIX: kdu calls our callback function if there's an error, and then bombs.
 +    // To regain control, we throw an exception, and catch it here.
 +    try
 +    {
 +        // Merov : Test!! DO NOT COMMIT!!
 +        //findDiscardLevelsBoundaries(base);
 +
 +        base.updateRawDiscardLevel();
 +        setupCodeStream(base, true, mode);
 +
 +        mRawImagep = &raw_image;
 +        mCodeStreamp->change_appearance(false, true, false);
 +
 +        // Apply loading discard level and cropping if required
 +        kdu_dims* region_kdu = NULL;
 +        if (region != NULL)
 +        {
 +            region_kdu = new kdu_dims;
 +            region_kdu->pos.x  = region[0];
 +            region_kdu->pos.y  = region[1];
 +            region_kdu->size.x = region[2] - region[0];
 +            region_kdu->size.y = region[3] - region[1];
 +        }
 +        int discard = (discard_level != -1 ? discard_level : base.getRawDiscardLevel());
 +        //LL_INFOS() << "Merov debug : initDecode, discard used = " << discard << ", asked = " << discard_level << LL_ENDL;
 +        // Apply loading restrictions
 +        mCodeStreamp->apply_input_restrictions( first_channel, max_channel_count, discard, 0, region_kdu);
 +
 +        // Clean-up
 +        if (region_kdu)
 +        {
 +            delete region_kdu;
 +            region_kdu = NULL;
 +        }
 +
 +        // Resize raw_image according to the image to be decoded
 +        kdu_dims dims; mCodeStreamp->get_dims(0,dims);
 +        S32 channels = base.getComponents() - first_channel;
 +        channels = llmin(channels,max_channel_count);
 +        raw_image.resize(dims.size.x, dims.size.y, channels);
 +
 +        if (!mTileIndicesp)
 +        {
 +            mTileIndicesp.reset(new kdu_dims);
 +        }
 +        mCodeStreamp->get_valid_tiles(*mTileIndicesp);
 +        if (!mTPosp)
 +        {
 +            mTPosp.reset(new kdu_coords);
 +            mTPosp->y = 0;
 +            mTPosp->x = 0;
 +        }
 +    }
 +    catch (const KDUError& msg)
 +    {
 +        base.setLastError(msg.what());
 +        return false;
 +    }
 +    catch (kdu_exception kdu_value)
 +    {
 +        // KDU internally throws kdu_exception. It's possible that such an
 +        // exception might leak out into our code. Catch kdu_exception
 +        // specially because boost::current_exception_diagnostic_information()
 +        // could do nothing with it.
 +        base.setLastError(report_kdu_exception(kdu_value));
 +        return false;
 +    }
 +    catch (...)
 +    {
 +        base.setLastError("Unknown J2C error: " +
 +                          boost::current_exception_diagnostic_information());
 +        return false;
 +    }
 +
 +    return true;
 +}
 +
 +
 +// Returns true to mean done, whether successful or not.
 +bool LLImageJ2CKDU::decodeImpl(LLImageJ2C &base, LLImageRaw &raw_image, F32 decode_time, S32 first_channel, S32 max_channel_count)
 +{
 +    LL_PROFILE_ZONE_SCOPED_CATEGORY_TEXTURE;
 +
 +    LLImageDataLock lockIn(&base);
 +    LLImageDataLock lockOut(&raw_image);
 +
 +    ECodeStreamMode mode = MODE_FAST;
 +
 +    bool limit_time = decode_time > 0.0f;
 +    LLTimer decode_timer;
 +
 +    if (!mCodeStreamp->exists())
 +    {
 +        if (!initDecode(base, raw_image, decode_time, mode, first_channel, max_channel_count))
 +        {
 +            // Initializing the J2C decode failed, bail out.
 +            cleanupCodeStream();
 +            return true; // done
 +        }
 +    }
 +
 +    // These can probably be grabbed from what's saved in the class.
 +    kdu_dims dims;
 +    mCodeStreamp->get_dims(0, dims);
 +
 +    // Now we are ready to walk through the tiles processing them one-by-one.
 +    kdu_byte *buffer = raw_image.getData();
 +    if (!buffer)
 +    {
 +        base.setLastError("Memory error");
 +        base.decodeFailed();
 +        cleanupCodeStream();
 +        return true; // done
 +    }
 +
 +    while (mTPosp->y < mTileIndicesp->size.y)
 +    {
 +        while (mTPosp->x < mTileIndicesp->size.x)
 +        {
 +            try
 +            {
 +                if (!mDecodeState)
 +                {
 +                    kdu_tile tile = mCodeStreamp->open_tile(*(mTPosp)+mTileIndicesp->pos);
 +
 +                    // 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
 +                    // during compression and so it can happen that tiles
 +                    // (at the image component level) actually have
 +                    // different dimensions.  For this reason, we cannot
 +                    // figure out the buffer region occupied by a tile
 +                    // directly from the tile indices.  Instead, we query
 +                    // the highest resolution of the first tile-component
 +                    // concerning its location and size on the canvas --
 +                    // the `dims' object already holds the location and
 +                    // size of the entire image component on the same
 +                    // canvas coordinate system.  Comparing the two tells
 +                    // us where the current tile is in the buffer.
 +                    S32 channels = base.getComponents() - first_channel;
 +                    if (channels > max_channel_count)
 +                    {
 +                        channels = max_channel_count;
 +                    }
 +                    kdu_resolution res = tile.access_component(0).access_resolution();
 +                    kdu_dims tile_dims; res.get_dims(tile_dims);
 +                    kdu_coords offset = tile_dims.pos - dims.pos;
 +                    int row_gap = channels*dims.size.x; // inter-row separation
 +                    kdu_byte *buf = buffer + offset.y*row_gap + offset.x*channels;
 +                    mDecodeState.reset(new LLKDUDecodeState(tile, buf, row_gap,
 +                                                            mCodeStreamp.get()));
 +                }
 +                // Do the actual processing
 +                F32 remaining_time = limit_time ? decode_time - decode_timer.getElapsedTimeF32().value() : 0.0f;
 +                // This is where we do the actual decode.  If we run out of time, return false.
 +                if (mDecodeState->processTileDecode(remaining_time, limit_time))
 +                {
 +                    mDecodeState.reset();
 +                }
 +                else
 +                {
 +                    // Not finished decoding yet.
 +                    base.setLastError("Ran out of time while decoding");
 +                    base.decodeFailed();
 +                    cleanupCodeStream();
 +                    return false;
 +                }
 +            }
 +            catch (const KDUError& msg)
 +            {
 +                base.setLastError(msg.what());
 +                base.decodeFailed();
 +                cleanupCodeStream();
 +                return true; // done
 +            }
 +            catch (kdu_exception kdu_value)
 +            {
 +                // KDU internally throws kdu_exception. It's possible that such an
 +                // exception might leak out into our code. Catch kdu_exception
 +                // specially because boost::current_exception_diagnostic_information()
 +                // could do nothing with it.
 +                base.setLastError(report_kdu_exception(kdu_value));
 +                base.decodeFailed();
 +                cleanupCodeStream();
 +                return true; // done
 +            }
 +            catch (...)
 +            {
 +                base.setLastError("Unknown J2C error: " +
 +                                  boost::current_exception_diagnostic_information());
 +                base.decodeFailed();
 +                cleanupCodeStream();
 +                return true; // done
 +            }
 +
 +
 +            mTPosp->x++;
 +        }
 +        mTPosp->y++;
 +        mTPosp->x = 0;
 +    }
 +
 +    cleanupCodeStream();
 +
 +    return true;
 +}
 +
 +
 +bool LLImageJ2CKDU::encodeImpl(LLImageJ2C &base, const LLImageRaw &raw_image, const char* comment_text, F32 encode_time, bool reversible)
 +{
 +    // Declare and set simple arguments
 +    bool transpose = false;
 +    bool vflip = true;
 +    bool hflip = false;
 +
 +    try
 +    {
 +        // Set up input image files
 +        siz_params siz;
 +
 +        // Should set rate someplace here
 +        LLKDUMemIn mem_in(raw_image.getData(),
 +            raw_image.getDataSize(),
 +            raw_image.getWidth(),
 +            raw_image.getHeight(),
 +            raw_image.getComponents(),
 +            &siz);
 +
 +        base.setSize(raw_image.getWidth(), raw_image.getHeight(), raw_image.getComponents());
 +
 +        int num_components = raw_image.getComponents();
 +
 +        siz.set(Scomponents,0,0,num_components);
 +        siz.set(Sdims,0,0,base.getHeight());  // Height of first image component
 +        siz.set(Sdims,0,1,base.getWidth());   // Width of first image component
 +        siz.set(Sprecision,0,0,8);  // Image samples have original bit-depth of 8
 +        siz.set(Ssigned,0,0,false); // Image samples are originally unsigned
 +
 +        kdu_params *siz_ref = &siz;
 +        siz_ref->finalize();
 +        siz_params transformed_siz; // Use this one to construct code-stream
 +        transformed_siz.copy_from(&siz,-1,-1,-1,0,transpose,false,false);
 +
 +        // Construct the `kdu_codestream' object and parse all remaining arguments
 +        U32 max_output_size = base.getWidth()*base.getHeight()*base.getComponents();
 +        max_output_size = (max_output_size < 1000 ? 1000 : max_output_size);
 +        U8 *output_buffer = new U8[max_output_size];
 +        U32 output_size = 0; // Address updated by LLKDUMemTarget to give the final compressed buffer size
 +        LLKDUMemTarget output(output_buffer, output_size, max_output_size);
 +
 +        kdu_codestream codestream;
 +        codestream.create(&transformed_siz,&output);
 +
 +        if (comment_text)
 +        {
 +            // Set the comments for the codestream
 +            kdu_codestream_comment comment = codestream.add_comment();
 +            comment.put_text(comment_text);
 +        }
 +
 +        if (num_components >= 3)
 +        {
 +            // Note that we always use YCC and not YUV
 +            // *TODO: Verify this doesn't screws up reversible textures (like sculpties) as YCC is not reversible but YUV is...
 +            set_default_colour_weights(codestream.access_siz());
 +        }
 +
 +        // Set codestream options
 +        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.
 +        llassert (base.mRate > 0.f);
 +        max_bytes = (U32)((F32)(max_bytes) * base.mRate);
 +
 +        // This code is where we specify the target number of bytes for each quality layer.
 +        // 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
 +        layer_bytes[nb_layers++] = FIRST_PACKET_SIZE;
 +        U32 i = MIN_LAYER_SIZE;
 +        while ((i < max_bytes) && (nb_layers < (MAX_NB_LAYERS-1)))
 +        {
 +            layer_bytes[nb_layers++] = i;
 +            i *= 4;
 +        }
 +        // Note: for small images, we can have (max_bytes < FIRST_PACKET_SIZE), hence the test
 +        if (layer_bytes[nb_layers-1] < max_bytes)
 +        {
 +            // Set the last quality layer so to fit the preset compression ratio
 +            layer_bytes[nb_layers++] = max_bytes;
 +        }
 +
 +        if (reversible)
 +        {
 +            // Use 0 for a last quality layer for reversible images so all remaining code blocks will be flushed
 +            // Hack: KDU encoding for reversible images has a bug for small images that leads to j2c images that
 +            // cannot be open or are very blurry. Avoiding that last layer prevents the problem to happen.
 +            if ((base.getWidth() >= 32) || (base.getHeight() >= 32))
 +            {
 +                layer_bytes[nb_layers++] = 0;
 +            }
 +            codestream.access_siz()->parse_string("Creversible=yes");
 +            // *TODO: we should use yuv in reversible mode
 +            // Don't turn this on now though as it creates problems on decoding for the moment
 +            //codestream.access_siz()->parse_string("Cycc=no");
 +        }
 +
 +        std::string layer_string = llformat("Clayers=%d",nb_layers);
 +        codestream.access_siz()->parse_string(layer_string.c_str());
 +
 +        // Set up data ordering, markers, etc... if precincts or blocks specified
 +        if ((mBlocksSize != -1) || (mPrecinctsSize != -1))
 +        {
 +            if (mPrecinctsSize != -1)
 +            {
 +                std::string precincts_string = llformat("Cprecincts={%d,%d}",mPrecinctsSize,mPrecinctsSize);
 +                codestream.access_siz()->parse_string(precincts_string.c_str());
 +            }
 +            if (mBlocksSize != -1)
 +            {
 +                std::string blocks_string = llformat("Cblk={%d,%d}",mBlocksSize,mBlocksSize);
 +                codestream.access_siz()->parse_string(blocks_string.c_str());
 +            }
 +            std::string ordering_string = llformat("Corder=LRCP");
 +            codestream.access_siz()->parse_string(ordering_string.c_str());
 +            std::string PLT_string = llformat("ORGgen_plt=yes");
 +            codestream.access_siz()->parse_string(PLT_string.c_str());
 +            std::string Parts_string = llformat("ORGtparts=R");
 +            codestream.access_siz()->parse_string(Parts_string.c_str());
 +        }
 +
 +        // Set the number of wavelets subresolutions (aka levels)
 +        if (mLevels != 0)
 +        {
 +            std::string levels_string = llformat("Clevels=%d",mLevels);
 +            codestream.access_siz()->parse_string(levels_string.c_str());
 +        }
 +
 +        // Complete the encode settings
 +        codestream.access_siz()->finalize_all();
 +        codestream.change_appearance(transpose,vflip,hflip);
 +
 +        // Now we are ready for sample data processing
 +        kdc_flow_control *tile = new kdc_flow_control(&mem_in,codestream);
 +        bool done = false;
 +        while (!done)
 +        {
 +            // Process line by line
 +            if (tile->advance_components())
 +            {
 +                tile->process_components();
 +            }
 +            else
 +            {
 +                done = true;
 +            }
 +        }
 +
 +        // Produce the compressed output
 +        codestream.flush(layer_bytes,nb_layers);
 +
 +        // Cleanup
 +        delete tile;
 +        codestream.destroy();
 +
 +        // Now that we're done encoding, create the new data buffer for the compressed
 +        // image and stick it there.
 +        base.copyData(output_buffer, output_size);
 +        base.updateData(); // set width, height
 +        delete[] output_buffer;
 +    }
 +    catch(const KDUError& msg)
 +    {
 +        base.setLastError(msg.what());
 +        return false;
 +    }
 +    catch (kdu_exception kdu_value)
 +    {
 +        // KDU internally throws kdu_exception. It's possible that such an
 +        // exception might leak out into our code. Catch kdu_exception
 +        // specially because boost::current_exception_diagnostic_information()
 +        // could do nothing with it.
 +        base.setLastError(report_kdu_exception(kdu_value));
 +        return false;
 +    }
 +    catch( ... )
 +    {
 +        base.setLastError("Unknown J2C error: " +
 +                          boost::current_exception_diagnostic_information());
 +        return false;
 +    }
 +
 +    return true;
 +}
 +
 +bool LLImageJ2CKDU::getMetadata(LLImageJ2C &base)
 +{
 +    // *FIX: kdu calls our callback function if there's an error, and
 +    // then bombs. To regain control, we throw an exception, and
 +    // catch it here.
 +    try
 +    {
 +        setupCodeStream(base, false, MODE_FAST);
 +        return true;
 +    }
 +    catch (const KDUError& msg)
 +    {
 +        base.setLastError(msg.what());
 +        return false;
 +    }
 +    catch (kdu_exception kdu_value)
 +    {
 +        // KDU internally throws kdu_exception. It's possible that such an
 +        // exception might leak out into our code. Catch kdu_exception
 +        // specially because boost::current_exception_diagnostic_information()
 +        // could do nothing with it.
 +        base.setLastError(report_kdu_exception(kdu_value));
 +        return false;
 +    }
 +    catch (...)
 +    {
 +        base.setLastError("Unknown J2C error: " +
 +                          boost::current_exception_diagnostic_information());
 +        return false;
 +    }
 +}
 +
 +/*****************************************************************************/
 +/* STATIC                        copy_block                                  */
 +/*****************************************************************************/
 +
 +/*==========================================================================*|
 +// Only called by copy_tile(), which is itself commented out
 +static void copy_block(kdu_block *in, kdu_block *out)
 +{
 +    if (in->K_max_prime != out->K_max_prime)
 +    {
 +        std::cout << "Cannot copy blocks belonging to subbands with different quantization parameters." << std::endl;
 +        return;
 +    }
 +    if ((in->size.x != out->size.x) || (in->size.y != out->size.y))
 +    {
 +        std::cout << "Cannot copy code-blocks with different dimensions." << std::endl;
 +        return;
 +    }
 +    out->missing_msbs = in->missing_msbs;
 +    if (out->max_passes < (in->num_passes+2))        // Gives us enough to round up
 +        out->set_max_passes(in->num_passes+2,false); // to the next whole bit-plane
 +    out->num_passes = in->num_passes;
 +    int num_bytes = 0;
 +    for (int z=0; z < in->num_passes; z++)
 +    {
 +        num_bytes += (out->pass_lengths[z] = in->pass_lengths[z]);
 +        out->pass_slopes[z] = in->pass_slopes[z];
 +    }
 +
 +    // Just copy compressed code-bytes. Block transcoding not supported.
 +    if (out->max_bytes < num_bytes)
 +        out->set_max_bytes(num_bytes,false);
 +    memcpy(out->byte_buffer,in->byte_buffer,(size_t) num_bytes);
 +}
 +|*==========================================================================*/
 +
 +/*****************************************************************************/
 +/* STATIC                        copy_tile                                   */
 +/*****************************************************************************/
 +
 +/*==========================================================================*|
 +// Only called by findDiscardLevelsBoundaries(), which is itself commented out
 +static void
 +copy_tile(kdu_tile tile_in, kdu_tile tile_out, int tnum_in, int tnum_out,
 +          kdu_params *siz_in, kdu_params *siz_out, int skip_components,
 +          int &num_blocks)
 +{
 +    int num_components = tile_out.get_num_components();
 +    int new_tpart=0, next_tpart = 1;
 +
 +    for (int c=0; c < num_components; c++)
 +    {
 +        kdu_tile_comp comp_in, comp_out;
 +        comp_in = tile_in.access_component(c);
 +        comp_out = tile_out.access_component(c);
 +        int num_resolutions = comp_out.get_num_resolutions();
 +        //std::cout << "    Copying tile : num_resolutions = " << num_resolutions << std::endl;
 +        for (int r=0; r < num_resolutions; r++)
 +        {
 +            kdu_resolution res_in;  res_in = comp_in.access_resolution(r);
 +            kdu_resolution res_out; res_out = comp_out.access_resolution(r);
 +            int b, min_band;
 +            int num_bands = res_in.get_valid_band_indices(min_band);
 +            std::cout << "        Copying tile : num_bands = " << num_bands << std::endl;
 +            for (b=min_band; num_bands > 0; num_bands--, b++)
 +            {
 +                kdu_subband band_in;  band_in = res_in.access_subband(b);
 +                kdu_subband band_out; band_out = res_out.access_subband(b);
 +                kdu_dims blocks_in;  band_in.get_valid_blocks(blocks_in);
 +                kdu_dims blocks_out; band_out.get_valid_blocks(blocks_out);
 +                if ((blocks_in.size.x != blocks_out.size.x) ||
 +                    (blocks_in.size.y != blocks_out.size.y))
 +                {
 +                    std::cout << "Transcoding operation cannot proceed: Code-block partitions for the input and output code-streams do not agree." << std::endl;
 +                    return;
 +                }
 +                kdu_coords idx;
 +                //std::cout << "            Copying tile : block indices, x = " << blocks_out.size.x << " and y = " << blocks_out.size.y << std::endl;
 +                for (idx.y=0; idx.y < blocks_out.size.y; idx.y++)
 +                {
 +                    for (idx.x=0; idx.x < blocks_out.size.x; idx.x++)
 +                    {
 +                        kdu_block *in =
 +                        band_in.open_block(idx+blocks_in.pos,&new_tpart);
 +                        for (; next_tpart <= new_tpart; next_tpart++)
 +                            siz_out->copy_from(siz_in,tnum_in,tnum_out,next_tpart,
 +                                               skip_components);
 +                        kdu_block *out = band_out.open_block(idx+blocks_out.pos);
 +                        copy_block(in,out);
 +                        band_in.close_block(in);
 +                        band_out.close_block(out);
 +                        num_blocks++;
 +                    }
 +                }
 +            }
 +        }
 +    }
 +}
 +|*==========================================================================*/
 +
 +// Find the block boundary for each discard level in the input image.
 +// We parse the input blocks and copy them in a temporary output stream.
 +// For the moment, we do nothing more that parsing the raw list of blocks and outputing result.
 +/*==========================================================================*|
 +// See comments in header file for why this is commented out.
 +void LLImageJ2CKDU::findDiscardLevelsBoundaries(LLImageJ2C &base)
 +{
 +    // We need the number of levels in that image before starting.
 +    getMetadata(base);
 +
 +    for (int discard_level = 0; discard_level < mLevels; discard_level++)
 +    {
 +        //std::cout << "Parsing discard level = " << discard_level << std::endl;
 +        // Create the input codestream object.
 +        setupCodeStream(base, true, MODE_FAST);
 +        mCodeStreamp->apply_input_restrictions(0, 4, discard_level, 0, NULL);
 +        mCodeStreamp->set_max_bytes(KDU_LONG_MAX,true);
 +        siz_params *siz_in = mCodeStreamp->access_siz();
 +
 +        // Create the output codestream object.
 +        siz_params siz;
 +        siz.copy_from(siz_in,-1,-1,-1,0,discard_level,false,false,false);
 +        siz.set(Scomponents,0,0,mCodeStreamp->get_num_components());
 +
 +        U32 max_output_size = base.getWidth()*base.getHeight()*base.getComponents();
 +        max_output_size = (max_output_size < 1000 ? 1000 : max_output_size);
 +        U8 *output_buffer = new U8[max_output_size];
 +        U32 output_size = 0; // Address updated by LLKDUMemTarget to give the final compressed buffer size
 +        LLKDUMemTarget output(output_buffer, output_size, max_output_size);
 +        kdu_codestream codestream_out;
 +        codestream_out.create(&siz,&output);
 +        //codestream_out.share_buffering(*mCodeStreamp);
 +        siz_params *siz_out = codestream_out.access_siz();
 +        siz_out->copy_from(siz_in,-1,-1,-1,0,discard_level,false,false,false);
 +        codestream_out.access_siz()->finalize_all(-1);
 +
 +        // Set up rate control variables
 +        kdu_long max_bytes = KDU_LONG_MAX;
 +        kdu_params *cod = siz_out->access_cluster(COD_params);
 +        int total_layers;  cod->get(Clayers,0,0,total_layers);
 +        kdu_long *layer_bytes = new kdu_long[total_layers];
 +        int nel, non_empty_layers = 0;
 +
 +        // Now ready to perform the transfer of compressed data between streams
 +        int flush_counter = INT_MAX;
 +        kdu_dims tile_indices_in;
 +        mCodeStreamp->get_valid_tiles(tile_indices_in);
 +        kdu_dims tile_indices_out;
 +        codestream_out.get_valid_tiles(tile_indices_out);
 +        assert((tile_indices_in.size.x == tile_indices_out.size.x) &&
 +               (tile_indices_in.size.y == tile_indices_out.size.y));
 +        int num_blocks=0;
 +
 +        kdu_coords idx;
 +        //std::cout << "Parsing tiles : x = " << tile_indices_out.size.x << " to y = " << tile_indices_out.size.y << std::endl;
 +        for (idx.y=0; idx.y < tile_indices_out.size.y; idx.y++)
 +        {
 +            for (idx.x=0; idx.x < tile_indices_out.size.x; idx.x++)
 +            {
 +                kdu_tile tile_in = mCodeStreamp->open_tile(idx+tile_indices_in.pos);
 +                int tnum_in = tile_in.get_tnum();
 +                int tnum_out = idx.x + idx.y*tile_indices_out.size.x;
 +                siz_out->copy_from(siz_in,tnum_in,tnum_out,0,0,discard_level,false,false,false);
 +                siz_out->finalize_all(tnum_out);
 +                // Note: do not open the output tile without first copying any tile-specific code-stream parameters
 +                kdu_tile tile_out = codestream_out.open_tile(idx+tile_indices_out.pos);
 +                assert(tnum_out == tile_out.get_tnum());
 +                copy_tile(tile_in,tile_out,tnum_in,tnum_out,siz_in,siz_out,0,num_blocks);
 +                tile_in.close();
 +                tile_out.close();
 +                flush_counter--;
 +                if ((flush_counter <= 0) && codestream_out.ready_for_flush())
 +                {
 +                    flush_counter = INT_MAX;
 +                    nel = codestream_out.trans_out(max_bytes,layer_bytes,total_layers);
 +                    non_empty_layers = (nel > non_empty_layers)?nel:non_empty_layers;
 +                }
 +            }
 +        }
 +
 +        // Generate the output code-stream
 +        if (codestream_out.ready_for_flush())
 +        {
 +            nel = codestream_out.trans_out(max_bytes,layer_bytes,total_layers);
 +            non_empty_layers = (nel > non_empty_layers)?nel:non_empty_layers;
 +        }
 +        if (non_empty_layers > total_layers)
 +            non_empty_layers = total_layers; // Can happen if a tile has more layers
 +
 +        // Print out stats
 +        std::cout << "Code stream parsing for discard level = " << discard_level << std::endl;
 +        std::cout << "    Total compressed memory in  = " << mCodeStreamp->get_compressed_data_memory() << " bytes" << std::endl;
 +        std::cout << "    Total compressed memory out = " << codestream_out.get_compressed_data_memory() << " bytes" << std::endl;
 +        //std::cout << "    Output contains " << total_layers << " quality layers" << std::endl;
 +        std::cout << "    Transferred " << num_blocks << " code-blocks from in to out" << std::endl;
 +        //std::cout << "    Read " << mCodeStreamp->get_num_tparts() << " tile-part(s) from a total of " << (int) tile_indices_in.area() << " tile(s)" << std::endl;
 +        std::cout << "    Total bytes read = " << mCodeStreamp->get_total_bytes() << std::endl;
 +        //std::cout << "    Wrote " << codestream_out.get_num_tparts() << " tile-part(s) in a total of " << (int) tile_indices_out.area() << " tile(s)" << std::endl;
 +        std::cout << "    Total bytes written = " << codestream_out.get_total_bytes() << std::endl;
 +        std::cout << "-------------" << std::endl;
 +
 +        // Clean-up
 +        cleanupCodeStream();
 +        codestream_out.destroy();
 +        delete[] output_buffer;
 +    }
 +    return;
 +}
 +|*==========================================================================*/
 +
 +void set_default_colour_weights(kdu_params *siz)
 +{
 +    kdu_params *cod = siz->access_cluster(COD_params);
 +    assert(cod != NULL);
 +
 +    bool can_use_ycc = true;
 +    bool rev0 = false;
 +    int depth0 = 0, sub_x0 = 1, sub_y0 = 1;
 +    for (int c = 0; c < 3; c++)
 +    {
 +        int depth = 0; siz->get(Sprecision,c,0,depth);
 +        int sub_y = 1; siz->get(Ssampling,c,0,sub_y);
 +        int sub_x = 1; siz->get(Ssampling,c,1,sub_x);
 +        kdu_params *coc = cod->access_relation(-1,c);
 +        bool rev = false; coc->get(Creversible,0,0,rev);
 +        if (c == 0)
 +        {
 +            rev0 = rev; depth0 = depth; sub_x0 = sub_x; sub_y0 = sub_y;
 +        }
 +        else if ((rev != rev0) || (depth != depth0) ||
 +                 (sub_x != sub_x0) || (sub_y != sub_y0))
 +        {
 +            can_use_ycc = false;
 +        }
 +    }
 +    if (!can_use_ycc)
 +    {
 +        return;
 +    }
 +
 +    bool use_ycc;
 +    if (!cod->get(Cycc,0,0,use_ycc))
 +    {
 +        cod->set(Cycc,0,0,use_ycc=true);
 +    }
 +    if (!use_ycc)
 +    {
 +        return;
 +    }
 +    float weight;
 +    if (cod->get(Clev_weights,0,0,weight) || cod->get(Cband_weights,0,0,weight))
 +    {
 +        // Weights already specified explicitly -> nothing to do
 +        return;
 +    }
 +
 +    // These example weights are adapted from numbers generated by Marcus Nadenau
 +    // at EPFL, for a viewing distance of 15 cm and a display resolution of
 +    // 300 DPI.
 +
 +    cod->parse_string("Cband_weights:C0="
 +        "{0.0901},{0.2758},{0.2758},"
 +        "{0.7018},{0.8378},{0.8378},{1}");
 +    cod->parse_string("Cband_weights:C1="
 +        "{0.0263},{0.0863},{0.0863},"
 +        "{0.1362},{0.2564},{0.2564},"
 +        "{0.3346},{0.4691},{0.4691},"
 +        "{0.5444},{0.6523},{0.6523},"
 +        "{0.7078},{0.7797},{0.7797},{1}");
 +    cod->parse_string("Cband_weights:C2="
 +        "{0.0773},{0.1835},{0.1835},"
 +        "{0.2598},{0.4130},{0.4130},"
 +        "{0.5040},{0.6464},{0.6464},"
 +        "{0.7220},{0.8254},{0.8254},"
 +        "{0.8769},{0.9424},{0.9424},{1}");
 +}
 +
 +/******************************************************************************/
 +/*                              transfer_bytes                                */
 +/******************************************************************************/
 +
 +void transfer_bytes(kdu_byte *dest, kdu_line_buf &src, int gap, int precision)
 +/* Transfers source samples from the supplied line buffer into the output
 +byte buffer, spacing successive output samples apart by `gap' bytes
 +(to allow for interleaving of colour components).  The function performs
 +all necessary level shifting, type conversion, rounding and truncation. */
 +{
 +    int width = src.get_width();
 +    if (src.get_buf32() != NULL)
 +    { // Decompressed samples have a 32-bit representation (integer or float)
 +        assert(precision >= 8); // Else would have used 16 bit representation
 +        kdu_sample32 *sp = src.get_buf32();
 +        if (!src.is_absolute())
 +        { // Transferring normalized floating point data.
 +            float scale16 = (float)(1<<16);
 +            kdu_int32 val;
 +
 +            for (; width > 0; width--, sp++, dest+=gap)
 +            {
 +                val = (kdu_int32)(sp->fval*scale16);
 +                val = (val+128)>>8; // May be faster than true rounding
 +                val += 128;
 +                if (val & ((0xffffffffU)<<8))
 +                {
 +                    val = (val < 0 ? 0 : 255);
 +                }
 +                *dest = (kdu_byte) val;
 +            }
 +        }
 +        else
 +        { // Transferring 32-bit absolute integers.
 +            kdu_int32 val;
 +            kdu_int32 downshift = precision-8;
 +            kdu_int32 offset = (1<<downshift)>>1;
 +
 +            for (; width > 0; width--, sp++, dest+=gap)
 +            {
 +                val = sp->ival;
 +                val = (val+offset)>>downshift;
 +                val += 128;
 +                if (val & ((0xffffffffU)<<8))
 +                {
 +                    val = (val < 0 ? 0 : 255);
 +                }
 +                *dest = (kdu_byte) val;
 +            }
 +        }
 +    }
 +    else
 +    { // Source data is 16 bits.
 +        kdu_sample16 *sp = src.get_buf16();
 +        if (!src.is_absolute())
 +        { // Transferring 16-bit fixed point quantities
 +            kdu_int16 val;
 +
 +            if (precision >= 8)
 +            { // Can essentially ignore the bit-depth.
 +                for (; width > 0; width--, sp++, dest+=gap)
 +                {
 +                    val = sp->ival;
 +                    val += (1<<(KDU_FIX_POINT-8))>>1;
 +                    val >>= (KDU_FIX_POINT-8);
 +                    val += 128;
 +                    if (val & ((0xffffffffU)<<8))
 +                    {
 +                        val = (val < 0 ? 0 : 255);
 +                    }
 +                    *dest = (kdu_byte) val;
 +                }
 +            }
 +            else
 +            { // Need to force zeros into one or more least significant bits.
 +                kdu_int16 downshift = KDU_FIX_POINT-precision;
 +                kdu_int16 upshift = 8-precision;
 +                kdu_int16 offset = 1<<(downshift-1);
 +
 +                for (; width > 0; width--, sp++, dest+=gap)
 +                {
 +                    val = sp->ival;
 +                    val = (val+offset)>>downshift;
 +                    val <<= upshift;
 +                    val += 128;
 +                    if (val & ((0xffffffffU)<<8))
 +                    {
 +                        val = (val < 0 ? 0 : 256 - (1<<upshift));
 +                    }
 +                    *dest = (kdu_byte) val;
 +                }
 +            }
 +        }
 +        else
 +        { // Transferring 16-bit absolute integers.
 +            kdu_int16 val;
 +
 +            if (precision >= 8)
 +            {
 +                kdu_int16 downshift = precision-8;
 +                kdu_int16 offset = (1<<downshift)>>1;
 +
 +                for (; width > 0; width--, sp++, dest+=gap)
 +                {
 +                    val = sp->ival;
 +                    val = (val+offset)>>downshift;
 +                    val += 128;
 +                    if (val & ((0xffffffffU)<<8))
 +                    {
 +                        val = (val < 0 ? 0 : 255);
 +                    }
 +                    *dest = (kdu_byte) val;
 +                }
 +            }
 +            else
 +            {
 +                kdu_int16 upshift = 8-precision;
 +
 +                for (; width > 0; width--, sp++, dest+=gap)
 +                {
 +                    val = sp->ival;
 +                    val <<= upshift;
 +                    val += 128;
 +                    if (val & ((0xffffffffU)<<8))
 +                    {
 +                        val = (val < 0 ? 0 : 256 - (1<<upshift));
 +                    }
 +                    *dest = (kdu_byte) val;
 +                }
 +            }
 +        }
 +    }
 +}
 +
 +LLKDUDecodeState::LLKDUDecodeState(kdu_tile tile, kdu_byte *buf, S32 row_gap,
 +                                   kdu_codestream* codestreamp)
 +{
 +    S32 c;
 +
 +    mTile = tile;
 +    mBuf = buf;
 +    mRowGap = row_gap;
 +
 +    mNumComponents = tile.get_num_components();
 +
 +    llassert(mNumComponents <= 4);
 +    mUseYCC = tile.get_ycc();
 +
 +    for (c = 0; c < 4; ++c)
 +    {
 +        mReversible[c] = false;
 +        mBitDepths[c] = 0;
 +    }
 +
 +    // Open tile-components and create processing engines and resources
 +    for (c = 0; c < mNumComponents; c++)
 +    {
 +        mComps[c] = mTile.access_component(c);
 +        mReversible[c] = mComps[c].get_reversible();
 +        mBitDepths[c] = mComps[c].get_bit_depth();
 +        kdu_resolution res = mComps[c].access_resolution(); // Get top resolution
 +        kdu_dims comp_dims; res.get_dims(comp_dims);
 +        if (c == 0)
 +        {
 +            mDims = comp_dims;
 +        }
 +        else
 +        {
 +            llassert(mDims == comp_dims); // Safety check; the caller has ensured this
 +        }
 +        bool use_shorts = (mComps[c].get_bit_depth(true) <= 16);
 +        mLines[c].pre_create(&mAllocator,mDims.size.x,mReversible[c],use_shorts,0,0);
 +        if (res.which() == 0) // No DWT levels used
 +        {
 +            mEngines[c] = kdu_decoder(res.access_subband(LL_BAND),&mAllocator,use_shorts);
 +        }
 +        else
 +        {
 +            mEngines[c] = kdu_synthesis(res,&mAllocator,use_shorts);
 +        }
 +    }
 +    mAllocator.finalize(*codestreamp); // Actually creates buffering resources
 +    for (c = 0; c < mNumComponents; c++)
 +    {
 +        mLines[c].create(); // Grabs resources from the allocator.
 +    }
 +}
 +
 +LLKDUDecodeState::~LLKDUDecodeState()
 +{
 +    // Cleanup
 +    for (S32 c = 0; c < mNumComponents; c++)
 +    {
 +        mEngines[c].destroy(); // engines are interfaces; no default destructors
 +    }
 +    mTile.close();
 +}
 +
 +bool LLKDUDecodeState::processTileDecode(F32 decode_time, bool limit_time)
 +/* Decompresses a tile, writing the data into the supplied byte buffer.
 +The buffer contains interleaved image components, if there are any.
 +Although you may think of the buffer as belonging entirely to this tile,
 +the `buf' pointer may actually point into a larger buffer representing
 +multiple tiles.  For this reason, `row_gap' is needed to identify the
 +separation between consecutive rows in the real buffer. */
 +{
 +    LL_PROFILE_ZONE_SCOPED_CATEGORY_TEXTURE;
 +    S32 c;
 +    // Now walk through the lines of the buffer, recovering them from the
 +    // relevant tile-component processing engines.
 +
 +    LLTimer decode_timer;
 +    while (mDims.size.y--)
 +    {
 +        {
 +            LL_PROFILE_ZONE_NAMED_CATEGORY_TEXTURE("kduptc - pull");
 +            for (c = 0; c < mNumComponents; c++)
 +            {
 +                mEngines[c].pull(mLines[c]);
 +            }
 +        }
 +
 +        if ((mNumComponents >= 3) && mUseYCC)
 +        {
 +            LL_PROFILE_ZONE_NAMED_CATEGORY_TEXTURE("kduptc - convert");
 +            kdu_convert_ycc_to_rgb(mLines[0],mLines[1],mLines[2]);
 +        }
 +
 +        {
 +            LL_PROFILE_ZONE_NAMED_CATEGORY_TEXTURE("kduptc - transfer");
 +            for (c = 0; c < mNumComponents; c++)
 +            {
 +                transfer_bytes(mBuf + c, mLines[c], mNumComponents, mBitDepths[c]);
 +            }
 +        }
 +        mBuf += mRowGap;
 +        if (mDims.size.y % 10)
 +        {
 +            if (limit_time && decode_timer.getElapsedTimeF32() > decode_time)
 +            {
 +                return false;
 +            }
 +        }
 +    }
 +    return true;
 +}
 +
 +// kdc_flow_control
 +
 +kdc_flow_control::kdc_flow_control (kdu_supp::kdu_image_in_base *img_in, kdu_codestream codestream)
 +{
 +    int n;
 +
 +    this->codestream = codestream;
 +    codestream.get_valid_tiles(valid_tile_indices);
 +    tile_idx = valid_tile_indices.pos;
 +    tile = codestream.open_tile(tile_idx,NULL);
 +
 +    // Set up the individual components
 +    num_components = codestream.get_num_components(true);
 +    components = new kdc_component_flow_control[num_components];
 +    count_delta = 0;
 +    kdc_component_flow_control *comp = components;
 +    for (n = 0; n < num_components; n++, comp++)
 +    {
 +        comp->line = NULL;
 +        comp->reader = img_in;
 +        kdu_coords subsampling;
 +        codestream.get_subsampling(n,subsampling,true);
 +        kdu_dims dims;
 +        codestream.get_tile_dims(tile_idx,n,dims,true);
 +        comp->vert_subsampling = subsampling.y;
 +        if ((n == 0) || (comp->vert_subsampling < count_delta))
 +        {
 +            count_delta = comp->vert_subsampling;
 +        }
 +        comp->ratio_counter = 0;
 +        comp->remaining_lines = comp->initial_lines = dims.size.y;
 +    }
 +    assert(num_components >= 0);
 +
 +    tile.set_components_of_interest(num_components);
 +    max_buffer_memory = engine.create(codestream,tile,false,NULL,false,1,NULL,NULL,false);
 +}
 +
 +kdc_flow_control::~kdc_flow_control()
 +{
 +    if (components != NULL)
 +    {
 +        delete[] components;
 +    }
 +    if (engine.exists())
 +    {
 +        engine.destroy();
 +    }
 +}
 +
 +bool kdc_flow_control::advance_components()
 +{
 +    bool found_line = false;
 +    while (!found_line)
 +    {
 +        bool all_done = true;
 +        kdc_component_flow_control *comp = components;
 +        for (int n = 0; n < num_components; n++, comp++)
 +        {
 +            assert(comp->ratio_counter >= 0);
 +            if (comp->remaining_lines > 0)
 +            {
 +                all_done = false;
 +                comp->ratio_counter -= count_delta;
 +                if (comp->ratio_counter < 0)
 +                {
 +                    found_line = true;
 +                    comp->line = engine.exchange_line(n,NULL,NULL);
 +                    assert(comp->line != NULL);
 +                    if (comp->line->get_width())
 +                    {
 +                        comp->reader->get(n,*(comp->line),0);
 +                    }
 +                }
 +            }
 +        }
 +        if (all_done)
 +        {
 +            return false;
 +        }
 +    }
 +    return true;
 +}
 +
 +void kdc_flow_control::process_components()
 +{
 +    kdc_component_flow_control *comp = components;
 +    for (int n = 0; n < num_components; n++, comp++)
 +    {
 +        if (comp->ratio_counter < 0)
 +        {
 +            comp->ratio_counter += comp->vert_subsampling;
 +            assert(comp->ratio_counter >= 0);
 +            assert(comp->remaining_lines > 0);
 +            comp->remaining_lines--;
 +            assert(comp->line != NULL);
 +            engine.exchange_line(n,comp->line,NULL);
 +            comp->line = NULL;
 +        }
 +    }
 +}
 diff --git a/indra/llkdu/llimagej2ckdu.h b/indra/llkdu/llimagej2ckdu.h index fe3902380c..8037c81868 100644 --- a/indra/llkdu/llimagej2ckdu.h +++ b/indra/llkdu/llimagej2ckdu.h @@ -1,25 +1,25 @@ -/**  +/**   * @file llimagej2ckdu.h   * @brief This is an implementation of JPEG2000 encode/decode using Kakadu   *   * $LicenseInfo:firstyear=2010&license=viewerlgpl$   * Second Life Viewer Source Code   * Copyright (C) 2010, Linden Research, Inc. - *  + *   * This library is free software; you can redistribute it and/or   * modify it under the terms of the GNU Lesser General Public   * License as published by the Free Software Foundation;   * version 2.1 of the License only. - *  + *   * This library is distributed in the hope that it will be useful,   * but WITHOUT ANY WARRANTY; without even the implied warranty of   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU   * Lesser General Public License for more details. - *  + *   * You should have received a copy of the GNU Lesser General Public   * License along with this library; if not, write to the Free Software   * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA - *  + *   * Linden Research, Inc., 945 Battery Street, San Francisco, CA  94111  USA   * $/LicenseInfo$   */ @@ -48,84 +48,84 @@ class LLKDUDecodeState;  class LLKDUMemSource;  class LLImageJ2CKDU : public LLImageJ2CImpl -{	 +{  public: -	enum ECodeStreamMode  -	{ -		MODE_FAST = 0, -		MODE_RESILIENT = 1, -		MODE_FUSSY = 2 -	}; -	LLImageJ2CKDU(); -	virtual ~LLImageJ2CKDU(); -	 +    enum ECodeStreamMode +    { +        MODE_FAST = 0, +        MODE_RESILIENT = 1, +        MODE_FUSSY = 2 +    }; +    LLImageJ2CKDU(); +    virtual ~LLImageJ2CKDU(); +  protected: -	virtual bool getMetadata(LLImageJ2C &base); -	virtual bool decodeImpl(LLImageJ2C &base, LLImageRaw &raw_image, F32 decode_time, S32 first_channel, S32 max_channel_count); -	virtual bool encodeImpl(LLImageJ2C &base, const LLImageRaw &raw_image, const char* comment_text, F32 encode_time=0.0, -								bool reversible=false); -	virtual bool initDecode(LLImageJ2C &base, LLImageRaw &raw_image, int discard_level = -1, int* region = NULL); -	virtual bool initEncode(LLImageJ2C &base, LLImageRaw &raw_image, int blocks_size = -1, int precincts_size = -1, int levels = 0); -	virtual std::string getEngineInfo() const; +    virtual bool getMetadata(LLImageJ2C &base); +    virtual bool decodeImpl(LLImageJ2C &base, LLImageRaw &raw_image, F32 decode_time, S32 first_channel, S32 max_channel_count); +    virtual bool encodeImpl(LLImageJ2C &base, const LLImageRaw &raw_image, const char* comment_text, F32 encode_time=0.0, +                                bool reversible=false); +    virtual bool initDecode(LLImageJ2C &base, LLImageRaw &raw_image, int discard_level = -1, int* region = NULL); +    virtual bool initEncode(LLImageJ2C &base, LLImageRaw &raw_image, int blocks_size = -1, int precincts_size = -1, int levels = 0); +    virtual std::string getEngineInfo() const;  private: -	bool initDecode(LLImageJ2C &base, LLImageRaw &raw_image, F32 decode_time, ECodeStreamMode mode, S32 first_channel, S32 max_channel_count, int discard_level = -1, int* region = NULL); -	void setupCodeStream(LLImageJ2C &base, bool keep_codestream, ECodeStreamMode mode); -	void cleanupCodeStream(); - -	// This method was public, but the only call to it is commented out in our -	// own initDecode() method. I (nat 2016-08-04) don't know what it does or -	// why. Even if it should be uncommented, it should probably still be -	// private. -//	void findDiscardLevelsBoundaries(LLImageJ2C &base); - -	// Helper class to hold a kdu_codestream, which is a handle to the -	// underlying implementation object. When CodeStreamHolder is reset() or -	// destroyed, it calls kdu_codestream::destroy() -- which kdu_codestream -	// itself does not. -	// -	// Call through it like a smart pointer using operator->(). -	// -	// Every RAII class must be noncopyable. For this we don't need move -	// support. -	class CodeStreamHolder: public boost::noncopyable -	{ -	public: -		~CodeStreamHolder() -		{ -			reset(); -		} - -		void reset() -		{ -			if (mCodeStream.exists()) -			{ -				mCodeStream.destroy(); -			} -		} - -		// for those few times when you need a raw kdu_codestream* -		kdu_core::kdu_codestream* get() { return &mCodeStream; } -		kdu_core::kdu_codestream* operator->() { return &mCodeStream; } - -	private: -		kdu_core::kdu_codestream mCodeStream; -	}; - -	// Encode variable -	std::unique_ptr<LLKDUMemSource> mInputp; -	CodeStreamHolder mCodeStreamp; -	std::unique_ptr<kdu_core::kdu_coords> mTPosp; // tile position -	std::unique_ptr<kdu_core::kdu_dims> mTileIndicesp; -	int mBlocksSize; -	int mPrecinctsSize; -	int mLevels; - -	// Temporary variables for in-progress decodes... -	// We don't own this LLImageRaw. We're simply pointing to an instance -	// passed into initDecode(). -	LLImageRaw *mRawImagep; -	std::unique_ptr<LLKDUDecodeState> mDecodeState; +    bool initDecode(LLImageJ2C &base, LLImageRaw &raw_image, F32 decode_time, ECodeStreamMode mode, S32 first_channel, S32 max_channel_count, int discard_level = -1, int* region = NULL); +    void setupCodeStream(LLImageJ2C &base, bool keep_codestream, ECodeStreamMode mode); +    void cleanupCodeStream(); + +    // This method was public, but the only call to it is commented out in our +    // own initDecode() method. I (nat 2016-08-04) don't know what it does or +    // why. Even if it should be uncommented, it should probably still be +    // private. +//  void findDiscardLevelsBoundaries(LLImageJ2C &base); + +    // Helper class to hold a kdu_codestream, which is a handle to the +    // underlying implementation object. When CodeStreamHolder is reset() or +    // destroyed, it calls kdu_codestream::destroy() -- which kdu_codestream +    // itself does not. +    // +    // Call through it like a smart pointer using operator->(). +    // +    // Every RAII class must be noncopyable. For this we don't need move +    // support. +    class CodeStreamHolder: public boost::noncopyable +    { +    public: +        ~CodeStreamHolder() +        { +            reset(); +        } + +        void reset() +        { +            if (mCodeStream.exists()) +            { +                mCodeStream.destroy(); +            } +        } + +        // for those few times when you need a raw kdu_codestream* +        kdu_core::kdu_codestream* get() { return &mCodeStream; } +        kdu_core::kdu_codestream* operator->() { return &mCodeStream; } + +    private: +        kdu_core::kdu_codestream mCodeStream; +    }; + +    // Encode variable +    std::unique_ptr<LLKDUMemSource> mInputp; +    CodeStreamHolder mCodeStreamp; +    std::unique_ptr<kdu_core::kdu_coords> mTPosp; // tile position +    std::unique_ptr<kdu_core::kdu_dims> mTileIndicesp; +    int mBlocksSize; +    int mPrecinctsSize; +    int mLevels; + +    // Temporary variables for in-progress decodes... +    // We don't own this LLImageRaw. We're simply pointing to an instance +    // passed into initDecode(). +    LLImageRaw *mRawImagep; +    std::unique_ptr<LLKDUDecodeState> mDecodeState;  };  #endif diff --git a/indra/llkdu/llkdumem.cpp b/indra/llkdu/llkdumem.cpp index 96e9da25d8..04ab033093 100644 --- a/indra/llkdu/llkdumem.cpp +++ b/indra/llkdu/llkdumem.cpp @@ -1,25 +1,25 @@ - /**  + /**   * @file llkdumem.cpp   * @brief Helper class for kdu memory management   *   * $LicenseInfo:firstyear=2010&license=viewerlgpl$   * Second Life Viewer Source Code   * Copyright (C) 2010, Linden Research, Inc. - *  + *   * This library is free software; you can redistribute it and/or   * modify it under the terms of the GNU Lesser General Public   * License as published by the Free Software Foundation;   * version 2.1 of the License only. - *  + *   * This library is distributed in the hope that it will be useful,   * but WITHOUT ANY WARRANTY; without even the implied warranty of   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU   * Lesser General Public License for more details. - *  + *   * You should have received a copy of the GNU Lesser General Public   * License along with this library; if not, write to the Free Software   * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA - *  + *   * Linden Research, Inc., 945 Battery Street, San Francisco, CA  94111  USA   * $/LicenseInfo$   */ @@ -36,164 +36,164 @@ using kd_supp_image_local::image_line_buf;  #endif  LLKDUMemIn::LLKDUMemIn(const U8 *data, -					   const U32 size, -					   const U16 width, -					   const U16 height, -					   const U8 in_num_components, -					   siz_params *siz) +                       const U32 size, +                       const U16 width, +                       const U16 height, +                       const U8 in_num_components, +                       siz_params *siz)  { -	U8 n; - -	first_comp_idx = 0; -	rows = height; -	cols = width; -	num_components = in_num_components; -	alignment_bytes = 0; - -	for (n = 0; n < 3; ++n) -	{ -		precision[n] = 0; -	} - -	for (n = 0; n < num_components; ++n) -	{ -		siz->set(Sdims,n,0,rows); -		siz->set(Sdims,n,1,cols); -		siz->set(Ssigned,n,0,false); -		siz->set(Sprecision,n,0,8); -	} -	incomplete_lines = NULL; -	free_lines = NULL; -	num_unread_rows = rows; - -	mData = data; -	mDataSize = size; -	mCurPos = 0; +    U8 n; + +    first_comp_idx = 0; +    rows = height; +    cols = width; +    num_components = in_num_components; +    alignment_bytes = 0; + +    for (n = 0; n < 3; ++n) +    { +        precision[n] = 0; +    } + +    for (n = 0; n < num_components; ++n) +    { +        siz->set(Sdims,n,0,rows); +        siz->set(Sdims,n,1,cols); +        siz->set(Ssigned,n,0,false); +        siz->set(Sprecision,n,0,8); +    } +    incomplete_lines = NULL; +    free_lines = NULL; +    num_unread_rows = rows; + +    mData = data; +    mDataSize = size; +    mCurPos = 0;  }  LLKDUMemIn::~LLKDUMemIn()  { -	if ((num_unread_rows > 0) || (incomplete_lines != NULL)) -	{ -		kdu_warning w; -		w << "Not all rows of image components " -			<< first_comp_idx << " through " -			<< first_comp_idx+num_components-1 -			<< " were consumed!"; -	} -	image_line_buf *tmp; -	while ((tmp=incomplete_lines) != NULL) -	{ -		incomplete_lines = tmp->next; -		delete tmp;  -	} -	while ((tmp=free_lines) != NULL) -	{ -		free_lines = tmp->next; -		delete tmp; -	} +    if ((num_unread_rows > 0) || (incomplete_lines != NULL)) +    { +        kdu_warning w; +        w << "Not all rows of image components " +            << first_comp_idx << " through " +            << first_comp_idx+num_components-1 +            << " were consumed!"; +    } +    image_line_buf *tmp; +    while ((tmp=incomplete_lines) != NULL) +    { +        incomplete_lines = tmp->next; +        delete tmp; +    } +    while ((tmp=free_lines) != NULL) +    { +        free_lines = tmp->next; +        delete tmp; +    }  }  bool LLKDUMemIn::get(int comp_idx, kdu_line_buf &line, int x_tnum)  { -	int idx = comp_idx - this->first_comp_idx; -	assert((idx >= 0) && (idx < num_components)); -	x_tnum = x_tnum*num_components+idx; -	image_line_buf *scan, *prev=NULL; -	for (scan = incomplete_lines; scan != NULL; prev = scan, scan = scan->next) -	{ -		assert(scan->next_x_tnum >= x_tnum); -		if (scan->next_x_tnum == x_tnum) -		{ -			break; -		} -	} -	if (scan == NULL) -	{ // Need to read a new image line. -		assert(x_tnum == 0); // Must consume in very specific order. -		if (num_unread_rows == 0) -		{ -	        return false; -		} -		if ((scan = free_lines) == NULL) -		{ -			scan = new image_line_buf(cols+3,num_components); -		} -		free_lines = scan->next; -		if (prev == NULL) -		{ -	        incomplete_lines = scan; -		} -		else -		{ -			prev->next = scan; -		} - -		// Copy from image buffer into scan. -		memcpy(scan->buf, mData+mCurPos, cols*num_components); -		mCurPos += cols*num_components; - -		num_unread_rows--; -		scan->accessed_samples = 0; -		scan->next_x_tnum = 0; -	} - -	assert((cols-scan->accessed_samples) >= line.get_width()); - -	int comp_offset = idx; -	kdu_byte *sp = scan->buf+num_components*scan->accessed_samples + comp_offset; -	int n=line.get_width(); - -	if (line.get_buf32() != NULL) -	{ -		kdu_sample32 *dp = line.get_buf32(); -		if (line.is_absolute()) -		{ // 32-bit absolute integers -			for (; n > 0; n--, sp+=num_components, dp++) -			{ -				dp->ival = ((kdu_int32)(*sp)) - 128; -			} -		} -		else -		{ // true 32-bit floats -			for (; n > 0; n--, sp+=num_components, dp++) -			{ -				dp->fval = (((float)(*sp)) / 256.0F) - 0.5F; -			} -		} -	} -	else -	{ -		kdu_sample16 *dp = line.get_buf16(); -		if (line.is_absolute()) -		{ // 16-bit absolute integers -			for (; n > 0; n--, sp+=num_components, dp++) -			{ -				dp->ival = ((kdu_int16)(*sp)) - 128; -			} -		} -		else -		{ // 16-bit normalized representation. -			for (; n > 0; n--, sp+=num_components, dp++) -			{ -				dp->ival = (((kdu_int16)(*sp)) - 128) << (KDU_FIX_POINT-8); -			} -		} -	} - -	scan->next_x_tnum++; -	if (idx == (num_components-1)) -	{ -		scan->accessed_samples += line.get_width(); -	} -	if (scan->accessed_samples == cols) -	{ // Send empty line to free list. -		assert(scan == incomplete_lines); -		incomplete_lines = scan->next; -		scan->next = free_lines; -		free_lines = scan; -	} +    int idx = comp_idx - this->first_comp_idx; +    assert((idx >= 0) && (idx < num_components)); +    x_tnum = x_tnum*num_components+idx; +    image_line_buf *scan, *prev=NULL; +    for (scan = incomplete_lines; scan != NULL; prev = scan, scan = scan->next) +    { +        assert(scan->next_x_tnum >= x_tnum); +        if (scan->next_x_tnum == x_tnum) +        { +            break; +        } +    } +    if (scan == NULL) +    { // Need to read a new image line. +        assert(x_tnum == 0); // Must consume in very specific order. +        if (num_unread_rows == 0) +        { +            return false; +        } +        if ((scan = free_lines) == NULL) +        { +            scan = new image_line_buf(cols+3,num_components); +        } +        free_lines = scan->next; +        if (prev == NULL) +        { +            incomplete_lines = scan; +        } +        else +        { +            prev->next = scan; +        } + +        // Copy from image buffer into scan. +        memcpy(scan->buf, mData+mCurPos, cols*num_components); +        mCurPos += cols*num_components; + +        num_unread_rows--; +        scan->accessed_samples = 0; +        scan->next_x_tnum = 0; +    } + +    assert((cols-scan->accessed_samples) >= line.get_width()); + +    int comp_offset = idx; +    kdu_byte *sp = scan->buf+num_components*scan->accessed_samples + comp_offset; +    int n=line.get_width(); + +    if (line.get_buf32() != NULL) +    { +        kdu_sample32 *dp = line.get_buf32(); +        if (line.is_absolute()) +        { // 32-bit absolute integers +            for (; n > 0; n--, sp+=num_components, dp++) +            { +                dp->ival = ((kdu_int32)(*sp)) - 128; +            } +        } +        else +        { // true 32-bit floats +            for (; n > 0; n--, sp+=num_components, dp++) +            { +                dp->fval = (((float)(*sp)) / 256.0F) - 0.5F; +            } +        } +    } +    else +    { +        kdu_sample16 *dp = line.get_buf16(); +        if (line.is_absolute()) +        { // 16-bit absolute integers +            for (; n > 0; n--, sp+=num_components, dp++) +            { +                dp->ival = ((kdu_int16)(*sp)) - 128; +            } +        } +        else +        { // 16-bit normalized representation. +            for (; n > 0; n--, sp+=num_components, dp++) +            { +                dp->ival = (((kdu_int16)(*sp)) - 128) << (KDU_FIX_POINT-8); +            } +        } +    } + +    scan->next_x_tnum++; +    if (idx == (num_components-1)) +    { +        scan->accessed_samples += line.get_width(); +    } +    if (scan->accessed_samples == cols) +    { // Send empty line to free list. +        assert(scan == incomplete_lines); +        incomplete_lines = scan->next; +        scan->next = free_lines; +        free_lines = scan; +    }    return true;  } diff --git a/indra/llkdu/llkdumem.h b/indra/llkdu/llkdumem.h index 09d81f38de..177a8ea976 100644 --- a/indra/llkdu/llkdumem.h +++ b/indra/llkdu/llkdumem.h @@ -1,25 +1,25 @@ -/**  +/**   * @file llkdumem.h   * @brief Helper class for kdu memory management   *   * $LicenseInfo:firstyear=2010&license=viewerlgpl$   * Second Life Viewer Source Code   * Copyright (C) 2010, Linden Research, Inc. - *  + *   * This library is free software; you can redistribute it and/or   * modify it under the terms of the GNU Lesser General Public   * License as published by the Free Software Foundation;   * version 2.1 of the License only. - *  + *   * This library is distributed in the hope that it will be useful,   * but WITHOUT ANY WARRANTY; without even the implied warranty of   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU   * Lesser General Public License for more details. - *  + *   * You should have received a copy of the GNU Lesser General Public   * License along with this library; if not, write to the Free Software   * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA - *  + *   * Linden Research, Inc., 945 Battery Street, San Francisco, CA  94111  USA   * $/LicenseInfo$   */ @@ -47,106 +47,106 @@  class LLKDUMemSource: public kdu_core::kdu_compressed_source  {  public: -	LLKDUMemSource(U8 *input_buffer, U32 size) -	{ -		mData = input_buffer; -		mSize = size; -		mCurPos = 0; -	} - -	~LLKDUMemSource() -	{ -	} - -	int read(kdu_core::kdu_byte *buf, int num_bytes) -	{ -		U32 num_out; -		num_out = num_bytes; - -		if ((mSize - mCurPos) < (U32)num_bytes) -		{ -			num_out = mSize -mCurPos; -		} -		memcpy(buf, mData + mCurPos, num_out); -		mCurPos += num_out; -		return num_out; -	} - -	void reset() -	{ -		mCurPos = 0; -	} +    LLKDUMemSource(U8 *input_buffer, U32 size) +    { +        mData = input_buffer; +        mSize = size; +        mCurPos = 0; +    } + +    ~LLKDUMemSource() +    { +    } + +    int read(kdu_core::kdu_byte *buf, int num_bytes) +    { +        U32 num_out; +        num_out = num_bytes; + +        if ((mSize - mCurPos) < (U32)num_bytes) +        { +            num_out = mSize -mCurPos; +        } +        memcpy(buf, mData + mCurPos, num_out); +        mCurPos += num_out; +        return num_out; +    } + +    void reset() +    { +        mCurPos = 0; +    }  private: -	U8 *mData; -	U32 mSize; -	U32 mCurPos; +    U8 *mData; +    U32 mSize; +    U32 mCurPos;  };  class LLKDUMemTarget: public kdu_core::kdu_compressed_target  {  public: -	LLKDUMemTarget(U8 *output_buffer, U32 &output_size, const U32 buffer_size) -	{ -		mData = output_buffer; -		mSize = buffer_size; -		mCurPos = 0; -		mOutputSize = &output_size; -	} - -	~LLKDUMemTarget() -	{ -	} - -	bool write(const kdu_core::kdu_byte *buf, int num_bytes) -	{ -		U32 num_out; -		num_out = num_bytes; - -		if ((mSize - mCurPos) < (U32)num_bytes) -		{ -			num_out = mSize - mCurPos; -			memcpy(mData + mCurPos, buf, num_out); -			return false; -		} -		memcpy(mData + mCurPos, buf, num_out); -		mCurPos += num_out; -		*mOutputSize = mCurPos; -		return true; -	} -	 +    LLKDUMemTarget(U8 *output_buffer, U32 &output_size, const U32 buffer_size) +    { +        mData = output_buffer; +        mSize = buffer_size; +        mCurPos = 0; +        mOutputSize = &output_size; +    } + +    ~LLKDUMemTarget() +    { +    } + +    bool write(const kdu_core::kdu_byte *buf, int num_bytes) +    { +        U32 num_out; +        num_out = num_bytes; + +        if ((mSize - mCurPos) < (U32)num_bytes) +        { +            num_out = mSize - mCurPos; +            memcpy(mData + mCurPos, buf, num_out); +            return false; +        } +        memcpy(mData + mCurPos, buf, num_out); +        mCurPos += num_out; +        *mOutputSize = mCurPos; +        return true; +    } +  private: -	U8 *mData; -	U32 mSize; -	U32 mCurPos; -	U32 *mOutputSize; +    U8 *mData; +    U32 mSize; +    U32 mCurPos; +    U32 *mOutputSize;  };  class LLKDUMemIn : public kdu_supp::kdu_image_in_base  {  public: -	LLKDUMemIn(const U8 *data, -				const U32 size, -				const U16 rows, -				const U16 cols, -				U8 in_num_components, -				kdu_core::siz_params *siz); -	~LLKDUMemIn(); +    LLKDUMemIn(const U8 *data, +                const U32 size, +                const U16 rows, +                const U16 cols, +                U8 in_num_components, +                kdu_core::siz_params *siz); +    ~LLKDUMemIn(); -	bool get(int comp_idx, kdu_core::kdu_line_buf &line, int x_tnum); +    bool get(int comp_idx, kdu_core::kdu_line_buf &line, int x_tnum);  private: -	const U8 *mData; -	int first_comp_idx; -	int num_components; -	int rows, cols; -	int alignment_bytes; // Number of 0's at end of each line. -	int precision[3]; -	kd_supp_image_local::image_line_buf *incomplete_lines; // Each "sample" represents a full pixel -	kd_supp_image_local::image_line_buf *free_lines; -	int num_unread_rows; - -	U32 mCurPos; -	U32 mDataSize; +    const U8 *mData; +    int first_comp_idx; +    int num_components; +    int rows, cols; +    int alignment_bytes; // Number of 0's at end of each line. +    int precision[3]; +    kd_supp_image_local::image_line_buf *incomplete_lines; // Each "sample" represents a full pixel +    kd_supp_image_local::image_line_buf *free_lines; +    int num_unread_rows; + +    U32 mCurPos; +    U32 mDataSize;  };  #endif diff --git a/indra/llkdu/tests/llimagej2ckdu_test.cpp b/indra/llkdu/tests/llimagej2ckdu_test.cpp index 16213b7f45..db81d60d9e 100644 --- a/indra/llkdu/tests/llimagej2ckdu_test.cpp +++ b/indra/llkdu/tests/llimagej2ckdu_test.cpp @@ -1,4 +1,4 @@ -/**  +/**   * @file llimagej2ckdu_test.cpp   * @author Merov Linden   * @date 2010-12-17 @@ -6,27 +6,27 @@   * $LicenseInfo:firstyear=2006&license=viewerlgpl$   * Second Life Viewer Source Code   * Copyright (C) 2010, Linden Research, Inc. - *  + *   * This library is free software; you can redistribute it and/or   * modify it under the terms of the GNU Lesser General Public   * License as published by the Free Software Foundation;   * version 2.1 of the License only. - *  + *   * This library is distributed in the hope that it will be useful,   * but WITHOUT ANY WARRANTY; without even the implied warranty of   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU   * Lesser General Public License for more details. - *  + *   * You should have received a copy of the GNU Lesser General Public   * License along with this library; if not, write to the Free Software   * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA - *  + *   * Linden Research, Inc., 945 Battery Street, San Francisco, CA  94111  USA   * $/LicenseInfo$   */  #include "linden_common.h" -// Class to test  +// Class to test  #include "llimagej2ckdu.h"  #if __clang__ @@ -44,7 +44,7 @@  // -------------------------------------------------------------------------------------------  // Stubbing: Declarations required to link and run the class being tested -// Notes:  +// Notes:  // * Add here stubbed implementation of the few classes and methods used in the class to be tested  // * Add as little as possible (let the link errors guide you)  // * Do not make any assumption as to how those classes or methods work (i.e. don't copy/paste code) @@ -53,7 +53,7 @@  // End Stubbing  // -------------------------------------------------------------------------------------------  // Stub the LL Image Classes -//LLTrace::MemStatHandle	LLImageBase::sMemStat("LLImage"); +//LLTrace::MemStatHandle    LLImageBase::sMemStat("LLImage");  LLImageRaw::LLImageRaw() { }  LLImageRaw::~LLImageRaw() { } @@ -241,81 +241,81 @@ void kdu_params::operator delete(void *) {}  namespace tut  { -	// Test wrapper declarations -	struct llimagej2ckdu_test -	{ -		// Derived test class -		class LLTestImageJ2CKDU : public LLImageJ2CKDU -		{ -		public: -			// Provides public access to some protected methods for testing -			bool callGetMetadata(LLImageJ2C &base) { return getMetadata(base); } -			bool callDecodeImpl(LLImageJ2C &base, LLImageRaw &raw_image, F32 decode_time, S32 first_channel, S32 max_channel_count) -			{ -				return decodeImpl(base, raw_image, decode_time, first_channel, max_channel_count); -			} -			bool callEncodeImpl(LLImageJ2C &base, const LLImageRaw &raw_image, const char* comment_text) -			{ -				return encodeImpl(base, raw_image, comment_text); -			} -		}; -		// Instance to be tested -		LLTestImageJ2CKDU* mImage; -		 -		// Constructor and destructor of the test wrapper -		llimagej2ckdu_test() -		{ -			mImage = new LLTestImageJ2CKDU; -		} -		~llimagej2ckdu_test() -		{ -			delete mImage; -		} -	}; -	 -	// Tut templating thingamagic: test group, object and test instance -	typedef test_group<llimagej2ckdu_test> llimagej2ckdu_t; -	typedef llimagej2ckdu_t::object llimagej2ckdu_object_t; -	tut::llimagej2ckdu_t tut_llimagej2ckdu("LLImageJ2CKDU"); -	 -	// --------------------------------------------------------------------------------------- -	// Test functions -	// Notes: -	// * Test as many as you possibly can without requiring a full blown simulation of everything -	// * The tests are executed in sequence so the test instance state may change between calls -	// * Remember that you cannot test private methods with tut -	// --------------------------------------------------------------------------------------- +    // Test wrapper declarations +    struct llimagej2ckdu_test +    { +        // Derived test class +        class LLTestImageJ2CKDU : public LLImageJ2CKDU +        { +        public: +            // Provides public access to some protected methods for testing +            bool callGetMetadata(LLImageJ2C &base) { return getMetadata(base); } +            bool callDecodeImpl(LLImageJ2C &base, LLImageRaw &raw_image, F32 decode_time, S32 first_channel, S32 max_channel_count) +            { +                return decodeImpl(base, raw_image, decode_time, first_channel, max_channel_count); +            } +            bool callEncodeImpl(LLImageJ2C &base, const LLImageRaw &raw_image, const char* comment_text) +            { +                return encodeImpl(base, raw_image, comment_text); +            } +        }; +        // Instance to be tested +        LLTestImageJ2CKDU* mImage; + +        // Constructor and destructor of the test wrapper +        llimagej2ckdu_test() +        { +            mImage = new LLTestImageJ2CKDU; +        } +        ~llimagej2ckdu_test() +        { +            delete mImage; +        } +    }; + +    // Tut templating thingamagic: test group, object and test instance +    typedef test_group<llimagej2ckdu_test> llimagej2ckdu_t; +    typedef llimagej2ckdu_t::object llimagej2ckdu_object_t; +    tut::llimagej2ckdu_t tut_llimagej2ckdu("LLImageJ2CKDU"); + +    // --------------------------------------------------------------------------------------- +    // Test functions +    // Notes: +    // * Test as many as you possibly can without requiring a full blown simulation of everything +    // * The tests are executed in sequence so the test instance state may change between calls +    // * Remember that you cannot test private methods with tut +    // --------------------------------------------------------------------------------------- -	// Test 1 : test getMetadata() -	template<> template<> -	void llimagej2ckdu_object_t::test<1>() -	{ -		LLImageJ2C* image = new LLImageJ2C(); -		bool res = mImage->callGetMetadata(*image); -		// Trying to set up a data stream with all NIL values and stubbed KDU will "work" and return true -		// Note that is linking with KDU, that call will throw an exception and fail, returning false -		ensure("getMetadata() test failed", res); -	} +    // Test 1 : test getMetadata() +    template<> template<> +    void llimagej2ckdu_object_t::test<1>() +    { +        LLImageJ2C* image = new LLImageJ2C(); +        bool res = mImage->callGetMetadata(*image); +        // Trying to set up a data stream with all NIL values and stubbed KDU will "work" and return true +        // Note that is linking with KDU, that call will throw an exception and fail, returning false +        ensure("getMetadata() test failed", res); +    } -	// Test 2 : test decodeImpl() -	template<> template<> -	void llimagej2ckdu_object_t::test<2>() -	{ -		LLImageJ2C* image = new LLImageJ2C(); -		LLImageRaw* raw = new LLImageRaw(); -		bool res = mImage->callDecodeImpl(*image, *raw, 0.0, 0, 0); -		// Decoding returns true whenever there's nothing else to do, including if decoding failed, so we'll get true here -		ensure("decodeImpl() test failed", res); -	} +    // Test 2 : test decodeImpl() +    template<> template<> +    void llimagej2ckdu_object_t::test<2>() +    { +        LLImageJ2C* image = new LLImageJ2C(); +        LLImageRaw* raw = new LLImageRaw(); +        bool res = mImage->callDecodeImpl(*image, *raw, 0.0, 0, 0); +        // Decoding returns true whenever there's nothing else to do, including if decoding failed, so we'll get true here +        ensure("decodeImpl() test failed", res); +    } -	// Test 3 : test encodeImpl() -	template<> template<> -	void llimagej2ckdu_object_t::test<3>() -	{ -		LLImageJ2C* image = new LLImageJ2C(); -		LLImageRaw* raw = new LLImageRaw(); -		bool res = mImage->callEncodeImpl(*image, *raw, NULL); -		// Encoding returns true unless an exception was raised, so we'll get true here though nothing really was done -		ensure("encodeImpl() test failed", res); -	} +    // Test 3 : test encodeImpl() +    template<> template<> +    void llimagej2ckdu_object_t::test<3>() +    { +        LLImageJ2C* image = new LLImageJ2C(); +        LLImageRaw* raw = new LLImageRaw(); +        bool res = mImage->callEncodeImpl(*image, *raw, NULL); +        // Encoding returns true unless an exception was raised, so we'll get true here though nothing really was done +        ensure("encodeImpl() test failed", res); +    }  } | 
