diff options
author | Merov Linden <merov@lindenlab.com> | 2010-11-11 09:33:29 -0800 |
---|---|---|
committer | Merov Linden <merov@lindenlab.com> | 2010-11-11 09:33:29 -0800 |
commit | 94f44437d7e7ccb54f227e35523fa8a4e027d7d2 (patch) | |
tree | dd899aa6e98b2c10b22fa0db58cfca866a0322de /indra/llkdu | |
parent | ee8df375449e97476408417ec6dea0d6a8853d73 (diff) | |
parent | 0e07758b540374aa421d2938ce5ed13e2b9eefe4 (diff) |
STORM-151 : merge kdu static linking, add indra/llkdu implementation. Doesn't build yet.
Diffstat (limited to 'indra/llkdu')
-rw-r--r-- | indra/llkdu/CMakeLists.txt | 85 | ||||
-rw-r--r-- | indra/llkdu/llblockdata.cpp | 162 | ||||
-rw-r--r-- | indra/llkdu/llblockdata.h | 107 | ||||
-rw-r--r-- | indra/llkdu/llblockdecoder.cpp | 273 | ||||
-rw-r--r-- | indra/llkdu/llblockdecoder.h | 42 | ||||
-rw-r--r-- | indra/llkdu/llblockencoder.cpp | 343 | ||||
-rw-r--r-- | indra/llkdu/llblockencoder.h | 49 | ||||
-rw-r--r-- | indra/llkdu/llimagej2ckdu.cpp | 1006 | ||||
-rw-r--r-- | indra/llkdu/llimagej2ckdu.h | 92 | ||||
-rw-r--r-- | indra/llkdu/llkdumem.cpp | 392 | ||||
-rw-r--r-- | indra/llkdu/llkdumem.h | 167 |
11 files changed, 2718 insertions, 0 deletions
diff --git a/indra/llkdu/CMakeLists.txt b/indra/llkdu/CMakeLists.txt new file mode 100644 index 0000000000..2806af26c3 --- /dev/null +++ b/indra/llkdu/CMakeLists.txt @@ -0,0 +1,85 @@ +# -*- cmake -*- + +project(llkdu) + +# Visual Studio 2005 has a dumb bug that causes it to fail compilation +# of KDU if building with both optimisation and /WS (treat warnings as +# errors), even when the specific warnings that make it croak are +# disabled. + +set(VS_DISABLE_FATAL_WARNINGS ON) + +include(00-Common) +include(LLCommon) +include(LLImage) +include(LLKDU) +include(LLMath) +#include(LLVFS) +#include(Linking) + +include_directories( + ${LLCOMMON_INCLUDE_DIRS} + ${LLIMAGE_INCLUDE_DIRS} + ${KDU_INCLUDE_DIR} + ${LLMATH_INCLUDE_DIRS} + ) + +set(llkdu_SOURCE_FILES + kdc_flow_control.cpp + kde_flow_control.cpp + kdu_image.cpp + llblockdata.cpp + llblockdecoder.cpp + llblockencoder.cpp + llimagej2ckdu.cpp + llkdumem.cpp + ) + +set(llkdu_HEADER_FILES + CMakeLists.txt + + kdc_flow_control.h + kde_flow_control.h + kdu_image.h + kdu_image_local.h + llblockdata.h + llblockdecoder.h + llblockencoder.h + llimagej2ckdu.h + llkdumem.h + ) + +set_source_files_properties(${llkdu_HEADER_FILES} + PROPERTIES HEADER_FILE_ONLY TRUE) + +list(APPEND llkdu_SOURCE_FILES ${llkdu_HEADER_FILES}) + +if (WINDOWS) + # This turns off the warning about flow control ending in a destructor. + set_source_files_properties( + kdu_image.cpp llkdumem.cpp + PROPERTIES + COMPILE_FLAGS "/wd4702 /wd4722" + ) + + # This turns off the warning about sprintf in the following 2 files. + set_source_files_properties( + kde_flow_control.cpp kdc_flow_control.cpp + PROPERTIES + COMPILE_FLAGS /D_CRT_SECURE_NO_DEPRECATE + ) +endif (WINDOWS) + +if (LLKDU_LIBRARY) + add_library (${LLKDU_STATIC_LIBRARY} ${llkdu_SOURCE_FILES}) + + target_link_libraries( + ${LLKDU_STATIC_LIBRARY} +# ${LLIMAGE_LIBRARIES} +# ${LLVFS_LIBRARIES} + ${LLMATH_LIBRARIES} +# ${LLCOMMON_LIBRARIES} + ${KDU_LIBRARY} +# ${WINDOWS_LIBRARIES} + ) +endif (LLKDU_LIBRARY) diff --git a/indra/llkdu/llblockdata.cpp b/indra/llkdu/llblockdata.cpp new file mode 100644 index 0000000000..6a7bc3e6c4 --- /dev/null +++ b/indra/llkdu/llblockdata.cpp @@ -0,0 +1,162 @@ +/** + * @file llblockdata.cpp + * @brief Image block structure + * + * $LicenseInfo:firstyear=2010&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2010, Linden Research, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA + * $/LicenseInfo$ + */ + +#include "linden_common.h" + +#include "llblockdata.h" +#include "llmath.h" + +LLBlockData::LLBlockData(const U32 type) +{ + mType = type; + mWidth = 0; + mHeight = 0; + mRowStride = 0; + mData = NULL; +} + +void LLBlockData::setData(U8 *data, const U32 width, const U32 height, const U32 row_stride) +{ + mData = data; + mWidth = width; + mHeight = height; + if (row_stride) + { + mRowStride = row_stride; + } + else + { + mRowStride = width * 4; + } +} + +U32 LLBlockData::getType() const +{ + return mType; +} + + +U8 *LLBlockData::getData() const +{ + return mData; +} + +U32 LLBlockData::getSize() const +{ + return mWidth*mHeight; +} + +U32 LLBlockData::getWidth() const +{ + return mWidth; +} +U32 LLBlockData::getHeight() const +{ + return mHeight; +} + +U32 LLBlockData::getRowStride() const +{ + return mRowStride; +} + +LLBlockDataU32::LLBlockDataU32() : LLBlockData(BLOCK_TYPE_U32) +{ + mPrecision = 32; +} + +void LLBlockDataU32::setData(U32 *data, const U32 width, const U32 height, const U32 row_stride) +{ + LLBlockData::setData((U8 *)data, width, height, row_stride); +} + +U32 LLBlockDataU32::getSize() const +{ + return mWidth*mHeight*4; +} + +void LLBlockDataU32::setPrecision(const U32 bits) +{ + mPrecision = bits; +} + +U32 LLBlockDataU32::getPrecision() const +{ + return mPrecision; +} + +void LLBlockDataF32::setPrecision(const U32 bits) +{ + mPrecision = bits; +} + +U32 LLBlockDataF32::getPrecision() const +{ + return mPrecision; +} + +void LLBlockDataF32::setData(F32 *data, const U32 width, const U32 height, const U32 row_stride) +{ + LLBlockData::setData((U8 *)data, width, height, row_stride); +} + +void LLBlockDataF32::setMin(const F32 min) +{ + mMin = min; +} + +void LLBlockDataF32::setMax(const F32 max) +{ + mMax = max; +} + +void LLBlockDataF32::calcMinMax() +{ + U32 x, y; + + mMin = *(F32*)mData; + mMax = mMin; + + for (y = 0; y < mHeight; y++) + { + for (x = 0; x < mWidth; x++) + { + F32 data = *(F32*)(mData + y*mRowStride + x*4); + mMin = llmin(data, mMin); + mMax = llmax(data, mMax); + } + } +} + +F32 LLBlockDataF32::getMin() const +{ + return mMin; +} + +F32 LLBlockDataF32::getMax() const +{ + return mMax; +} diff --git a/indra/llkdu/llblockdata.h b/indra/llkdu/llblockdata.h new file mode 100644 index 0000000000..dcc847e7e2 --- /dev/null +++ b/indra/llkdu/llblockdata.h @@ -0,0 +1,107 @@ +/** + * @file llblockdata.h + * @brief Image block structure + * + * $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$ + */ + +#ifndef LL_LLBLOCKDATA_H +#define LL_LLBLOCKDATA_H + +#include "stdtypes.h" + +////////////////////////////////////////////////// +// +// This class stores all of the information about +// a single channel of raw data, either integer +// or floating point. +// +class LLBlockData +{ +protected: + U32 mType; + U32 mWidth; + U32 mHeight; + U32 mRowStride; + U8 *mData; +public: + enum + { + BLOCK_TYPE_U32 = 1, + BLOCK_TYPE_F32 = 2 + }; + + LLBlockData(const U32 type); + virtual ~LLBlockData() {} + + void setData(U8 *data, const U32 width, const U32 height, const U32 row_stride = 0); + + U32 getType() const; + U8 *getData() const; + virtual U32 getSize() const; + U32 getWidth() const; + U32 getHeight() const; + U32 getRowStride() const; +}; + +class LLBlockDataU32 : public LLBlockData +{ +protected: + U32 mPrecision; +public: + LLBlockDataU32(); + + void setData(U32 *data, const U32 width, const U32 height, const U32 row_stride = 0); + void setPrecision(const U32 bits); + + /*virtual*/ U32 getSize() const; + U32 getPrecision() const; +}; + +class LLBlockDataF32 : public LLBlockData +{ +protected: + U32 mPrecision; + F32 mMin; + F32 mMax; +public: + LLBlockDataF32() + : LLBlockData(LLBlockData::BLOCK_TYPE_F32), + mPrecision(0), + mMin(0.f), + mMax(0.f) + {}; + + void setData(F32 *data, const U32 width, const U32 height, const U32 row_stride = 0); + + void setPrecision(const U32 bits); + void setMin(const F32 min); + void setMax(const F32 max); + + void calcMinMax(); + + U32 getPrecision() const; + F32 getMin() const; + F32 getMax() const; +}; + +#endif // LL_LLBLOCKDATA_H diff --git a/indra/llkdu/llblockdecoder.cpp b/indra/llkdu/llblockdecoder.cpp new file mode 100644 index 0000000000..b4ddb2fba2 --- /dev/null +++ b/indra/llkdu/llblockdecoder.cpp @@ -0,0 +1,273 @@ + /** + * @file llblockdecoder.cpp + * @brief Image block decompression + * + * $LicenseInfo:firstyear=2010&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2010, Linden Research, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA + * $/LicenseInfo$ + */ + +#include "linden_common.h" + +#include "llblockdecoder.h" + +// KDU core header files +#include "kdu/kdu_elementary.h" +#include "kdu/kdu_messaging.h" +#include "kdu/kdu_params.h" +#include "kdu/kdu_compressed.h" +#include "kdu/kdu_sample_processing.h" + +// KDU utility functions. +#include "kde_flow_control.h" + +#include "llkdumem.h" + +#include "llblockdata.h" +#include "llerror.h" + + +BOOL LLBlockDecoder::decodeU32(LLBlockDataU32 &block_data, U8 *source_data, const U32 source_size) const +{ + U32 width, height; + + llassert(source_data); + + LLKDUMemSource source(source_data, source_size); + + source.reset(); + + kdu_codestream codestream; + + codestream.create(&source); + codestream.set_fast(); + + kdu_dims dims; + codestream.get_dims(0,dims); + llassert(codestream.get_num_components() == 1); + + width = dims.size.x; + height = dims.size.y; + + // Assumes U32 data. + U8 *output = block_data.getData(); + + kdu_dims tile_indices; + codestream.get_valid_tiles(tile_indices); + + kdu_coords tpos; + tpos.x = 0; + tpos.y = 0; + + // Now we are ready to walk through the tiles processing them one-by-one. + while (tpos.y < tile_indices.size.y) + { + while (tpos.x < tile_indices.size.x) + { + kdu_tile tile = codestream.open_tile(tpos+tile_indices.pos); + + 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 = block_data.getRowStride(); // inter-row separation + kdu_byte *buf = output + offset.y*row_gap + offset.x*4; + + kdu_tile_comp tile_comp = tile.access_component(0); + bool reversible = tile_comp.get_reversible(); + U32 precision = tile_comp.get_bit_depth(); + U32 precision_scale = 1 << precision; + llassert(precision >= 8); // Else would have used 16 bit representation + + kdu_resolution comp_res = tile_comp.access_resolution(); // Get top resolution + kdu_dims comp_dims; + comp_res.get_dims(comp_dims); + + bool use_shorts = (tile_comp.get_bit_depth(true) <= 16); + + kdu_line_buf line; + kdu_sample_allocator allocator; + kdu_pull_ifc engine; + + line.pre_create(&allocator, comp_dims.size.x, reversible, use_shorts); + if (res.which() == 0) // No DWT levels used + { + engine = kdu_decoder(res.access_subband(LL_BAND), &allocator, use_shorts); + } + else + { + engine = kdu_synthesis(res, &allocator, use_shorts); + } + allocator.finalize(); // Actually creates buffering resources + + line.create(); // Grabs resources from the allocator. + + // Do the actual processing + while (tile_dims.size.y--) + { + engine.pull(line, true); + int width = line.get_width(); + + llassert(line.get_buf32()); + llassert(!line.is_absolute()); + // Decompressed samples have a 32-bit representation (integer or float) + kdu_sample32 *sp = line.get_buf32(); + // Transferring normalized floating point data. + U32 *dest_u32 = (U32 *)buf; + for (; width > 0; width--, sp++, dest_u32++) + { + if (sp->fval < -0.5f) + { + *dest_u32 = 0; + } + else + { + *dest_u32 = (U32)((sp->fval + 0.5f)*precision_scale); + } + } + buf += row_gap; + } + engine.destroy(); + tile.close(); + tpos.x++; + } + tpos.y++; + tpos.x = 0; + } + codestream.destroy(); + + return TRUE; +} + +BOOL LLBlockDecoder::decodeF32(LLBlockDataF32 &block_data, U8 *source_data, const U32 source_size, const F32 min, const F32 max) const +{ + U32 width, height; + F32 range, range_inv, float_offset; + bool use_shorts = false; + + range = max - min; + range_inv = 1.f / range; + float_offset = 0.5f*(max + min); + + llassert(source_data); + + LLKDUMemSource source(source_data, source_size); + + source.reset(); + + kdu_codestream codestream; + + codestream.create(&source); + codestream.set_fast(); + + kdu_dims dims; + codestream.get_dims(0,dims); + llassert(codestream.get_num_components() == 1); + + width = dims.size.x; + height = dims.size.y; + + // Assumes F32 data. + U8 *output = block_data.getData(); + + kdu_dims tile_indices; + codestream.get_valid_tiles(tile_indices); + + kdu_coords tpos; + tpos.x = 0; + tpos.y = 0; + + // Now we are ready to walk through the tiles processing them one-by-one. + while (tpos.y < tile_indices.size.y) + { + while (tpos.x < tile_indices.size.x) + { + kdu_tile tile = codestream.open_tile(tpos+tile_indices.pos); + + 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 = block_data.getRowStride(); // inter-row separation + kdu_byte *buf = output + offset.y*row_gap + offset.x*4; + + kdu_tile_comp tile_comp = tile.access_component(0); + bool reversible = tile_comp.get_reversible(); + + kdu_resolution comp_res = tile_comp.access_resolution(); // Get top resolution + kdu_dims comp_dims; + comp_res.get_dims(comp_dims); + + kdu_line_buf line; + kdu_sample_allocator allocator; + kdu_pull_ifc engine; + + line.pre_create(&allocator, comp_dims.size.x, reversible, use_shorts); + if (res.which() == 0) // No DWT levels used + { + engine = kdu_decoder(res.access_subband(LL_BAND), &allocator, use_shorts); + } + else + { + engine = kdu_synthesis(res, &allocator, use_shorts); + } + allocator.finalize(); // Actually creates buffering resources + + line.create(); // Grabs resources from the allocator. + + // Do the actual processing + while (tile_dims.size.y--) + { + engine.pull(line, true); + int width = line.get_width(); + + llassert(line.get_buf32()); + llassert(!line.is_absolute()); + // Decompressed samples have a 32-bit representation (integer or float) + kdu_sample32 *sp = line.get_buf32(); + // Transferring normalized floating point data. + F32 *dest_f32 = (F32 *)buf; + for (; width > 0; width--, sp++, dest_f32++) + { + if (sp->fval < -0.5f) + { + *dest_f32 = min; + } + else if (sp->fval > 0.5f) + { + *dest_f32 = max; + } + else + { + *dest_f32 = (sp->fval) * range + float_offset; + } + } + buf += row_gap; + } + engine.destroy(); + tile.close(); + tpos.x++; + } + tpos.y++; + tpos.x = 0; + } + codestream.destroy(); + return TRUE; +} diff --git a/indra/llkdu/llblockdecoder.h b/indra/llkdu/llblockdecoder.h new file mode 100644 index 0000000000..97959d338e --- /dev/null +++ b/indra/llkdu/llblockdecoder.h @@ -0,0 +1,42 @@ +/** + * @file llblockdecoder.h + * @brief Image block decompression + * + * $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$ + */ + +#ifndef LL_LLBLOCKDECODER_H +#define LL_LLBLOCKDECODER_H + +#include "stdtypes.h" + +class LLBlockDataU32; +class LLBlockDataF32; + +class LLBlockDecoder +{ +public: + BOOL decodeU32(LLBlockDataU32 &block_data, U8 *source_data, const U32 source_size) const; + BOOL decodeF32(LLBlockDataF32 &block_data, U8 *source_data, const U32 source_size, const F32 min, const F32 max) const; +}; + +#endif // LL_LLBLOCKDECODER_H diff --git a/indra/llkdu/llblockencoder.cpp b/indra/llkdu/llblockencoder.cpp new file mode 100644 index 0000000000..f19841e36f --- /dev/null +++ b/indra/llkdu/llblockencoder.cpp @@ -0,0 +1,343 @@ + /** + * @file llblockencoder.cpp + * @brief Image block compression + * + * $LicenseInfo:firstyear=2010&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2010, Linden Research, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA + * $/LicenseInfo$ + */ + +#include "linden_common.h" + +#include "llblockencoder.h" + +// KDU core header files +#include "kdu/kdu_elementary.h" +#include "kdu/kdu_messaging.h" +#include "kdu/kdu_params.h" +#include "kdu/kdu_compressed.h" +#include "kdu/kdu_sample_processing.h" + +// KDU utility functions. +#include "kdc_flow_control.h" + +#include "llkdumem.h" + +#include "llblockdata.h" +#include "llerror.h" + +LLBlockEncoder::LLBlockEncoder() +{ + mBPP = 0.f; +} + +U8 *LLBlockEncoder::encode(const LLBlockData &block_data, U32 &output_size) const +{ + switch (block_data.getType()) + { + case LLBlockData::BLOCK_TYPE_U32: + { + LLBlockDataU32 &bd_u32 = (LLBlockDataU32 &)block_data; + return encodeU32(bd_u32, output_size); + } + case LLBlockData::BLOCK_TYPE_F32: + { + LLBlockDataF32 &bd_f32 = (LLBlockDataF32 &)block_data; + return encodeF32(bd_f32, output_size); + } + default: + llerrs << "Unsupported block type!" << llendl; + output_size = 0; + return NULL; + } +} + +U8 *LLBlockEncoder::encodeU32(const LLBlockDataU32 &block_data, U32 &output_size) const +{ + // OK, for now, just use the standard KDU encoder, with a single channel + // integer channel. + + // Collect simple arguments. + bool allow_rate_prediction, allow_shorts, mem, quiet, no_weights; + + allow_rate_prediction = true; + allow_shorts = false; + no_weights = false; + bool use_absolute = false; + mem = false; + quiet = false; + + // Set codestream options + siz_params siz; + S16 precision = block_data.getPrecision(); + + siz.set(Sdims,0,0,(U16)block_data.getHeight()); + siz.set(Sdims,0,1,(U16)block_data.getWidth()); + siz.set(Ssigned,0,0,false); + siz.set(Scomponents,0,0,1); + siz.set(Sprecision,0,0, precision); + + // Construct the `kdu_codestream' object and parse all remaining arguments. + output_size = block_data.getSize(); + if (output_size < 1000) + { + output_size = 1000; + } + + U8 *output_buffer = new U8[output_size]; + + LLKDUMemTarget output(output_buffer, output_size, block_data.getSize()); + + kdu_codestream codestream; + codestream.create(&siz,&output); + + codestream.access_siz()->parse_string("Clayers=1"); + codestream.access_siz()->finalize_all(); + + kdu_tile tile = codestream.open_tile(kdu_coords(0,0)); + + // Open tile-components and create processing engines and resources + kdu_dims dims; + kdu_sample_allocator allocator; + kdu_tile_comp tile_comp; + kdu_line_buf line; + kdu_push_ifc engine; + + tile_comp = tile.access_component(0); + kdu_resolution res = tile_comp.access_resolution(); // Get top resolution + + res.get_dims(dims); + + line.pre_create(&allocator,dims.size.x, use_absolute, allow_shorts); + + if (res.which() == 0) // No DWT levels (should not occur in this example) + { + engine = kdu_encoder(res.access_subband(LL_BAND),&allocator, use_absolute); + } + else + { + engine = kdu_analysis(res,&allocator, use_absolute); + } + + allocator.finalize(); // Actually creates buffering resources + line.create(); // Grabs resources from the allocator. + + // Now walk through the lines of the buffer, pushing them into the + // relevant tile-component processing engines. + + U32 *source_u32 = NULL; + F32 scale_inv = 1.f / (1 << precision); + + S32 y; + for (y = 0; y < dims.size.y; y++) + { + source_u32 = (U32*)(block_data.getData() + y * block_data.getRowStride()); + kdu_sample32 *dest = line.get_buf32(); + for (S32 n=dims.size.x; n > 0; n--, dest++, source_u32++) + { + // Just pack it in, for now. + dest->fval = (F32)(*source_u32) * scale_inv - 0.5f;// - 0.5f; + } + engine.push(line, true); + } + + // Cleanup + engine.destroy(); // engines are interfaces; no default destructors + + // Produce the final compressed output. + kdu_long layer_bytes[1] = {0}; + + layer_bytes[0] = (kdu_long) (mBPP*block_data.getWidth()*block_data.getHeight()); + // Here we are not requesting specific sizes for any of the 12 + // quality layers. As explained in the description of + // "kdu_codestream::flush" (see "kdu_compressed.h"), the rate allocator + // will then assign the layers in such a way as to achieve roughly + // two quality layers per octave change in bit-rate, with the final + // layer reaching true lossless quality. + + codestream.flush(layer_bytes,1); + // You can see how many bytes were assigned + // to each quality layer by looking at the entries of `layer_bytes' here. + // The flush function can do a lot of interesting things which you may + // want to spend some time looking into. In addition to targeting + // specific bit-rates for each quality layer, it can be configured to + // use rate-distortion slope thresholds of your choosing and to return + // the thresholds which it finds to be best for any particular set of + // target layer sizes. This opens the door to feedback-oriented rate + // control for video. You should also look into the + // "kdu_codestream::set_max_bytes" and + // "kdu_codestream::set_min_slope_threshold" functions which can be + // used to significantly speed up compression. + codestream.destroy(); // All done: simple as that. + + // Now that we're done encoding, create the new data buffer for the compressed + // image and stick it there. + + U8 *output_data = new U8[output_size]; + + memcpy(output_data, output_buffer, output_size); + + output.close(); // Not really necessary here. + + return output_data; +} + +U8 *LLBlockEncoder::encodeF32(const LLBlockDataF32 &block_data, U32 &output_size) const +{ + // OK, for now, just use the standard KDU encoder, with a single channel + // integer channel. + + // Collect simple arguments. + bool allow_rate_prediction, allow_shorts, mem, quiet, no_weights; + + allow_rate_prediction = true; + allow_shorts = false; + no_weights = false; + bool use_absolute = false; + mem = false; + quiet = false; + + F32 min, max, range, range_inv, offset; + min = block_data.getMin(); + max = block_data.getMax(); + range = max - min; + range_inv = 1.f / range; + offset = 0.5f*(max + min); + + // Set codestream options + siz_params siz; + S16 precision = block_data.getPrecision(); // Assume precision is always 32 bits for floating point. + + siz.set(Sdims,0,0,(U16)block_data.getHeight()); + siz.set(Sdims,0,1,(U16)block_data.getWidth()); + siz.set(Ssigned,0,0,false); + siz.set(Scomponents,0,0,1); + siz.set(Sprecision,0,0, precision); + + // Construct the `kdu_codestream' object and parse all remaining arguments. + output_size = block_data.getSize(); + if (output_size < 1000) + { + output_size = 1000; + } + + U8 *output_buffer = new U8[output_size*2]; + + LLKDUMemTarget output(output_buffer, output_size, block_data.getSize()); + + kdu_codestream codestream; + codestream.create(&siz,&output); + + codestream.access_siz()->parse_string("Clayers=1"); + codestream.access_siz()->finalize_all(); + + kdu_tile tile = codestream.open_tile(kdu_coords(0,0)); + + // Open tile-components and create processing engines and resources + kdu_dims dims; + kdu_sample_allocator allocator; + kdu_tile_comp tile_comp; + kdu_line_buf line; + kdu_push_ifc engine; + + tile_comp = tile.access_component(0); + kdu_resolution res = tile_comp.access_resolution(); // Get top resolution + + res.get_dims(dims); + + line.pre_create(&allocator,dims.size.x, use_absolute, allow_shorts); + + if (res.which() == 0) // No DWT levels (should not occur in this example) + { + engine = kdu_encoder(res.access_subband(LL_BAND),&allocator, use_absolute); + } + else + { + engine = kdu_analysis(res,&allocator, use_absolute); + } + + allocator.finalize(); // Actually creates buffering resources + line.create(); // Grabs resources from the allocator. + + // Now walk through the lines of the buffer, pushing them into the + // relevant tile-component processing engines. + + F32 *source_f32 = NULL; + + S32 y; + for (y = 0; y < dims.size.y; y++) + { + source_f32 = (F32*)(block_data.getData() + y * block_data.getRowStride()); + kdu_sample32 *dest = line.get_buf32(); + for (S32 n=dims.size.x; n > 0; n--, dest++, source_f32++) + { + dest->fval = ((*source_f32) - offset) * range_inv; + } + engine.push(line, true); + } + + // Cleanup + engine.destroy(); // engines are interfaces; no default destructors + + // Produce the final compressed output. + kdu_long layer_bytes[1] = {0}; + + layer_bytes[0] = (kdu_long) (mBPP*block_data.getWidth()*block_data.getHeight()); + // Here we are not requesting specific sizes for any of the 12 + // quality layers. As explained in the description of + // "kdu_codestream::flush" (see "kdu_compressed.h"), the rate allocator + // will then assign the layers in such a way as to achieve roughly + // two quality layers per octave change in bit-rate, with the final + // layer reaching true lossless quality. + + codestream.flush(layer_bytes,1); + // You can see how many bytes were assigned + // to each quality layer by looking at the entries of `layer_bytes' here. + // The flush function can do a lot of interesting things which you may + // want to spend some time looking into. In addition to targeting + // specific bit-rates for each quality layer, it can be configured to + // use rate-distortion slope thresholds of your choosing and to return + // the thresholds which it finds to be best for any particular set of + // target layer sizes. This opens the door to feedback-oriented rate + // control for video. You should also look into the + // "kdu_codestream::set_max_bytes" and + // "kdu_codestream::set_min_slope_threshold" functions which can be + // used to significantly speed up compression. + codestream.destroy(); // All done: simple as that. + + // Now that we're done encoding, create the new data buffer for the compressed + // image and stick it there. + + U8 *output_data = new U8[output_size]; + + memcpy(output_data, output_buffer, output_size); + + output.close(); // Not really necessary here. + + delete[] output_buffer; + + return output_data; +} + + +void LLBlockEncoder::setBPP(const F32 bpp) +{ + mBPP = bpp; +} diff --git a/indra/llkdu/llblockencoder.h b/indra/llkdu/llblockencoder.h new file mode 100644 index 0000000000..21381a27fa --- /dev/null +++ b/indra/llkdu/llblockencoder.h @@ -0,0 +1,49 @@ +/** + * @file llblockencoder.h + * @brief Image block compression + * + * $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$ + */ + +#ifndef LL_LLBLOCKENCODER_H +#define LL_LLBLOCKENCODER_H + +#include "stdtypes.h" + +class LLBlockData; +class LLBlockDataU32; +class LLBlockDataF32; + +class LLBlockEncoder +{ + F32 mBPP; // bits per point +public: + LLBlockEncoder(); + U8 *encode(const LLBlockData &block_data, U32 &output_size) const; + U8 *encodeU32(const LLBlockDataU32 &block_data, U32 &output_size) const; + U8 *encodeF32(const LLBlockDataF32 &block_data, U32 &output_size) const; + + void setBPP(const F32 bpp); +}; + +#endif // LL_LLBLOCKENCODER_H + diff --git a/indra/llkdu/llimagej2ckdu.cpp b/indra/llkdu/llimagej2ckdu.cpp new file mode 100644 index 0000000000..1785aa111d --- /dev/null +++ b/indra/llkdu/llimagej2ckdu.cpp @@ -0,0 +1,1006 @@ + /** + * @file llimagej2ckdu.cpp + * @brief This is an implementation of JPEG2000 encode/decode using Kakadu + * + * $LicenseInfo:firstyear=2010&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2010, Linden Research, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA + * $/LicenseInfo$ + */ + +#include "linden_common.h" +#include "llimagej2ckdu.h" + +// KDU utility functions. +#include "kde_flow_control.h" +#include "kdc_flow_control.h" + +#include "lltimer.h" +#include "llpointer.h" +#include "llkdumem.h" + + +// +// Kakadu specific implementation +// +void set_default_colour_weights(kdu_params *siz); + +const char* engineInfoLLImageJ2CKDU() +{ + return "KDU"; +} + +LLImageJ2CKDU* createLLImageJ2CKDU() +{ + return new LLImageJ2CKDU(); +} + +void destroyLLImageJ2CKDU(LLImageJ2CKDU* kdu) +{ + delete kdu; + kdu = NULL; +} + +LLImageJ2CImpl* fallbackCreateLLImageJ2CImpl() +{ + return new LLImageJ2CKDU(); +} + +void fallbackDestroyLLImageJ2CImpl(LLImageJ2CImpl* impl) +{ + delete impl; + impl = NULL; +} + +const char* fallbackEngineInfoLLImageJ2CImpl() +{ + return engineInfoLLImageJ2CKDU(); +} + +class LLKDUDecodeState +{ +public: + + 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; + + LLKDUDecodeState(kdu_tile tile, kdu_byte *buf, S32 row_gap); + ~LLKDUDecodeState(); + BOOL processTileDecode(F32 decode_time, BOOL limit_time = TRUE); + +public: + int *AssignLayerBytes(siz_params *siz, int &num_specs); + + void setupCodeStream(BOOL keep_codestream, LLImageJ2CKDU::ECodeStreamMode mode); + BOOL initDecode(LLImageRaw &raw_image, F32 decode_time, LLImageJ2CKDU::ECodeStreamMode mode, S32 first_channel, S32 max_channel_count ); +}; + +void ll_kdu_error( void ) +{ + // *FIX: This exception is bad, bad, bad. It gets thrown from a + // destructor which can lead imediate program termination! + throw "ll_kdu_error() throwing an exception"; +} +// Stuff for new kdu error handling. + +class LLKDUMessageWarning : public kdu_message +{ +public: + /*virtual*/ void put_text(const char *string); + + static LLKDUMessageWarning sDefaultMessage; +}; + +class LLKDUMessageError : public kdu_message +{ +public: + /*virtual*/ void put_text(const char *string); + /*virtual*/ void flush(bool end_of_message=false); + static LLKDUMessageError sDefaultMessage; +}; + +void LLKDUMessageWarning::put_text(const char *s) +{ + llinfos << "KDU Warning: " << s << llendl; +} + +void LLKDUMessageError::put_text(const char *s) +{ + llinfos << "KDU Error: " << s << llendl; +} + +void LLKDUMessageError::flush(bool end_of_message) +{ + if( end_of_message ) + { + throw "KDU throwing an exception"; + } +} + +LLKDUMessageWarning LLKDUMessageWarning::sDefaultMessage; +LLKDUMessageError LLKDUMessageError::sDefaultMessage; +static bool kdu_message_initialized = false; + +LLImageJ2CKDU::LLImageJ2CKDU() : LLImageJ2CImpl(), +mInputp(NULL), +mCodeStreamp(NULL), +mTPosp(NULL), +mTileIndicesp(NULL), +mRawImagep(NULL), +mDecodeState(NULL) +{ +} + +LLImageJ2CKDU::~LLImageJ2CKDU() +{ + cleanupCodeStream(); // in case destroyed before decode completed +} + +// Stuff for new simple decode +void transfer_bytes(kdu_byte *dest, kdu_line_buf &src, int gap, int precision); + +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 + // + if (!kdu_message_initialized) + { + kdu_message_initialized = true; + kdu_customize_errors(&LLKDUMessageError::sDefaultMessage); + kdu_customize_warnings(&LLKDUMessageWarning::sDefaultMessage); + } + + if (mCodeStreamp) + { + mCodeStreamp->destroy(); + delete mCodeStreamp; + mCodeStreamp = NULL; + } + + + if (!mInputp) + { + llassert(base.getData()); + // The compressed data has been loaded. + // Setup the source for the codestrea + mInputp = new LLKDUMemSource(base.getData(), data_size); + } + + llassert(mInputp); + mInputp->reset(); + mCodeStreamp = new kdu_codestream; + + mCodeStreamp->create(mInputp); + + + // Set the maximum number of bytes to use from the codestrea + mCodeStreamp->set_max_bytes(max_bytes); + + // 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 the 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(); + + if (components >= 3) + { // Check that components have consistent dimensions (for PPM file) + kdu_dims dims1; mCodeStreamp->get_dims(1,dims1); + kdu_dims dims2; mCodeStreamp->get_dims(2,dims2); + if ((dims1 != dims) || (dims2 != dims)) + { + llerrs << "Components don't have matching dimensions!" << llendl; + } + } + + base.setSize(dims.size.x, dims.size.y, components); + + if (!keep_codestream) + { + mCodeStreamp->destroy(); + delete mCodeStreamp; + mCodeStreamp = NULL; + delete mInputp; + mInputp = NULL; + } +} + +void LLImageJ2CKDU::cleanupCodeStream() +{ + delete mInputp; + mInputp = NULL; + + delete mDecodeState; + mDecodeState = NULL; + + if (mCodeStreamp) + { + mCodeStreamp->destroy(); + delete mCodeStreamp; + mCodeStreamp = NULL; + } + + delete mTPosp; + mTPosp = NULL; + + delete mTileIndicesp; + mTileIndicesp = NULL; +} + +BOOL LLImageJ2CKDU::initDecode(LLImageJ2C &base, LLImageRaw &raw_image, F32 decode_time, ECodeStreamMode mode, S32 first_channel, S32 max_channel_count ) +{ + 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 + { + base.updateRawDiscardLevel(); + setupCodeStream(base, TRUE, mode); + + /* + // + // Not being used OpenJPEG doesn't support it, just deprecate it. + // + + // Find the Linden Lab comment in the chain of comments + kdu_codestream_comment comment; + comment = mCodeStreamp->get_comment(); + while (comment.get_text()) + { + const char* text = comment.get_text(); + if( text == strstr( text, LINDEN_J2C_COMMENT_PREFIX) ) + { + mCommentText = text; + break; + } + //llinfos << "CS comment: " << comment.get_text() << llendl; + comment = mCodeStreamp->get_comment(comment); + } + */ + + mRawImagep = &raw_image; + mCodeStreamp->change_appearance(false, true, false); + mCodeStreamp->apply_input_restrictions(first_channel,max_channel_count,base.getRawDiscardLevel(),0,NULL); + + kdu_dims dims; mCodeStreamp->get_dims(0,dims); + S32 channels = base.getComponents() - first_channel; + if( channels > max_channel_count ) + { + channels = max_channel_count; + } + raw_image.resize(dims.size.x, dims.size.y, channels); + + // llinfos << "Resizing to " << dims.size.x << ":" << dims.size.y << llendl; + if (!mTileIndicesp) + { + mTileIndicesp = new kdu_dims; + } + mCodeStreamp->get_valid_tiles(*mTileIndicesp); + if (!mTPosp) + { + mTPosp = new kdu_coords; + mTPosp->y = 0; + mTPosp->x = 0; + } + } + catch (const char* msg) + { + base.setLastError(ll_safe_string(msg)); + return FALSE; + } + catch (...) + { + base.setLastError("Unknown J2C error"); + return FALSE; + } + + + return TRUE; +} + + +// Returns TRUE to mean done, whether successful or not. +BOOL LLImageJ2CKDU::decodeImpl(LLImageJ2C &base, LLImageRaw &raw_image, F32 decode_time, S32 first_channel, S32 max_channel_count) +{ + ECodeStreamMode mode = MODE_FAST; + + LLTimer decode_timer; + + if (!mCodeStreamp) + { + 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(); + + 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 = new LLKDUDecodeState(tile, buf, row_gap); + } + // 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))) + { + delete mDecodeState; + mDecodeState = NULL; + } + else + { + // Not finished decoding yet. + // setLastError("Ran out of time while decoding"); + return FALSE; + } + } + catch( const char* msg ) + { + base.setLastError(ll_safe_string(msg)); + base.decodeFailed(); + cleanupCodeStream(); + return TRUE; // done + } + catch( ... ) + { + base.setLastError( "Unknown J2C error" ); + 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) +{ + // Collect simple arguments. + + bool transpose, vflip, hflip; + bool allow_rate_prediction, allow_shorts, mem, quiet, no_weights; + int cpu_iterations; + std::ostream *record_stream; + + transpose = false; + record_stream = NULL; + allow_rate_prediction = true; + allow_shorts = true; + no_weights = false; + cpu_iterations = -1; + mem = false; + quiet = false; + vflip = true; + 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-strea + 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(); + if (max_output_size < 1000) + { + max_output_size = 1000; + } + U8 *output_buffer = new U8[max_output_size]; + + U32 output_size = max_output_size; // gets modified + LLKDUMemTarget output(output_buffer, output_size, base.getWidth()*base.getHeight()*base.getComponents()); + if (output_size > max_output_size) + { + llerrs << llformat("LLImageJ2C::encode output_size(%d) > max_output_size(%d)", + output_size,max_output_size) << llendl; + } + + 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); + } + + // Set codestream options + int num_layer_specs = 0; + + kdu_long layer_bytes[64]; + U32 max_bytes = 0; + + if ((num_components >= 3) && !no_weights) + { + set_default_colour_weights(codestream.access_siz()); + } + + if (reversible) + { + // If we're doing reversible, assume we're not using quality layers. + // Yes, I know this is incorrect! + codestream.access_siz()->parse_string("Creversible=yes"); + codestream.access_siz()->parse_string("Clayers=1"); + num_layer_specs = 1; + layer_bytes[0] = 0; + } + + else + { + // Rate is the argument passed into the LLImageJ2C which + // specifies the target compression rate. The default is 8:1. + // Possibly if max_bytes < 500, we should just use the default setting? + if (base.mRate != 0.f) + { + max_bytes = (U32)(base.mRate*base.getWidth()*base.getHeight()*base.getComponents()); + } + else + { + max_bytes = (U32)(base.getWidth()*base.getHeight()*base.getComponents()*0.125); + } + + const U32 min_bytes = FIRST_PACKET_SIZE; + if (max_bytes > min_bytes) + { + U32 i; + // This code is where we specify the target number of bytes for + // each layer. Not sure if we should do this for small images + // or not. The goal is to have this roughly align with + // different quality levels that we decode at. + for (i = min_bytes; i < max_bytes; i*=4) + { + if (i == min_bytes * 4) + { + i = 2000; + } + layer_bytes[num_layer_specs] = i; + num_layer_specs++; + } + layer_bytes[num_layer_specs] = max_bytes; + num_layer_specs++; + + std::string layer_string = llformat("Clayers=%d",num_layer_specs); + codestream.access_siz()->parse_string(layer_string.c_str()); + } + else + { + layer_bytes[0] = min_bytes; + num_layer_specs = 1; + std::string layer_string = llformat("Clayers=%d",num_layer_specs); + codestream.access_siz()->parse_string(layer_string.c_str()); + } + } + codestream.access_siz()->finalize_all(); + if (cpu_iterations >= 0) + { + codestream.collect_timing_stats(cpu_iterations); + } + codestream.change_appearance(transpose,vflip,hflip); + + // Now we are ready for sample data processing. + + int x_tnum; + kdu_dims tile_indices; codestream.get_valid_tiles(tile_indices); + kdc_flow_control **tile_flows = new kdc_flow_control *[tile_indices.size.x]; + for (x_tnum=0; x_tnum < tile_indices.size.x; x_tnum++) + { + tile_flows[x_tnum] = new kdc_flow_control(&mem_in,codestream,x_tnum,allow_shorts); + } + bool done = false; + + while (!done) + { + while (!done) + { // Process a row of tiles line by line. + done = true; + for (x_tnum=0; x_tnum < tile_indices.size.x; x_tnum++) + { + if (tile_flows[x_tnum]->advance_components()) + { + done = false; + tile_flows[x_tnum]->process_components(); + } + } + } + for (x_tnum=0; x_tnum < tile_indices.size.x; x_tnum++) + { + if (tile_flows[x_tnum]->advance_tile()) + { + done = false; + } + } + } + int sample_bytes = 0; + for (x_tnum=0; x_tnum < tile_indices.size.x; x_tnum++) + { + sample_bytes += tile_flows[x_tnum]->get_buffer_memory(); + delete tile_flows[x_tnum]; + } + delete [] tile_flows; + + // Produce the compressed output. + + codestream.flush(layer_bytes,num_layer_specs, NULL, true, false); + + // Cleanup + + codestream.destroy(); + if (record_stream != NULL) + { + delete record_stream; + } + + + // 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 char* msg) + { + base.setLastError(ll_safe_string(msg)); + return FALSE; + } + catch( ... ) + { + base.setLastError( "Unknown J2C error" ); + 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 char* msg ) + { + base.setLastError(ll_safe_string(msg)); + return FALSE; + } + catch( ... ) + { + base.setLastError( "Unknown J2C error" ); + return FALSE; + } + +} + + +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)) + return; // Weights already specified explicitly. + + /* These example weights are adapted from numbers generated by Marcus Nadenau + at EPFL, for a viewing distance of 15 cm and a display resolution of + 300 DPI. */ + + cod->parse_string("Cband_weights:C0=" + "{0.0901},{0.2758},{0.2758}," + "{0.7018},{0.8378},{0.8378},{1}"); + cod->parse_string("Cband_weights:C1=" + "{0.0263},{0.0863},{0.0863}," + "{0.1362},{0.2564},{0.2564}," + "{0.3346},{0.4691},{0.4691}," + "{0.5444},{0.6523},{0.6523}," + "{0.7078},{0.7797},{0.7797},{1}"); + cod->parse_string("Cband_weights:C2=" + "{0.0773},{0.1835},{0.1835}," + "{0.2598},{0.4130},{0.4130}," + "{0.5040},{0.6464},{0.6464}," + "{0.7220},{0.8254},{0.8254}," + "{0.8769},{0.9424},{0.9424},{1}"); +} + + +/******************************************************************************/ +/* transfer_bytes */ +/******************************************************************************/ + +void transfer_bytes(kdu_byte *dest, kdu_line_buf &src, int gap, int precision) +/* Transfers source samples from the supplied line buffer into the output +byte buffer, spacing successive output samples apart by `gap' bytes +(to allow for interleaving of colour components). The function performs +all necessary level shifting, type conversion, rounding and truncation. */ +{ + int width = src.get_width(); + if (src.get_buf32() != NULL) + { // Decompressed samples have a 32-bit representation (integer or float) + assert(precision >= 8); // Else would have used 16 bit representation + kdu_sample32 *sp = src.get_buf32(); + if (!src.is_absolute()) + { // Transferring normalized floating point data. + float scale16 = (float)(1<<16); + kdu_int32 val; + + for (; width > 0; width--, sp++, dest+=gap) + { + val = (kdu_int32)(sp->fval*scale16); + val = (val+128)>>8; // May be faster than true rounding + val += 128; + if (val & ((-1)<<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 & ((-1)<<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 & ((-1)<<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 & ((-1)<<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 & ((-1)<<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 & ((-1)<<8)) + { + val = (val<0)?0:(256-(1<<upshift)); + } + *dest = (kdu_byte) val; + } + } + } + } +} + +LLKDUDecodeState::LLKDUDecodeState(kdu_tile tile, kdu_byte *buf, S32 row_gap) +{ + 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); + 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(); // Actually creates buffering resources + for (c=0; c < mNumComponents; c++) + { + mLines[c].create(); // Grabs resources from the allocator. + } +} + +LLKDUDecodeState::~LLKDUDecodeState() +{ + S32 c; + // Cleanup + for (c=0; c < mNumComponents; c++) + { + mEngines[c].destroy(); // engines are interfaces; no default destructors + } + + mTile.close(); +} + +BOOL LLKDUDecodeState::processTileDecode(F32 decode_time, BOOL limit_time) +/* Decompresses a tile, writing the data into the supplied byte buffer. +The buffer contains interleaved image components, if there are any. +Although you may think of the buffer as belonging entirely to this tile, +the `buf' pointer may actually point into a larger buffer representing +multiple tiles. For this reason, `row_gap' is needed to identify the +separation between consecutive rows in the real buffer. */ +{ + 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--) + { + for (c=0; c < mNumComponents; c++) + { + mEngines[c].pull(mLines[c],true); + } + if ((mNumComponents >= 3) && mUseYCC) + { + kdu_convert_ycc_to_rgb(mLines[0],mLines[1],mLines[2]); + } + for (c=0; c < mNumComponents; c++) + { + transfer_bytes(mBuf+c,mLines[c],mNumComponents,mBitDepths[c]); + } + mBuf += mRowGap; + if (mDims.size.y % 10) + { + if (limit_time && decode_timer.getElapsedTimeF32() > decode_time) + { + return FALSE; + } + } + } + return TRUE; +} diff --git a/indra/llkdu/llimagej2ckdu.h b/indra/llkdu/llimagej2ckdu.h new file mode 100644 index 0000000000..5794ebdc68 --- /dev/null +++ b/indra/llkdu/llimagej2ckdu.h @@ -0,0 +1,92 @@ +/** + * @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$ + */ + +#ifndef LL_LLIMAGEJ2CKDU_H +#define LL_LLIMAGEJ2CKDU_H + +#include "llimagej2c.h" + +// +// +// +// KDU core header files +#include "kdu/kdu_elementary.h" +#include "kdu/kdu_messaging.h" +#include "kdu/kdu_params.h" +#include "kdu/kdu_compressed.h" +#include "kdu/kdu_sample_processing.h" + +class LLKDUDecodeState; +class LLKDUMemSource; + +class LLImageJ2CKDU : public LLImageJ2CImpl +{ +public: + enum ECodeStreamMode + { + MODE_FAST = 0, + MODE_RESILIENT = 1, + MODE_FUSSY = 2 + }; + +public: + 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); + + void setupCodeStream(LLImageJ2C &base, BOOL keep_codestream, ECodeStreamMode mode); + void cleanupCodeStream(); + BOOL initDecode(LLImageJ2C &base, LLImageRaw &raw_image, F32 decode_time, ECodeStreamMode mode, S32 first_channel, S32 max_channel_count ); + + // Encode variable + LLKDUMemSource *mInputp; + kdu_codestream *mCodeStreamp; + kdu_coords *mTPosp; // tile position + kdu_dims *mTileIndicesp; + + // Temporary variables for in-progress decodes... + LLImageRaw *mRawImagep; + LLKDUDecodeState *mDecodeState; +}; + +#if LL_WINDOWS +# define LLSYMEXPORT __declspec(dllexport) +#elif LL_LINUX +# define LLSYMEXPORT __attribute__ ((visibility("default"))) +#else +# define LLSYMEXPORT /**/ +#endif + +extern "C" LLSYMEXPORT const char* engineInfoLLImageJ2CKDU(); +extern "C" LLSYMEXPORT LLImageJ2CKDU* createLLImageJ2CKDU(); +extern "C" LLSYMEXPORT void destroyLLImageJ2CKDU(LLImageJ2CKDU* kdu); + +#endif diff --git a/indra/llkdu/llkdumem.cpp b/indra/llkdu/llkdumem.cpp new file mode 100644 index 0000000000..80f4c444d1 --- /dev/null +++ b/indra/llkdu/llkdumem.cpp @@ -0,0 +1,392 @@ + /** + * @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$ + */ + +#include "linden_common.h" + +#include "llkdumem.h" + +// Various image utility functions from kdu +#include "llerror.h" + +#if defined(LL_WINDOWS) +# pragma warning(disable: 4702) // unreachable code +#endif + +LLKDUMemIn::LLKDUMemIn(const U8 *data, + 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; +} + + +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; + } +} + + +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; + } + + return true; +} + + + +LLKDUMemOut::LLKDUMemOut(U8 *data, siz_params *siz, U8 in_num_components) +{ + int is_signed = 0; + int n; + + // Allocate memory segment + + first_comp_idx = 0; + if (!(siz->get(Scomponents,0,0,num_components) && + siz->get(Sdims,first_comp_idx,0,rows) && + siz->get(Sdims,first_comp_idx,1,cols) && + siz->get(Ssigned,first_comp_idx,0,is_signed))) + { + kdu_error e; e << "Attempting to create output image files before " + "all information is available concerning the image component " + "dimensions, bit-depth and signed/unsigned characteristics."; + } + num_components -= first_comp_idx; + + for (n=0; n < 3; n++) + { + precision[n] = 0; + } + + for (n=0; n < num_components; n++) + { + int prec; + + if (!(siz->compare(Sdims,first_comp_idx+n,0,rows) && + siz->compare(Sdims,first_comp_idx+n,1,cols) && + siz->compare(Ssigned,first_comp_idx+n,0,is_signed))) + { + assert(n > 0); + num_components = 1; + break; + } + if (!siz->get(Sprecision,first_comp_idx+n,0,prec)) + { + kdu_error e; e << "Attempting to create output image data before " + "all information is available concerning the image component " + "dimensions, bit-depth and signed/unsigned characteristics."; + } + llassert(n < 3); + precision[n] = prec; + } + if (is_signed) + { + kdu_warning w; + w << "Signed sample values will be written to the " + "BMP file as unsigned 8-bit quantities, centered about 128."; + } + + mCurPos = 0; + mData = data; + mDataSize = rows*cols*num_components; + + incomplete_lines = NULL; + free_lines = NULL; + num_unwritten_rows = rows; +} + +LLKDUMemOut::~LLKDUMemOut() +{ + if ((num_unwritten_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 completed!"; + } + 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; + } + + // Should either clean up or leave alone the data buffer... +// cout << "Done Destroying" << endl; +} + +void LLKDUMemOut::put(int comp_idx, kdu_line_buf &line, int x_tnum) +{ + int idx = 0; + + 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 open a new line buffer + assert(x_tnum == 0); // Must consume in very specific order. + 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; + } + scan->accessed_samples = 0; + scan->next_x_tnum = 0; + } + + assert((cols-scan->accessed_samples) >= line.get_width()); + + int comp_offset = idx; + + if (line.get_buf32() != NULL) + { + if (line.is_absolute()) + { + convert_ints_to_bytes(line.get_buf32(), + scan->buf+num_components*scan->accessed_samples+comp_offset, + line.get_width(),precision[idx],num_components); + } + else + { + convert_floats_to_bytes(line.get_buf32(), + scan->buf+num_components*scan->accessed_samples+comp_offset, + line.get_width(),precision[idx],num_components); + } + } + else + { + if (line.is_absolute()) + { + convert_shorts_to_bytes(line.get_buf16(), + scan->buf+num_components*scan->accessed_samples+comp_offset, + line.get_width(),precision[idx],num_components); + } + else + { + convert_fixpoint_to_bytes(line.get_buf16(), + scan->buf+num_components*scan->accessed_samples+comp_offset, + line.get_width(),precision[idx],num_components); + } + } + + scan->next_x_tnum++; + if (idx == (num_components-1)) + { + scan->accessed_samples += line.get_width(); + } + if (scan->accessed_samples == cols) + { + // Write completed line and send it to the free list. + if (num_unwritten_rows == 0) + { + kdu_error e; e << "Attempting to write too many lines to image " + "file for components " << first_comp_idx << " through " + << first_comp_idx+num_components-1 << "."; + } + if ((mCurPos + cols*num_components) > mDataSize) + { + llerrs << "LLKDUMemOut::put() too much data" << llendl; + } + // Write the data to the output buffer. + memcpy(mData+mCurPos, scan->buf, cols*num_components); + mCurPos += cols*num_components; + + num_unwritten_rows--; + assert(scan == incomplete_lines); + incomplete_lines = scan->next; + scan->next = free_lines; + free_lines = scan; + } +} diff --git a/indra/llkdu/llkdumem.h b/indra/llkdu/llkdumem.h new file mode 100644 index 0000000000..fecb4653db --- /dev/null +++ b/indra/llkdu/llkdumem.h @@ -0,0 +1,167 @@ +/** + * @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$ + */ + +#ifndef LL_LLKDUMEM_H +#define LL_LLKDUMEM_H + +// Support classes for reading and writing from memory buffers +// for KDU +#include "kdu_image.h" +#include "kdu/kdu_elementary.h" +#include "kdu/kdu_messaging.h" +#include "kdu/kdu_params.h" +#include "kdu/kdu_compressed.h" +#include "kdu/kdu_sample_processing.h" +#include "kdu_image_local.h" +#include "stdtypes.h" + +class LLKDUMemSource: public kdu_compressed_source +{ +public: // Member functions + LLKDUMemSource(U8 *input_buffer, U32 size) + { + mData = input_buffer; + mSize = size; + mCurPos = 0; + } + + ~LLKDUMemSource() + { + } + + int read(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: // Data + U8 *mData; + U32 mSize; + U32 mCurPos; +}; + +class LLKDUMemTarget: public kdu_compressed_target +{ +public: // Member functions + 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_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: // Data + U8 *mData; + U32 mSize; + U32 mCurPos; + U32 *mOutputSize; +}; + + +class LLKDUMemIn : public kdu_image_in_base +{ +public: // Member functions + LLKDUMemIn(const U8 *data, + const U32 size, + const U16 rows, + const U16 cols, + U8 in_num_components, + siz_params *siz); + ~LLKDUMemIn(); + bool get(int comp_idx, kdu_line_buf &line, int x_tnum); + + const U8 *mData; +private: // Data + 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]; + image_line_buf *incomplete_lines; // Each "sample" represents a full pixel + image_line_buf *free_lines; + int num_unread_rows; + + U32 mCurPos; + U32 mDataSize; +}; + +class LLKDUMemOut : public kdu_image_out_base +{ +public: // Member functions + LLKDUMemOut(U8 *data, siz_params *siz, U8 in_num_components); + LLKDUMemOut(siz_params *siz, U8 in_num_components); + ~LLKDUMemOut(); + void put(int comp_idx, kdu_line_buf &line, int x_tnum); + + U8 *mData; +private: // Data + int first_comp_idx; + int num_components; + int rows, cols; + int precision[3]; + image_line_buf *incomplete_lines; // Each "sample" represents a full pixel + image_line_buf *free_lines; + int num_unwritten_rows; + + U32 mCurPos; + U32 mDataSize; +}; + +#endif |