summaryrefslogtreecommitdiff
path: root/indra/llkdu/llblockencoder.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'indra/llkdu/llblockencoder.cpp')
-rw-r--r--indra/llkdu/llblockencoder.cpp343
1 files changed, 343 insertions, 0 deletions
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;
+}