diff options
author | Andrey Lihatskiy <alihatskiy@productengine.com> | 2024-05-15 16:35:49 +0300 |
---|---|---|
committer | GitHub <noreply@github.com> | 2024-05-15 16:35:49 +0300 |
commit | e49dcb8d0c9f539997effb640e350d9d0689aae6 (patch) | |
tree | 1bf99eaccce6de17c62f13c6595f7f497548dc5c /indra/llkdu | |
parent | 531cd34f670170ade57f8813fe48012b61a1d3c2 (diff) | |
parent | 5f8a7374b9f18e0112d6749a9c845bd077a81acb (diff) |
Merge pull request #1476 from secondlife/marchcat/x-b-merge
Maint X -> Maint B merge
Diffstat (limited to 'indra/llkdu')
-rw-r--r-- | indra/llkdu/include_kdu_xxxx.h | 4 | ||||
-rw-r--r-- | indra/llkdu/llimagej2ckdu.cpp | 2324 | ||||
-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, 1568 insertions, 1568 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 2ad42d6b87..c51650a470 100644 --- a/indra/llkdu/llimagej2ckdu.cpp +++ b/indra/llkdu/llimagej2ckdu.cpp @@ -1,25 +1,25 @@ -/** +/** * @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$ */ @@ -51,8 +51,8 @@ namespace kdu_core 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 << "]"; + return out << "(" << dims.pos.x << "," << dims.pos.y << ")," + "[" << dims.size.x << "x" << dims.size.y << "]"; } } // namespace kdu_core @@ -103,36 +103,36 @@ std::string report_kdu_exception(kdu_exception mb) 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(); - + 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'; + + 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; + 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; }; // @@ -143,73 +143,73 @@ void set_default_colour_weights(kdu_params *siz); // Factory function: see declaration in llimagej2c.cpp LLImageJ2CImpl* fallbackCreateLLImageJ2CImpl() { - return new LLImageJ2CKDU(); + return new LLImageJ2CKDU(); } std::string LLImageJ2CKDU::getEngineInfo() const { - return llformat("KDU %s", KDU_CORE_VERSION); + 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); + 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; + 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; - } + 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; + std::string mType; }; struct LLKDUMessageWarning : public LLKDUMessage { - LLKDUMessageWarning(): - LLKDUMessage("Warning") - { - kdu_customize_warnings(this); - } + 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. @@ -217,55 +217,55 @@ 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()")); - } - } + 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) + mInputp(), + mCodeStreamp(), + mTPosp(), + mTileIndicesp(), + mRawImagep(NULL), + mDecodeState(), + mBlocksSize(-1), + mPrecinctsSize(-1), + mLevels(0) { } LLImageJ2CKDU::~LLImageJ2CKDU() { - cleanupCodeStream(); // in case destroyed before decode completed + cleanupCodeStream(); // in case destroyed before decode completed } // Stuff for new simple decode @@ -277,114 +277,114 @@ void transfer_bytes(kdu_byte *dest, kdu_line_buf &src, int gap, int precision); // as well, when that still existed, with keep_codestream true and MODE_FAST. void LLImageJ2CKDU::setupCodeStream(LLImageJ2C &base, bool keep_codestream, ECodeStreamMode mode) { - 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(); - } + 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(); + mInputp.reset(); + mDecodeState.reset(); + mCodeStreamp.reset(); + mTPosp.reset(); + mTileIndicesp.reset(); } // This is the protected virtual method called by LLImageJ2C::initDecode(). @@ -392,34 +392,34 @@ void LLImageJ2CKDU::cleanupCodeStream() // 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); + 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; + 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 @@ -428,83 +428,83 @@ bool LLImageJ2CKDU::initEncode(LLImageJ2C &base, LLImageRaw &raw_image, int bloc 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; + 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; } @@ -512,344 +512,344 @@ bool LLImageJ2CKDU::initDecode(LLImageJ2C &base, LLImageRaw &raw_image, F32 deco bool LLImageJ2CKDU::decodeImpl(LLImageJ2C &base, LLImageRaw &raw_image, F32 decode_time, S32 first_channel, S32 max_channel_count) { LL_PROFILE_ZONE_SCOPED_CATEGORY_TEXTURE; - ECodeStreamMode mode = MODE_FAST; - - 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 = decode_time - decode_timer.getElapsedTimeF32(); - // This is where we do the actual decode. If we run out of time, return false. - if (mDecodeState->processTileDecode(remaining_time, (decode_time > 0.0f))) - { - mDecodeState.reset(); - } - else - { - // Not finished decoding yet. - // setLastError("Ran out of time while decoding"); - 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; + ECodeStreamMode mode = MODE_FAST; + + 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 = decode_time - decode_timer.getElapsedTimeF32(); + // This is where we do the actual decode. If we run out of time, return false. + if (mDecodeState->processTileDecode(remaining_time, (decode_time > 0.0f))) + { + mDecodeState.reset(); + } + else + { + // Not finished decoding yet. + // setLastError("Ran out of time while decoding"); + 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; + // 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; - } + // *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; + } } /*****************************************************************************/ @@ -860,31 +860,31 @@ bool LLImageJ2CKDU::getMetadata(LLImageJ2C &base) // 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++) + 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]; + 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); + if (out->max_bytes < num_bytes) + out->set_max_bytes(num_bytes,false); + memcpy(out->byte_buffer,in->byte_buffer,(size_t) num_bytes); } |*==========================================================================*/ @@ -896,56 +896,56 @@ static void copy_block(kdu_block *in, kdu_block *out) // 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) + 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++) + 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_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_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++; - } - } + 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++; + } + } } } } @@ -959,174 +959,174 @@ copy_tile(kdu_tile tile_in, kdu_tile tile_out, int tnum_in, int tnum_out, // 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; + // 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}"); + 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}"); } /******************************************************************************/ @@ -1139,192 +1139,192 @@ 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; - } - } - } - } + 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) + 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. - } + 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(); + // 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) @@ -1336,13 +1336,13 @@ 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. + 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--) - { + LLTimer decode_timer; + while (mDims.size.y--) + { { LL_PROFILE_ZONE_NAMED_CATEGORY_TEXTURE("kduptc - pull"); for (c = 0; c < mNumComponents; c++) @@ -1351,11 +1351,11 @@ separation between consecutive rows in the real buffer. */ } } - if ((mNumComponents >= 3) && mUseYCC) - { + if ((mNumComponents >= 3) && mUseYCC) + { LL_PROFILE_ZONE_NAMED_CATEGORY_TEXTURE("kduptc - convert"); - kdu_convert_ycc_to_rgb(mLines[0],mLines[1],mLines[2]); - } + kdu_convert_ycc_to_rgb(mLines[0],mLines[1],mLines[2]); + } { LL_PROFILE_ZONE_NAMED_CATEGORY_TEXTURE("kduptc - transfer"); @@ -1364,116 +1364,116 @@ separation between consecutive rows in the real buffer. */ 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; + 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::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); + 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(); - } + 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; + 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; - } - } + 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); + } } |