summaryrefslogtreecommitdiff
path: root/indra/llkdu
diff options
context:
space:
mode:
authorMerov Linden <merov@lindenlab.com>2010-11-11 09:33:29 -0800
committerMerov Linden <merov@lindenlab.com>2010-11-11 09:33:29 -0800
commit94f44437d7e7ccb54f227e35523fa8a4e027d7d2 (patch)
treedd899aa6e98b2c10b22fa0db58cfca866a0322de /indra/llkdu
parentee8df375449e97476408417ec6dea0d6a8853d73 (diff)
parent0e07758b540374aa421d2938ce5ed13e2b9eefe4 (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.txt85
-rw-r--r--indra/llkdu/llblockdata.cpp162
-rw-r--r--indra/llkdu/llblockdata.h107
-rw-r--r--indra/llkdu/llblockdecoder.cpp273
-rw-r--r--indra/llkdu/llblockdecoder.h42
-rw-r--r--indra/llkdu/llblockencoder.cpp343
-rw-r--r--indra/llkdu/llblockencoder.h49
-rw-r--r--indra/llkdu/llimagej2ckdu.cpp1006
-rw-r--r--indra/llkdu/llimagej2ckdu.h92
-rw-r--r--indra/llkdu/llkdumem.cpp392
-rw-r--r--indra/llkdu/llkdumem.h167
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