From bc6f669ff41db304723428746868d79d3f3b48da Mon Sep 17 00:00:00 2001 From: Merov Linden Date: Thu, 15 Mar 2012 13:01:14 -0700 Subject: SH-3047 : Read the number of levels from the j2c image header instead of relying on hacked computation based on width / height. --- indra/llkdu/llimagej2ckdu.cpp | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) (limited to 'indra/llkdu/llimagej2ckdu.cpp') diff --git a/indra/llkdu/llimagej2ckdu.cpp b/indra/llkdu/llimagej2ckdu.cpp index c156ed0cef..eed4139f3f 100644 --- a/indra/llkdu/llimagej2ckdu.cpp +++ b/indra/llkdu/llimagej2ckdu.cpp @@ -291,8 +291,13 @@ void LLImageJ2CKDU::setupCodeStream(LLImageJ2C &base, BOOL keep_codestream, ECod } } - base.setSize(dims.size.x, dims.size.y, components); + // Get the number of resolution levels in that image + mLevels = mCodeStreamp->get_min_dwt_levels(); + // Set the base dimensions + base.setSize(dims.size.x, dims.size.y, components); + base.setLevels(mLevels); + if (!keep_codestream) { mCodeStreamp->destroy(); @@ -394,12 +399,9 @@ BOOL LLImageJ2CKDU::initDecode(LLImageJ2C &base, LLImageRaw &raw_image, F32 deco // Resize raw_image according to the image to be decoded kdu_dims dims; mCodeStreamp->get_dims(0,dims); - // *TODO: Use the real number of levels read from the file throughout the code instead of relying on an infered value from dimensions - //S32 levels = mCodeStreamp->get_min_dwt_levels(); S32 channels = base.getComponents() - first_channel; channels = llmin(channels,max_channel_count); raw_image.resize(dims.size.x, dims.size.y, channels); - //llinfos << "j2c image dimension: width = " << dims.size.x << ", height = " << dims.size.y << ", channels = " << channels << ", levels = " << levels << llendl; if (!mTileIndicesp) { -- cgit v1.3 From 5c86d19373ebfab446e8335729faefabc66ad15f Mon Sep 17 00:00:00 2001 From: Merov Linden Date: Sun, 25 Mar 2012 11:52:10 -0700 Subject: SH-3050 : Parse an input codestream without decompressing it to find discard levels boundaries (test only). --- indra/llkdu/llimagej2ckdu.cpp | 202 ++++++++++++++++++++++++++++++- indra/llkdu/llimagej2ckdu.h | 1 + indra/llkdu/tests/llimagej2ckdu_test.cpp | 20 +++ 3 files changed, 222 insertions(+), 1 deletion(-) (limited to 'indra/llkdu/llimagej2ckdu.cpp') diff --git a/indra/llkdu/llimagej2ckdu.cpp b/indra/llkdu/llimagej2ckdu.cpp index eed4139f3f..8b170a3206 100644 --- a/indra/llkdu/llimagej2ckdu.cpp +++ b/indra/llkdu/llimagej2ckdu.cpp @@ -32,6 +32,7 @@ #include "llmath.h" #include "llkdumem.h" +#include "kdu_block_coding.h" class kdc_flow_control { @@ -244,7 +245,7 @@ void LLImageJ2CKDU::setupCodeStream(LLImageJ2C &base, BOOL keep_codestream, ECod mCodeStreamp->create(mInputp); // Set the maximum number of bytes to use from the codestream - mCodeStreamp->set_max_bytes(max_bytes); + mCodeStreamp->set_max_bytes(max_bytes,true); // If you want to flip or rotate the image for some reason, change // the resolution, or identify a restricted region of interest, this is @@ -369,6 +370,9 @@ BOOL LLImageJ2CKDU::initDecode(LLImageJ2C &base, LLImageRaw &raw_image, F32 deco // To regain control, we throw an exception, and catch it here. try { + // Merov : Test!! DO NOT COMMIT!! + //findDiscardLevelsBoundaries(base); + base.updateRawDiscardLevel(); setupCodeStream(base, TRUE, mode); @@ -752,6 +756,202 @@ BOOL LLImageJ2CKDU::getMetadata(LLImageJ2C &base) } } +/*****************************************************************************/ +/* STATIC copy_block */ +/*****************************************************************************/ + +static void copy_block(kdu_block *in, kdu_block *out) +{ + if (in->K_max_prime != out->K_max_prime) + { + std::cout << "Cannot copy blocks belonging to subbands with different quantization parameters." << std::endl; + return; + } + if ((in->size.x != out->size.x) || (in->size.y != out->size.y)) + { + std::cout << "Cannot copy code-blocks with different dimensions." << std::endl; + return; + } + out->missing_msbs = in->missing_msbs; + if (out->max_passes < (in->num_passes+2)) // Gives us enough to round up + out->set_max_passes(in->num_passes+2,false); // to the next whole bit-plane + out->num_passes = in->num_passes; + int num_bytes = 0; + for (int z=0; z < in->num_passes; z++) + { + num_bytes += (out->pass_lengths[z] = in->pass_lengths[z]); + out->pass_slopes[z] = in->pass_slopes[z]; + } + + // Just copy compressed code-bytes. Block transcoding not supported. + if (out->max_bytes < num_bytes) + out->set_max_bytes(num_bytes,false); + memcpy(out->byte_buffer,in->byte_buffer,(size_t) num_bytes); +} + +/*****************************************************************************/ +/* STATIC copy_tile */ +/*****************************************************************************/ + +static void +copy_tile(kdu_tile tile_in, kdu_tile tile_out, int tnum_in, int tnum_out, + kdu_params *siz_in, kdu_params *siz_out, int skip_components, + int &num_blocks) +{ + int num_components = tile_out.get_num_components(); + int new_tpart=0, next_tpart = 1; + + for (int c=0; c < num_components; c++) + { + kdu_tile_comp comp_in, comp_out; + comp_in = tile_in.access_component(c); + comp_out = tile_out.access_component(c); + int num_resolutions = comp_out.get_num_resolutions(); + for (int r=0; r < num_resolutions; r++) + { + kdu_resolution res_in; res_in = comp_in.access_resolution(r); + kdu_resolution res_out; res_out = comp_out.access_resolution(r); + int b, min_band; + int num_bands = res_in.get_valid_band_indices(min_band); + for (b=min_band; num_bands > 0; num_bands--, b++) + { + kdu_subband band_in; band_in = res_in.access_subband(b); + kdu_subband band_out; band_out = res_out.access_subband(b); + kdu_dims blocks_in; band_in.get_valid_blocks(blocks_in); + kdu_dims blocks_out; band_out.get_valid_blocks(blocks_out); + if ((blocks_in.size.x != blocks_out.size.x) || + (blocks_in.size.y != blocks_out.size.y)) + { + std::cout << "Transcoding operation cannot proceed: Code-block partitions for the input and output code-streams do not agree." << std::endl; + return; + } + kdu_coords idx; + for (idx.y=0; idx.y < blocks_out.size.y; idx.y++) + { + for (idx.x=0; idx.x < blocks_out.size.x; idx.x++) + { + kdu_block *in = + band_in.open_block(idx+blocks_in.pos,&new_tpart); + for (; next_tpart <= new_tpart; next_tpart++) + siz_out->copy_from(siz_in,tnum_in,tnum_out,next_tpart, + skip_components); + kdu_block *out = band_out.open_block(idx+blocks_out.pos); + copy_block(in,out); + band_in.close_block(in); + band_out.close_block(out); + num_blocks++; + } + } + } + } + } +} + +// Find the block boundary for each discard level in the input image. +// We parse the input blocks and copy them in a temporary output stream. +// For the moment, we do nothing more that parsing the raw list of blocks and outputing result. +void LLImageJ2CKDU::findDiscardLevelsBoundaries(LLImageJ2C &base) +{ + // We need the number of levels in that image before starting. + getMetadata(base); + + for (int discard_level = 0; discard_level < mLevels; discard_level++) + { + // Create the input codestream object. + setupCodeStream(base, TRUE, MODE_FAST); + mCodeStreamp->apply_input_restrictions(0, 4, discard_level, 0, NULL); + //mCodeStreamp->set_max_bytes(max,true); + siz_params *siz_in = mCodeStreamp->access_siz(); + + // Create the output codestream object. + siz_params siz; + siz.copy_from(siz_in,-1,-1,-1,0,discard_level,false,false,false); + siz.set(Scomponents,0,0,mCodeStreamp->get_num_components()); + + U32 max_output_size = base.getWidth()*base.getHeight()*base.getComponents(); + max_output_size = (max_output_size < 1000 ? 1000 : max_output_size); + U8 *output_buffer = new U8[max_output_size]; + U32 output_size = 0; // Address updated by LLKDUMemTarget to give the final compressed buffer size + LLKDUMemTarget output(output_buffer, output_size, max_output_size); + kdu_codestream codestream_out; + codestream_out.create(&siz,&output); + //codestream_out.share_buffering(*mCodeStreamp); + siz_params *siz_out = codestream_out.access_siz(); + siz_out->copy_from(siz_in,-1,-1,-1,0,discard_level,false,false,false); + codestream_out.access_siz()->finalize_all(-1); + + // Set up rate control variables + kdu_long max_bytes = KDU_LONG_MAX; + kdu_params *cod = siz_out->access_cluster(COD_params); + int total_layers; cod->get(Clayers,0,0,total_layers); + kdu_long *layer_bytes = new kdu_long[total_layers]; + int nel, non_empty_layers = 0; + + // Now ready to perform the transfer of compressed data between streams + int flush_counter = INT_MAX; + kdu_dims tile_indices_in; + mCodeStreamp->get_valid_tiles(tile_indices_in); + kdu_dims tile_indices_out; + codestream_out.get_valid_tiles(tile_indices_out); + assert((tile_indices_in.size.x == tile_indices_out.size.x) && + (tile_indices_in.size.y == tile_indices_out.size.y)); + int num_blocks=0; + + kdu_coords idx; + for (idx.y=0; idx.y < tile_indices_out.size.y; idx.y++) + { + for (idx.x=0; idx.x < tile_indices_out.size.x; idx.x++) + { + kdu_tile tile_in = mCodeStreamp->open_tile(idx+tile_indices_in.pos); + int tnum_in = tile_in.get_tnum(); + int tnum_out = idx.x + idx.y*tile_indices_out.size.x; + siz_out->copy_from(siz_in,tnum_in,tnum_out,0,0,discard_level,false,false,false); + siz_out->finalize_all(tnum_out); + // Note: do not open the output tile without first copying any tile-specific code-stream parameters + kdu_tile tile_out = codestream_out.open_tile(idx+tile_indices_out.pos); + assert(tnum_out == tile_out.get_tnum()); + copy_tile(tile_in,tile_out,tnum_in,tnum_out,siz_in,siz_out,0,num_blocks); + tile_in.close(); + tile_out.close(); + flush_counter--; + if ((flush_counter <= 0) && codestream_out.ready_for_flush()) + { + flush_counter = INT_MAX; + nel = codestream_out.trans_out(max_bytes,layer_bytes,total_layers); + non_empty_layers = (nel > non_empty_layers)?nel:non_empty_layers; + } + } + } + + // Generate the output code-stream + if (codestream_out.ready_for_flush()) + { + nel = codestream_out.trans_out(max_bytes,layer_bytes,total_layers); + non_empty_layers = (nel > non_empty_layers)?nel:non_empty_layers; + } + if (non_empty_layers > total_layers) + non_empty_layers = total_layers; // Can happen if a tile has more layers + + // Print out stats + std::cout << "Code stream parsing for discard level = " << discard_level << std::endl; + std::cout << " Total compressed memory in = " << mCodeStreamp->get_compressed_data_memory() << " bytes" << std::endl; + std::cout << " Total compressed memory out = " << codestream_out.get_compressed_data_memory() << " bytes" << std::endl; + //std::cout << " Output contains " << total_layers << " quality layers" << std::endl; + std::cout << " Transferred " << num_blocks << " code-blocks from in to out" << std::endl; + //std::cout << " Read " << mCodeStreamp->get_num_tparts() << " tile-part(s) from a total of " << (int) tile_indices_in.area() << " tile(s)" << std::endl; + std::cout << " Total bytes read = " << mCodeStreamp->get_total_bytes() << std::endl; + //std::cout << " Wrote " << codestream_out.get_num_tparts() << " tile-part(s) in a total of " << (int) tile_indices_out.area() << " tile(s)" << std::endl; + std::cout << " Total bytes written = " << codestream_out.get_total_bytes() << std::endl; + std::cout << "-------------" << std::endl; + + // Clean-up + cleanupCodeStream(); + codestream_out.destroy(); + delete[] output_buffer; + } + return; +} + void set_default_colour_weights(kdu_params *siz) { kdu_params *cod = siz->access_cluster(COD_params); diff --git a/indra/llkdu/llimagej2ckdu.h b/indra/llkdu/llimagej2ckdu.h index 1489dbf704..9ab0b9e4a7 100644 --- a/indra/llkdu/llimagej2ckdu.h +++ b/indra/llkdu/llimagej2ckdu.h @@ -60,6 +60,7 @@ protected: BOOL reversible=FALSE); /*virtual*/ BOOL initDecode(LLImageJ2C &base, LLImageRaw &raw_image, int discard_level = -1, int* region = NULL); /*virtual*/ BOOL initEncode(LLImageJ2C &base, LLImageRaw &raw_image, int blocks_size = -1, int precincts_size = -1, int levels = 0); + void findDiscardLevelsBoundaries(LLImageJ2C &base); private: BOOL initDecode(LLImageJ2C &base, LLImageRaw &raw_image, F32 decode_time, ECodeStreamMode mode, S32 first_channel, S32 max_channel_count, int discard_level = -1, int* region = NULL); diff --git a/indra/llkdu/tests/llimagej2ckdu_test.cpp b/indra/llkdu/tests/llimagej2ckdu_test.cpp index ab60ab6d50..feb6671e40 100644 --- a/indra/llkdu/tests/llimagej2ckdu_test.cpp +++ b/indra/llkdu/tests/llimagej2ckdu_test.cpp @@ -29,6 +29,7 @@ // Class to test #include "llimagej2ckdu.h" #include "llkdumem.h" +#include "kdu_block_coding.h" // Tut header #include "lltut.h" @@ -107,16 +108,25 @@ bool LLKDUMemIn::get(int, kdu_line_buf&, int) { return false; } // Stub Kakadu Library calls kdu_tile_comp kdu_tile::access_component(int ) { kdu_tile_comp a; return a; } +kdu_block_encoder::kdu_block_encoder() { } +kdu_block_decoder::kdu_block_decoder() { } +void kdu_block::set_max_passes(int , bool ) { } +void kdu_block::set_max_bytes(int , bool ) { } +void kdu_block::set_max_samples(int ) { } void kdu_tile::close(kdu_thread_env* ) { } int kdu_tile::get_num_components() { return 0; } bool kdu_tile::get_ycc() { return false; } void kdu_tile::set_components_of_interest(int , const int* ) { } +int kdu_tile::get_tnum() { return 0; } kdu_resolution kdu_tile_comp::access_resolution() { kdu_resolution a; return a; } +kdu_resolution kdu_tile_comp::access_resolution(int ) { kdu_resolution a; return a; } int kdu_tile_comp::get_bit_depth(bool ) { return 8; } bool kdu_tile_comp::get_reversible() { return false; } +int kdu_tile_comp::get_num_resolutions() { return 1; } kdu_subband kdu_resolution::access_subband(int ) { kdu_subband a; return a; } void kdu_resolution::get_dims(kdu_dims& ) { } int kdu_resolution::which() { return 0; } +int kdu_resolution::get_valid_band_indices(int &) { return 1; } kdu_decoder::kdu_decoder(kdu_subband , kdu_sample_allocator*, bool , float, int, kdu_thread_env*, kdu_thread_queue*) { } kdu_synthesis::kdu_synthesis(kdu_resolution, kdu_sample_allocator*, bool, float, kdu_thread_env*, kdu_thread_queue*) { } kdu_params::kdu_params(const char*, bool, bool, bool, bool, bool) { } @@ -124,6 +134,7 @@ kdu_params::~kdu_params() { } void kdu_params::set(const char* , int , int , bool ) { } void kdu_params::set(const char* , int , int , int ) { } void kdu_params::finalize_all(bool ) { } +void kdu_params::finalize_all(int, bool ) { } void kdu_params::copy_from(kdu_params*, int, int, int, int, int, bool, bool, bool) { } bool kdu_params::parse_string(const char*) { return false; } bool kdu_params::get(const char*, int, int, bool&, bool, bool, bool) { return false; } @@ -148,9 +159,18 @@ void kdu_codestream::get_subsampling(int , kdu_coords&, bool ) { } void kdu_codestream::flush(kdu_long *, int , kdu_uint16 *, bool, bool, double, kdu_thread_env*) { } void kdu_codestream::set_resilient(bool ) { } int kdu_codestream::get_num_components(bool ) { return 0; } +kdu_long kdu_codestream::get_total_bytes(bool ) { return 0; } +kdu_long kdu_codestream::get_compressed_data_memory(bool ) {return 0; } +void kdu_codestream::share_buffering(kdu_codestream ) { } +int kdu_codestream::get_num_tparts() { return 0; } +int kdu_codestream::trans_out(kdu_long, kdu_long*, int, bool, kdu_thread_env* ) { return 0; } +bool kdu_codestream::ready_for_flush(kdu_thread_env*) { return false; } siz_params* kdu_codestream::access_siz() { return NULL; } kdu_tile kdu_codestream::open_tile(kdu_coords , kdu_thread_env* ) { kdu_tile a; return a; } kdu_codestream_comment kdu_codestream::add_comment() { kdu_codestream_comment a; return a; } +void kdu_subband::close_block(kdu_block*, kdu_thread_env*) { } +void kdu_subband::get_valid_blocks(kdu_dims &indices) { } +kdu_block* kdu_subband::open_block(kdu_coords, int*, kdu_thread_env*) { return NULL; } bool kdu_codestream_comment::put_text(const char*) { return false; } void kdu_customize_warnings(kdu_message*) { } void kdu_customize_errors(kdu_message*) { } -- cgit v1.3 From 792943c211f90738e245b11e128525190ff1b107 Mon Sep 17 00:00:00 2001 From: Merov Linden Date: Fri, 30 Mar 2012 14:07:43 -0700 Subject: SH-3050 : Add a call to set_max_bytes() and some clean up --- indra/llkdu/llimagej2ckdu.cpp | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) (limited to 'indra/llkdu/llimagej2ckdu.cpp') diff --git a/indra/llkdu/llimagej2ckdu.cpp b/indra/llkdu/llimagej2ckdu.cpp index 8b170a3206..fdfab5506a 100644 --- a/indra/llkdu/llimagej2ckdu.cpp +++ b/indra/llkdu/llimagej2ckdu.cpp @@ -807,12 +807,14 @@ copy_tile(kdu_tile tile_in, kdu_tile tile_out, int tnum_in, int tnum_out, comp_in = tile_in.access_component(c); comp_out = tile_out.access_component(c); int num_resolutions = comp_out.get_num_resolutions(); + //std::cout << " Copying tile : num_resolutions = " << num_resolutions << std::endl; for (int r=0; r < num_resolutions; r++) { kdu_resolution res_in; res_in = comp_in.access_resolution(r); kdu_resolution res_out; res_out = comp_out.access_resolution(r); int b, min_band; int num_bands = res_in.get_valid_band_indices(min_band); + std::cout << " Copying tile : num_bands = " << num_bands << std::endl; for (b=min_band; num_bands > 0; num_bands--, b++) { kdu_subband band_in; band_in = res_in.access_subband(b); @@ -826,6 +828,7 @@ copy_tile(kdu_tile tile_in, kdu_tile tile_out, int tnum_in, int tnum_out, return; } kdu_coords idx; + //std::cout << " Copying tile : block indices, x = " << blocks_out.size.x << " and y = " << blocks_out.size.y << std::endl; for (idx.y=0; idx.y < blocks_out.size.y; idx.y++) { for (idx.x=0; idx.x < blocks_out.size.x; idx.x++) @@ -857,10 +860,11 @@ void LLImageJ2CKDU::findDiscardLevelsBoundaries(LLImageJ2C &base) for (int discard_level = 0; discard_level < mLevels; discard_level++) { + //std::cout << "Parsing discard level = " << discard_level << std::endl; // Create the input codestream object. setupCodeStream(base, TRUE, MODE_FAST); mCodeStreamp->apply_input_restrictions(0, 4, discard_level, 0, NULL); - //mCodeStreamp->set_max_bytes(max,true); + mCodeStreamp->set_max_bytes(KDU_LONG_MAX,true); siz_params *siz_in = mCodeStreamp->access_siz(); // Create the output codestream object. @@ -898,6 +902,7 @@ void LLImageJ2CKDU::findDiscardLevelsBoundaries(LLImageJ2C &base) int num_blocks=0; kdu_coords idx; + //std::cout << "Parsing tiles : x = " << tile_indices_out.size.x << " to y = " << tile_indices_out.size.y << std::endl; for (idx.y=0; idx.y < tile_indices_out.size.y; idx.y++) { for (idx.x=0; idx.x < tile_indices_out.size.x; idx.x++) -- cgit v1.3 From df09fd8e8b5b73330e4942c2cb218a216d7aca99 Mon Sep 17 00:00:00 2001 From: Merov Linden Date: Mon, 2 Apr 2012 19:05:32 -0700 Subject: SH-3060 : Preliminary implementation of the new byte range computation, implement setting to turn it on or off --- indra/llimage/llimage.cpp | 4 ++- indra/llimage/llimage.h | 5 +++- indra/llimage/llimagej2c.cpp | 47 +++++++++++---------------------- indra/llkdu/llimagej2ckdu.cpp | 14 ++++++++-- indra/newview/app_settings/settings.xml | 11 ++++++++ indra/newview/llappviewer.cpp | 2 +- 6 files changed, 47 insertions(+), 36 deletions(-) (limited to 'indra/llkdu/llimagej2ckdu.cpp') diff --git a/indra/llimage/llimage.cpp b/indra/llimage/llimage.cpp index 937655a22d..7f95441075 100644 --- a/indra/llimage/llimage.cpp +++ b/indra/llimage/llimage.cpp @@ -48,11 +48,13 @@ //static std::string LLImage::sLastErrorMessage; LLMutex* LLImage::sMutex = NULL; +bool LLImage::sUseNewByteRange = false; LLPrivateMemoryPool* LLImageBase::sPrivatePoolp = NULL ; //static -void LLImage::initClass() +void LLImage::initClass(bool use_new_byte_range) { + sUseNewByteRange = use_new_byte_range; sMutex = new LLMutex(NULL); LLImageBase::createPrivatePool() ; diff --git a/indra/llimage/llimage.h b/indra/llimage/llimage.h index eba8362f1c..b757547ab8 100644 --- a/indra/llimage/llimage.h +++ b/indra/llimage/llimage.h @@ -89,15 +89,18 @@ typedef enum e_image_codec class LLImage { public: - static void initClass(); + static void initClass(bool use_new_byte_range = false); static void cleanupClass(); static const std::string& getLastError(); static void setLastError(const std::string& message); + static bool useNewByteRange() { return sUseNewByteRange; } + protected: static LLMutex* sMutex; static std::string sLastErrorMessage; + static bool sUseNewByteRange; }; //============================================================================ diff --git a/indra/llimage/llimagej2c.cpp b/indra/llimage/llimagej2c.cpp index fbf4b769e1..cbb6f75b43 100644 --- a/indra/llimage/llimagej2c.cpp +++ b/indra/llimage/llimagej2c.cpp @@ -262,10 +262,12 @@ S32 LLImageJ2C::calcHeaderSizeJ2C() //static S32 LLImageJ2C::calcDataSizeJ2C(S32 w, S32 h, S32 comp, S32 discard_level, F32 rate) { - // Note: this only provides an *estimate* of the size in bytes of an image level - // *TODO: find a way to read the true size (when available) and convey the fact - // that the result is an estimate in the other cases - if (rate <= 0.f) rate = .125f; + // Note: This provides an estimation for the first quality layer of a given discard level + // This is however an efficient approximation, as the true discard level boundary would be + // in general too big for fast fetching. + // For details about the equation used here, see https://wiki.lindenlab.com/wiki/THX1138_KDU_Improvements#Byte_Range_Study + if (rate <= 0.f) rate = 1.f/8.f; + // Compute w/pow(2,discard_level) and h/pow(2,discard_level) while (discard_level > 0) { if (w < 1 || h < 1) @@ -274,7 +276,13 @@ S32 LLImageJ2C::calcDataSizeJ2C(S32 w, S32 h, S32 comp, S32 discard_level, F32 r h >>= 1; discard_level--; } - S32 bytes = (S32)((F32)(w*h*comp)*rate); + // Temporary: compute both new and old range and pick one according to the settings TextureNewByteRange + // *TODO: Take the old code out once we have enough tests done + // *TODO: Replace the magic "7" by the number of quality layers in the j2c image + S32 bytes; + S32 new_bytes = sqrt((F32)(w*h))*(F32)(comp)*rate*1000.f/7.f; + S32 old_bytes = (S32)((F32)(w*h*comp)*rate); + bytes = (LLImage::useNewByteRange() ? new_bytes : old_bytes); bytes = llmax(bytes, calcHeaderSizeJ2C()); return bytes; } @@ -284,11 +292,7 @@ S32 LLImageJ2C::calcHeaderSize() return calcHeaderSizeJ2C(); } - -// calcDataSize() returns how many bytes to read -// to load discard_level (including header and higher discard levels) -// *TODO: This is deeply wrong. That size should be taken from the image file header or other -// relevant infos. In any case, this is only an approximation. +// calcDataSize() returns how many bytes to read to load discard_level (including header) S32 LLImageJ2C::calcDataSize(S32 discard_level) { discard_level = llclamp(discard_level, 0, MAX_DISCARD_LEVEL); @@ -304,25 +308,6 @@ S32 LLImageJ2C::calcDataSize(S32 discard_level) mDataSizes[level] = calcDataSizeJ2C(getWidth(), getHeight(), getComponents(), level, mRate); level--; } - - /* This is technically a more correct way to calculate the size required - for each discard level, since they should include the size needed for - lower levels. Unfortunately, this doesn't work well and will lead to - download stalls. The true correct way is to parse the header. This will - all go away with http textures at some point. - - // Calculate the size for each discard level. Lower levels (higher quality) - // contain the cumulative size of higher levels - S32 total_size = calcHeaderSizeJ2C(); - - S32 level = MAX_DISCARD_LEVEL; // Start at the highest discard - while ( level >= 0 ) - { // Add in this discard level and all before it - total_size += calcDataSizeJ2C(getWidth(), getHeight(), getComponents(), level, mRate); - mDataSizes[level] = total_size; - level--; - } - */ } return mDataSizes[discard_level]; } @@ -337,8 +322,8 @@ S32 LLImageJ2C::calcDiscardLevelBytes(S32 bytes) } while (1) { - S32 bytes_needed = calcDataSize(discard_level); // virtual - if (bytes >= bytes_needed - (bytes_needed>>2)) // For J2c, up the res at 75% of the optimal number of bytes + S32 bytes_needed = calcDataSize(discard_level); + if (bytes >= bytes_needed) { break; } diff --git a/indra/llkdu/llimagej2ckdu.cpp b/indra/llkdu/llimagej2ckdu.cpp index fdfab5506a..4468b8563b 100644 --- a/indra/llkdu/llimagej2ckdu.cpp +++ b/indra/llkdu/llimagej2ckdu.cpp @@ -294,6 +294,13 @@ void LLImageJ2CKDU::setupCodeStream(LLImageJ2C &base, BOOL keep_codestream, ECod // Get the number of resolution levels in that image mLevels = mCodeStreamp->get_min_dwt_levels(); + + //kdu_coords idx; idx.x = 0; idx.y = 0; + //kdu_dims tile_indices_in; + //mCodeStreamp->get_valid_tiles(tile_indices_in); + //mCodeStreamp->create_tile(idx+tile_indices_in.pos); + //int layers = mCodeStreamp->get_max_tile_layers(); + //llinfos << "Merov debug : setupCodeStream, levels = " << mLevels << ", layers = " << layers << llendl; // Set the base dimensions base.setSize(dims.size.x, dims.size.y, components); @@ -390,7 +397,7 @@ BOOL LLImageJ2CKDU::initDecode(LLImageJ2C &base, LLImageRaw &raw_image, F32 deco region_kdu->size.y = region[3] - region[1]; } int discard = (discard_level != -1 ? discard_level : base.getRawDiscardLevel()); - + llinfos << "Merov debug : initDecode, discard used = " << discard << ", asked = " << discard_level << llendl; // Apply loading restrictions mCodeStreamp->apply_input_restrictions( first_channel, max_channel_count, discard, 0, region_kdu); @@ -468,6 +475,9 @@ BOOL LLImageJ2CKDU::decodeImpl(LLImageJ2C &base, LLImageRaw &raw_image, F32 deco { kdu_tile tile = mCodeStreamp->open_tile(*(mTPosp)+mTileIndicesp->pos); + int layers = mCodeStreamp->get_max_tile_layers(); + llinfos << "Merov debug : decodeImpl, levels = " << mLevels << ", layers = " << layers << llendl; + // 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 @@ -675,7 +685,7 @@ BOOL LLImageJ2CKDU::encodeImpl(LLImageJ2C &base, const LLImageRaw &raw_image, co std::string blocks_string = llformat("Cblk={%d,%d}",mBlocksSize,mBlocksSize); codestream.access_siz()->parse_string(blocks_string.c_str()); } - std::string ordering_string = llformat("Corder=RPCL"); + std::string ordering_string = llformat("Corder=LRCP"); codestream.access_siz()->parse_string(ordering_string.c_str()); std::string PLT_string = llformat("ORGgen_plt=yes"); codestream.access_siz()->parse_string(PLT_string.c_str()); diff --git a/indra/newview/app_settings/settings.xml b/indra/newview/app_settings/settings.xml index 9fff543b13..5fc9c5d863 100755 --- a/indra/newview/app_settings/settings.xml +++ b/indra/newview/app_settings/settings.xml @@ -10686,6 +10686,17 @@ Value 0 + TextureNewByteRange + + Comment + Use the new more accurate byte range computation for j2c discard levels + Persist + 1 + Type + Boolean + Value + 1 + TexturePickerShowFolders Comment diff --git a/indra/newview/llappviewer.cpp b/indra/newview/llappviewer.cpp index bea8303d69..1a68d0317e 100644 --- a/indra/newview/llappviewer.cpp +++ b/indra/newview/llappviewer.cpp @@ -1947,7 +1947,7 @@ bool LLAppViewer::initThreads() static const bool enable_threads = true; #endif - LLImage::initClass(); + LLImage::initClass(gSavedSettings.getBOOL("TextureNewByteRange")); LLVFSThread::initClass(enable_threads && false); LLLFSThread::initClass(enable_threads && false); -- cgit v1.3 From c6511d9c857c079e0360f88c05328feb70a8bc0d Mon Sep 17 00:00:00 2001 From: Merov Linden Date: Wed, 4 Apr 2012 15:59:52 -0700 Subject: SH-3075 : Fix encoding for reversible images and small textures --- indra/llimage/llimage.h | 2 + indra/llkdu/llimagej2ckdu.cpp | 112 +++++++++++++++++++++--------------------- 2 files changed, 58 insertions(+), 56 deletions(-) (limited to 'indra/llkdu/llimagej2ckdu.cpp') diff --git a/indra/llimage/llimage.h b/indra/llimage/llimage.h index b757547ab8..18f8ae2fbb 100644 --- a/indra/llimage/llimage.h +++ b/indra/llimage/llimage.h @@ -48,6 +48,8 @@ const S32 MAX_PRECINCT_SIZE = 2048; // No reason to be bigger than MAX_IMAGE_S const S32 MIN_PRECINCT_SIZE = 4; // Can't be smaller than MIN_BLOCK_SIZE const S32 MAX_BLOCK_SIZE = 64; // Max total block size is 4096, hence 64x64 when using square blocks const S32 MIN_BLOCK_SIZE = 4; // Min block dim is 4 according to jpeg2000 spec +const S32 MIN_LAYER_SIZE = 2000; // Size of the first quality layer (after header). Must be > to FIRST_PACKET_SIZE!! +const S32 MAX_NB_LAYERS = 64; // Max number of layers we'll entertain in SL (practical limit) const S32 MIN_IMAGE_SIZE = (1<= 3) { // Note that we always use YCC and not YUV @@ -612,67 +606,70 @@ BOOL LLImageJ2CKDU::encodeImpl(LLImageJ2C &base, const LLImageRaw &raw_image, co set_default_colour_weights(codestream.access_siz()); } + // Set codestream options + int num_layer_specs = 0; + kdu_long layer_bytes[MAX_NB_LAYERS]; + U32 max_bytes = (U32)(base.getWidth() * base.getHeight() * base.getComponents()); + + // Rate is the argument passed into the LLImageJ2C which + // specifies the target compression rate. The default is 8:1. + // *TODO: mRate is actually always 8:1 in the viewer. Test different values. Also force to reversible for small (< 500 bytes) textures. + if (base.mRate != 0.f) + { + max_bytes = (U32)((F32)(max_bytes) * base.mRate); + } + else + { + max_bytes = (U32)((F32)(max_bytes) / 8.0f); + } + + // If the image is very small, code it in a lossless way. + // Note: it'll also have only 1 layer which is fine as there's no point reordering blocks in that case. + if (max_bytes < FIRST_PACKET_SIZE) + { + reversible = true; + } + + // This code is where we specify the target number of bytes for each quality layer. + // We're using a logarithmic spacing rule that fits with our way of fetching texture data. + // Note: For more info on this layers business, read kdu_codestream::flush() doc in kdu_compressed.h + U32 i = FIRST_PACKET_SIZE; + while ((i < max_bytes) && (num_layer_specs < (MAX_NB_LAYERS-1))) + { + if (i == FIRST_PACKET_SIZE * 4) + { + // That really just means that the first layer is FIRST_PACKET_SIZE and the second is MIN_LAYER_SIZE + i = MIN_LAYER_SIZE; + } + layer_bytes[num_layer_specs] = i; + num_layer_specs++; + i *= 4; + } + if (reversible) { codestream.access_siz()->parse_string("Creversible=yes"); - // *TODO: we should use yuv in reversible mode and one level since those images are small. + // *TODO: we should use yuv in reversible mode and one res level since those images are small. // Don't turn this on now though as both create problems on decoding for the moment //codestream.access_siz()->parse_string("Clevels=1"); //codestream.access_siz()->parse_string("Cycc=no"); - // If we're doing reversible (i.e. lossless compression), assumes we're not using quality layers. - // *TODO: this is incorrect and unecessary. Try using the regular layer setting. - codestream.access_siz()->parse_string("Clayers=1"); - num_layer_specs = 1; - layer_bytes[0] = 0; + // In the reversible case, set the last entry of that table to 0 so that all generated bits will + // indeed be output by the time the last quality layer is encountered. + layer_bytes[num_layer_specs] = 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? - // *TODO: mRate is actually always 8:1 in the viewer. Test different values. Also force to reversible for small (< 500 bytes) textures. - 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()); - } + // Truncate the last quality layer if necessary so to fit the set compression ratio + 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()); // Set up data ordering, markers, etc... if precincts or blocks specified + // Note: This code is *not* used in the encoding made by the viewer. It is currently used only + // by llimage_libtest to create various j2c and test alternative compression schemes. if ((mBlocksSize != -1) || (mPrecinctsSize != -1)) { if (mPrecinctsSize != -1) @@ -692,16 +689,19 @@ BOOL LLImageJ2CKDU::encodeImpl(LLImageJ2C &base, const LLImageRaw &raw_image, co std::string Parts_string = llformat("ORGtparts=R"); codestream.access_siz()->parse_string(Parts_string.c_str()); } + + // Set the number of wavelets subresolutions (aka levels) if (mLevels != 0) { std::string levels_string = llformat("Clevels=%d",mLevels); codestream.access_siz()->parse_string(levels_string.c_str()); } + // Complete the encode settings codestream.access_siz()->finalize_all(); codestream.change_appearance(transpose,vflip,hflip); - // Now we are ready for sample data processing. + // Now we are ready for sample data processing kdc_flow_control *tile = new kdc_flow_control(&mem_in,codestream); bool done = false; while (!done) -- cgit v1.3 From 782981866a70f4a33a298c93ee80aaf138fdf459 Mon Sep 17 00:00:00 2001 From: Merov Linden Date: Wed, 4 Apr 2012 18:58:34 -0700 Subject: SH-3060 : New byte range implementation. Intermediate (not working) state. --- indra/llimage/llimage.cpp | 3 ++- indra/llimage/llimage.h | 3 +++ indra/llimage/llimagej2c.cpp | 20 +++++++++++----- indra/llimage/llimagej2c.h | 3 ++- indra/llkdu/llimagej2ckdu.cpp | 40 +++++++++++++++++--------------- indra/llkdu/llimagej2ckdu.h | 1 + indra/llkdu/tests/llimagej2ckdu_test.cpp | 1 + 7 files changed, 44 insertions(+), 27 deletions(-) (limited to 'indra/llkdu/llimagej2ckdu.cpp') diff --git a/indra/llimage/llimage.cpp b/indra/llimage/llimage.cpp index 7f95441075..cce03ff6e6 100644 --- a/indra/llimage/llimage.cpp +++ b/indra/llimage/llimage.cpp @@ -1337,7 +1337,8 @@ LLImageFormatted::LLImageFormatted(S8 codec) mDecoding(0), mDecoded(0), mDiscardLevel(-1), - mLevels(0) + mLevels(0), + mLayers(0) { mMemType = LLMemType::MTYPE_IMAGEFORMATTED; } diff --git a/indra/llimage/llimage.h b/indra/llimage/llimage.h index 18f8ae2fbb..c5a7d6262e 100644 --- a/indra/llimage/llimage.h +++ b/indra/llimage/llimage.h @@ -320,6 +320,8 @@ public: S8 getDiscardLevel() const { return mDiscardLevel; } S8 getLevels() const { return mLevels; } void setLevels(S8 nlevels) { mLevels = nlevels; } + S32 getLayers() const { return mLayers; } + void setLayers(S32 nlayers) { mLayers = nlayers; } // setLastError needs to be deferred for J2C images since it may be called from a DLL virtual void resetLastError(); @@ -334,6 +336,7 @@ protected: S8 mDecoded; // unused, but changing LLImage layout requires recompiling static Mac/Linux libs. 2009-01-30 JC S8 mDiscardLevel; // Current resolution level worked on. 0 = full res, 1 = half res, 2 = quarter res, etc... S8 mLevels; // Number of resolution levels in that image. Min is 1. 0 means unknown. + S32 mLayers; // Number of quality layers in that image. Min is 1. 0 means unknown. public: static S32 sGlobalFormattedMemory; diff --git a/indra/llimage/llimagej2c.cpp b/indra/llimage/llimagej2c.cpp index cbb6f75b43..dc5bd8b5d5 100644 --- a/indra/llimage/llimagej2c.cpp +++ b/indra/llimage/llimagej2c.cpp @@ -58,7 +58,8 @@ LLImageJ2C::LLImageJ2C() : LLImageFormatted(IMG_CODEC_J2C), mRawDiscardLevel(-1), mRate(0.0f), mReversible(FALSE), - mAreaUsedForDataSizeCalcs(0) + mAreaUsedForDataSizeCalcs(0), + mLayersUsedForDataSizeCalcs(0) { mImpl = fallbackCreateLLImageJ2CImpl(); @@ -260,7 +261,7 @@ S32 LLImageJ2C::calcHeaderSizeJ2C() } //static -S32 LLImageJ2C::calcDataSizeJ2C(S32 w, S32 h, S32 comp, S32 discard_level, F32 rate) +S32 LLImageJ2C::calcDataSizeJ2C(S32 w, S32 h, S32 comp, S32 discard_level, S32 nb_layers, F32 rate) { // Note: This provides an estimation for the first quality layer of a given discard level // This is however an efficient approximation, as the true discard level boundary would be @@ -278,10 +279,11 @@ S32 LLImageJ2C::calcDataSizeJ2C(S32 w, S32 h, S32 comp, S32 discard_level, F32 r } // Temporary: compute both new and old range and pick one according to the settings TextureNewByteRange // *TODO: Take the old code out once we have enough tests done - // *TODO: Replace the magic "7" by the number of quality layers in the j2c image S32 bytes; - S32 new_bytes = sqrt((F32)(w*h))*(F32)(comp)*rate*1000.f/7.f; + F32 layer_factor = ((nb_layers > 0) && (nb_layers < 7) ? 3.0f * (7 - nb_layers): 3.0f); + S32 new_bytes = sqrt((F32)(w*h))*(F32)(comp)*rate*1000.f/layer_factor; S32 old_bytes = (S32)((F32)(w*h*comp)*rate); + llinfos << "Merov debug : calcDataSizeJ2C, layers = " << nb_layers << ", old = " << old_bytes << ", new = " << new_bytes << llendl; bytes = (LLImage::useNewByteRange() ? new_bytes : old_bytes); bytes = llmax(bytes, calcHeaderSizeJ2C()); return bytes; @@ -298,14 +300,20 @@ S32 LLImageJ2C::calcDataSize(S32 discard_level) discard_level = llclamp(discard_level, 0, MAX_DISCARD_LEVEL); if ( mAreaUsedForDataSizeCalcs != (getHeight() * getWidth()) - || mDataSizes[0] == 0) + || (mLayersUsedForDataSizeCalcs != getLayers()) + || (mDataSizes[0] == 0)) { + if (mLayersUsedForDataSizeCalcs != getLayers()) + { + llinfos << "Merov debug : recomputing data size because " << mLayersUsedForDataSizeCalcs << " != " << getLayers() << llendl; + } mAreaUsedForDataSizeCalcs = getHeight() * getWidth(); + mLayersUsedForDataSizeCalcs = getLayers(); S32 level = MAX_DISCARD_LEVEL; // Start at the highest discard while ( level >= 0 ) { - mDataSizes[level] = calcDataSizeJ2C(getWidth(), getHeight(), getComponents(), level, mRate); + mDataSizes[level] = calcDataSizeJ2C(getWidth(), getHeight(), getComponents(), level, getLayers(), mRate); level--; } } diff --git a/indra/llimage/llimagej2c.h b/indra/llimage/llimagej2c.h index 914174fc57..28e3026aac 100644 --- a/indra/llimage/llimagej2c.h +++ b/indra/llimage/llimagej2c.h @@ -72,7 +72,7 @@ public: S32 getMaxBytes() const { return mMaxBytes; } static S32 calcHeaderSizeJ2C(); - static S32 calcDataSizeJ2C(S32 w, S32 h, S32 comp, S32 discard_level, F32 rate = 0.f); + static S32 calcDataSizeJ2C(S32 w, S32 h, S32 comp, S32 discard_level, S32 nb_layers = 0, F32 rate = 0.f); static std::string getEngineInfo(); @@ -88,6 +88,7 @@ protected: S32 mDataSizes[MAX_DISCARD_LEVEL+1]; // Size of data required to reach a given level U32 mAreaUsedForDataSizeCalcs; // Height * width used to calculate mDataSizes + S32 mLayersUsedForDataSizeCalcs; // Numbers of layers used to calculate mDataSizes S8 mRawDiscardLevel; F32 mRate; diff --git a/indra/llkdu/llimagej2ckdu.cpp b/indra/llkdu/llimagej2ckdu.cpp index 78c9be7bfd..08d840917d 100644 --- a/indra/llkdu/llimagej2ckdu.cpp +++ b/indra/llkdu/llimagej2ckdu.cpp @@ -195,7 +195,8 @@ mRawImagep(NULL), mDecodeState(NULL), mBlocksSize(-1), mPrecinctsSize(-1), -mLevels(0) +mLevels(0), +mLayers(0) { } @@ -245,6 +246,8 @@ void LLImageJ2CKDU::setupCodeStream(LLImageJ2C &base, BOOL keep_codestream, ECod mCodeStreamp->create(mInputp); // Set the maximum number of bytes to use from the codestream + // *TODO: This seems to be wrong. The base class should have no idea of how j2c compression works so no + // good way of computing what's the byte range to be used. mCodeStreamp->set_max_bytes(max_bytes,true); // If you want to flip or rotate the image for some reason, change @@ -295,13 +298,6 @@ void LLImageJ2CKDU::setupCodeStream(LLImageJ2C &base, BOOL keep_codestream, ECod // Get the number of resolution levels in that image mLevels = mCodeStreamp->get_min_dwt_levels(); - //kdu_coords idx; idx.x = 0; idx.y = 0; - //kdu_dims tile_indices_in; - //mCodeStreamp->get_valid_tiles(tile_indices_in); - //mCodeStreamp->create_tile(idx+tile_indices_in.pos); - //int layers = mCodeStreamp->get_max_tile_layers(); - //llinfos << "Merov debug : setupCodeStream, levels = " << mLevels << ", layers = " << layers << llendl; - // Set the base dimensions base.setSize(dims.size.x, dims.size.y, components); base.setLevels(mLevels); @@ -364,7 +360,8 @@ BOOL LLImageJ2CKDU::initEncode(LLImageJ2C &base, LLImageRaw &raw_image, int bloc mLevels = levels; if (mLevels != 0) { - mLevels = llclamp(mLevels,MIN_DECOMPOSITION_LEVELS,MIN_DECOMPOSITION_LEVELS); + mLevels = llclamp(mLevels,MIN_DECOMPOSITION_LEVELS,MAX_DECOMPOSITION_LEVELS); + base.setLevels(mLevels); } return TRUE; } @@ -476,7 +473,11 @@ BOOL LLImageJ2CKDU::decodeImpl(LLImageJ2C &base, LLImageRaw &raw_image, F32 deco kdu_tile tile = mCodeStreamp->open_tile(*(mTPosp)+mTileIndicesp->pos); int layers = mCodeStreamp->get_max_tile_layers(); - llinfos << "Merov debug : decodeImpl, levels = " << mLevels << ", layers = " << layers << llendl; + if (layers > mLayers) + { + mLayers = layers; + base.setLayers(mLayers); + } // Find the region of the buffer occupied by this // tile. Note that we have no control over @@ -607,7 +608,7 @@ BOOL LLImageJ2CKDU::encodeImpl(LLImageJ2C &base, const LLImageRaw &raw_image, co } // Set codestream options - int num_layer_specs = 0; + mLayers = 0; kdu_long layer_bytes[MAX_NB_LAYERS]; U32 max_bytes = (U32)(base.getWidth() * base.getHeight() * base.getComponents()); @@ -634,15 +635,15 @@ BOOL LLImageJ2CKDU::encodeImpl(LLImageJ2C &base, const LLImageRaw &raw_image, co // We're using a logarithmic spacing rule that fits with our way of fetching texture data. // Note: For more info on this layers business, read kdu_codestream::flush() doc in kdu_compressed.h U32 i = FIRST_PACKET_SIZE; - while ((i < max_bytes) && (num_layer_specs < (MAX_NB_LAYERS-1))) + while ((i < max_bytes) && (mLayers < (MAX_NB_LAYERS-1))) { if (i == FIRST_PACKET_SIZE * 4) { // That really just means that the first layer is FIRST_PACKET_SIZE and the second is MIN_LAYER_SIZE i = MIN_LAYER_SIZE; } - layer_bytes[num_layer_specs] = i; - num_layer_specs++; + layer_bytes[mLayers] = i; + mLayers++; i *= 4; } @@ -655,17 +656,18 @@ BOOL LLImageJ2CKDU::encodeImpl(LLImageJ2C &base, const LLImageRaw &raw_image, co //codestream.access_siz()->parse_string("Cycc=no"); // In the reversible case, set the last entry of that table to 0 so that all generated bits will // indeed be output by the time the last quality layer is encountered. - layer_bytes[num_layer_specs] = 0; + layer_bytes[mLayers] = 0; } else { // Truncate the last quality layer if necessary so to fit the set compression ratio - layer_bytes[num_layer_specs] = max_bytes; + layer_bytes[mLayers] = max_bytes; } - num_layer_specs++; + mLayers++; - std::string layer_string = llformat("Clayers=%d",num_layer_specs); + std::string layer_string = llformat("Clayers=%d",mLayers); codestream.access_siz()->parse_string(layer_string.c_str()); + base.setLayers(mLayers); // Set up data ordering, markers, etc... if precincts or blocks specified // Note: This code is *not* used in the encoding made by the viewer. It is currently used only @@ -718,7 +720,7 @@ BOOL LLImageJ2CKDU::encodeImpl(LLImageJ2C &base, const LLImageRaw &raw_image, co } // Produce the compressed output - codestream.flush(layer_bytes,num_layer_specs); + codestream.flush(layer_bytes,mLayers); // Cleanup delete tile; diff --git a/indra/llkdu/llimagej2ckdu.h b/indra/llkdu/llimagej2ckdu.h index 9ab0b9e4a7..fab97326d4 100644 --- a/indra/llkdu/llimagej2ckdu.h +++ b/indra/llkdu/llimagej2ckdu.h @@ -75,6 +75,7 @@ private: int mBlocksSize; int mPrecinctsSize; int mLevels; + int mLayers; // Temporary variables for in-progress decodes... LLImageRaw *mRawImagep; diff --git a/indra/llkdu/tests/llimagej2ckdu_test.cpp b/indra/llkdu/tests/llimagej2ckdu_test.cpp index feb6671e40..405f7c9388 100644 --- a/indra/llkdu/tests/llimagej2ckdu_test.cpp +++ b/indra/llkdu/tests/llimagej2ckdu_test.cpp @@ -146,6 +146,7 @@ void kdu_codestream::set_fast() { } void kdu_codestream::set_fussy() { } void kdu_codestream::get_dims(int, kdu_dims&, bool ) { } int kdu_codestream::get_min_dwt_levels() { return 5; } +int kdu_codestream::get_max_tile_layers() { return 1; } void kdu_codestream::change_appearance(bool, bool, bool) { } void kdu_codestream::get_tile_dims(kdu_coords, int, kdu_dims&, bool ) { } void kdu_codestream::destroy() { } -- cgit v1.3 From 91094d92a75b3900be15bfb8be4b9f7cc072487b Mon Sep 17 00:00:00 2001 From: Merov Linden Date: Thu, 5 Apr 2012 22:05:00 -0700 Subject: SH-3060 : Implement new byte range computation, cleaned up use of compression rate as well. --- indra/llimage/llimage.cpp | 3 +- indra/llimage/llimage.h | 4 +-- indra/llimage/llimagej2c.cpp | 55 +++++++++++++++++------------------ indra/llimage/llimagej2c.h | 7 +++-- indra/llkdu/llimagej2ckdu.cpp | 44 +++++++++------------------- indra/llkdu/llimagej2ckdu.h | 1 - indra/newview/lltexlayer.cpp | 1 - indra/newview/llviewertexturelist.cpp | 1 - 8 files changed, 46 insertions(+), 70 deletions(-) (limited to 'indra/llkdu/llimagej2ckdu.cpp') diff --git a/indra/llimage/llimage.cpp b/indra/llimage/llimage.cpp index cce03ff6e6..7f95441075 100644 --- a/indra/llimage/llimage.cpp +++ b/indra/llimage/llimage.cpp @@ -1337,8 +1337,7 @@ LLImageFormatted::LLImageFormatted(S8 codec) mDecoding(0), mDecoded(0), mDiscardLevel(-1), - mLevels(0), - mLayers(0) + mLevels(0) { mMemType = LLMemType::MTYPE_IMAGEFORMATTED; } diff --git a/indra/llimage/llimage.h b/indra/llimage/llimage.h index c5a7d6262e..a643e4d9f5 100644 --- a/indra/llimage/llimage.h +++ b/indra/llimage/llimage.h @@ -62,6 +62,7 @@ const S32 MAX_IMAGE_DATA_SIZE = MAX_IMAGE_AREA * MAX_IMAGE_COMPONENTS; //2048 * // *TODO: change both to 1024 when SIM texture fetching is deprecated const S32 FIRST_PACKET_SIZE = 600; const S32 MAX_IMG_PACKET_SIZE = 1000; +const S32 HTTP_PACKET_SIZE = 1496; // Base classes for images. // There are two major parts for the image: @@ -320,8 +321,6 @@ public: S8 getDiscardLevel() const { return mDiscardLevel; } S8 getLevels() const { return mLevels; } void setLevels(S8 nlevels) { mLevels = nlevels; } - S32 getLayers() const { return mLayers; } - void setLayers(S32 nlayers) { mLayers = nlayers; } // setLastError needs to be deferred for J2C images since it may be called from a DLL virtual void resetLastError(); @@ -336,7 +335,6 @@ protected: S8 mDecoded; // unused, but changing LLImage layout requires recompiling static Mac/Linux libs. 2009-01-30 JC S8 mDiscardLevel; // Current resolution level worked on. 0 = full res, 1 = half res, 2 = quarter res, etc... S8 mLevels; // Number of resolution levels in that image. Min is 1. 0 means unknown. - S32 mLayers; // Number of quality layers in that image. Min is 1. 0 means unknown. public: static S32 sGlobalFormattedMemory; diff --git a/indra/llimage/llimagej2c.cpp b/indra/llimage/llimagej2c.cpp index dc5bd8b5d5..7894de0de5 100644 --- a/indra/llimage/llimagej2c.cpp +++ b/indra/llimage/llimagej2c.cpp @@ -56,10 +56,9 @@ std::string LLImageJ2C::getEngineInfo() LLImageJ2C::LLImageJ2C() : LLImageFormatted(IMG_CODEC_J2C), mMaxBytes(0), mRawDiscardLevel(-1), - mRate(0.0f), + mRate(DEFAULT_COMPRESSION_RATE), mReversible(FALSE), - mAreaUsedForDataSizeCalcs(0), - mLayersUsedForDataSizeCalcs(0) + mAreaUsedForDataSizeCalcs(0) { mImpl = fallbackCreateLLImageJ2CImpl(); @@ -257,33 +256,40 @@ BOOL LLImageJ2C::encode(const LLImageRaw *raw_imagep, const char* comment_text, //static S32 LLImageJ2C::calcHeaderSizeJ2C() { - return FIRST_PACKET_SIZE; // Hack. just needs to be >= actual header size... + return HTTP_PACKET_SIZE; // Hack. just needs to be >= actual header size... } //static -S32 LLImageJ2C::calcDataSizeJ2C(S32 w, S32 h, S32 comp, S32 discard_level, S32 nb_layers, F32 rate) +S32 LLImageJ2C::calcDataSizeJ2C(S32 w, S32 h, S32 comp, S32 discard_level, F32 rate) { - // Note: This provides an estimation for the first quality layer of a given discard level + // Note: This provides an estimation for the first to last quality layer of a given discard level // This is however an efficient approximation, as the true discard level boundary would be // in general too big for fast fetching. // For details about the equation used here, see https://wiki.lindenlab.com/wiki/THX1138_KDU_Improvements#Byte_Range_Study - if (rate <= 0.f) rate = 1.f/8.f; - // Compute w/pow(2,discard_level) and h/pow(2,discard_level) - while (discard_level > 0) + + // Estimate the number of layers. This is consistent with what's done in j2c encoding + S32 nb_layers = 1; + S32 surface = w*h; + S32 s = 64*64; + while (surface > s) { - if (w < 1 || h < 1) - break; - w >>= 1; - h >>= 1; - discard_level--; + nb_layers++; + s *= 4; } + F32 layer_factor = 3.0f * (7 - llclamp(nb_layers,1,6)); + + // Compute w/pow(2,discard_level) and h/pow(2,discard_level) + w >>= discard_level; + h >>= discard_level; + w = llmax(w, 1); + h = llmax(h, 1); + // Temporary: compute both new and old range and pick one according to the settings TextureNewByteRange // *TODO: Take the old code out once we have enough tests done S32 bytes; - F32 layer_factor = ((nb_layers > 0) && (nb_layers < 7) ? 3.0f * (7 - nb_layers): 3.0f); S32 new_bytes = sqrt((F32)(w*h))*(F32)(comp)*rate*1000.f/layer_factor; S32 old_bytes = (S32)((F32)(w*h*comp)*rate); - llinfos << "Merov debug : calcDataSizeJ2C, layers = " << nb_layers << ", old = " << old_bytes << ", new = " << new_bytes << llendl; + //llinfos << "Merov debug : w = " << w << ", h = " << h << ", c = " << comp << ", r = " << rate << ", d = " << discard_level << ", l = " << nb_layers << ", old = " << old_bytes << ", new = " << new_bytes << llendl; bytes = (LLImage::useNewByteRange() ? new_bytes : old_bytes); bytes = llmax(bytes, calcHeaderSizeJ2C()); return bytes; @@ -298,26 +304,22 @@ S32 LLImageJ2C::calcHeaderSize() S32 LLImageJ2C::calcDataSize(S32 discard_level) { discard_level = llclamp(discard_level, 0, MAX_DISCARD_LEVEL); - + return calcDataSizeJ2C(getWidth(), getHeight(), getComponents(), discard_level, mRate); + /* if ( mAreaUsedForDataSizeCalcs != (getHeight() * getWidth()) - || (mLayersUsedForDataSizeCalcs != getLayers()) || (mDataSizes[0] == 0)) { - if (mLayersUsedForDataSizeCalcs != getLayers()) - { - llinfos << "Merov debug : recomputing data size because " << mLayersUsedForDataSizeCalcs << " != " << getLayers() << llendl; - } mAreaUsedForDataSizeCalcs = getHeight() * getWidth(); - mLayersUsedForDataSizeCalcs = getLayers(); S32 level = MAX_DISCARD_LEVEL; // Start at the highest discard while ( level >= 0 ) { - mDataSizes[level] = calcDataSizeJ2C(getWidth(), getHeight(), getComponents(), level, getLayers(), mRate); + mDataSizes[level] = calcDataSizeJ2C(getWidth(), getHeight(), getComponents(), level, mRate); level--; } } return mDataSizes[discard_level]; + */ } S32 LLImageJ2C::calcDiscardLevelBytes(S32 bytes) @@ -344,11 +346,6 @@ S32 LLImageJ2C::calcDiscardLevelBytes(S32 bytes) return discard_level; } -void LLImageJ2C::setRate(F32 rate) -{ - mRate = rate; -} - void LLImageJ2C::setMaxBytes(S32 max_bytes) { mMaxBytes = max_bytes; diff --git a/indra/llimage/llimagej2c.h b/indra/llimage/llimagej2c.h index 28e3026aac..91c344d12f 100644 --- a/indra/llimage/llimagej2c.h +++ b/indra/llimage/llimagej2c.h @@ -31,6 +31,9 @@ #include "llassettype.h" #include "llmetricperformancetester.h" +// JPEG2000 : compression rate used in j2c conversion. +const F32 DEFAULT_COMPRESSION_RATE = 1.f/8.f; + class LLImageJ2CImpl; class LLImageCompressionTester ; @@ -67,12 +70,11 @@ public: // Encode accessors void setReversible(const BOOL reversible); // Use non-lossy? - void setRate(F32 rate); void setMaxBytes(S32 max_bytes); S32 getMaxBytes() const { return mMaxBytes; } static S32 calcHeaderSizeJ2C(); - static S32 calcDataSizeJ2C(S32 w, S32 h, S32 comp, S32 discard_level, S32 nb_layers = 0, F32 rate = 0.f); + static S32 calcDataSizeJ2C(S32 w, S32 h, S32 comp, S32 discard_level, F32 rate = DEFAULT_COMPRESSION_RATE); static std::string getEngineInfo(); @@ -88,7 +90,6 @@ protected: S32 mDataSizes[MAX_DISCARD_LEVEL+1]; // Size of data required to reach a given level U32 mAreaUsedForDataSizeCalcs; // Height * width used to calculate mDataSizes - S32 mLayersUsedForDataSizeCalcs; // Numbers of layers used to calculate mDataSizes S8 mRawDiscardLevel; F32 mRate; diff --git a/indra/llkdu/llimagej2ckdu.cpp b/indra/llkdu/llimagej2ckdu.cpp index 08d840917d..31db9d48e3 100644 --- a/indra/llkdu/llimagej2ckdu.cpp +++ b/indra/llkdu/llimagej2ckdu.cpp @@ -195,8 +195,7 @@ mRawImagep(NULL), mDecodeState(NULL), mBlocksSize(-1), mPrecinctsSize(-1), -mLevels(0), -mLayers(0) +mLevels(0) { } @@ -472,13 +471,6 @@ BOOL LLImageJ2CKDU::decodeImpl(LLImageJ2C &base, LLImageRaw &raw_image, F32 deco { kdu_tile tile = mCodeStreamp->open_tile(*(mTPosp)+mTileIndicesp->pos); - int layers = mCodeStreamp->get_max_tile_layers(); - if (layers > mLayers) - { - mLayers = layers; - base.setLayers(mLayers); - } - // 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 @@ -608,21 +600,14 @@ BOOL LLImageJ2CKDU::encodeImpl(LLImageJ2C &base, const LLImageRaw &raw_image, co } // Set codestream options - mLayers = 0; + int nb_layers = 0; kdu_long layer_bytes[MAX_NB_LAYERS]; U32 max_bytes = (U32)(base.getWidth() * base.getHeight() * base.getComponents()); - // Rate is the argument passed into the LLImageJ2C which - // specifies the target compression rate. The default is 8:1. - // *TODO: mRate is actually always 8:1 in the viewer. Test different values. Also force to reversible for small (< 500 bytes) textures. - if (base.mRate != 0.f) - { - max_bytes = (U32)((F32)(max_bytes) * base.mRate); - } - else - { - max_bytes = (U32)((F32)(max_bytes) / 8.0f); - } + // Rate is the argument passed into the LLImageJ2C which specifies the target compression rate. The default is 8:1. + // *TODO: mRate is actually always 8:1 in the viewer. Test different values. + llassert (base.mRate > 0.f); + max_bytes = (U32)((F32)(max_bytes) * base.mRate); // If the image is very small, code it in a lossless way. // Note: it'll also have only 1 layer which is fine as there's no point reordering blocks in that case. @@ -635,15 +620,15 @@ BOOL LLImageJ2CKDU::encodeImpl(LLImageJ2C &base, const LLImageRaw &raw_image, co // We're using a logarithmic spacing rule that fits with our way of fetching texture data. // Note: For more info on this layers business, read kdu_codestream::flush() doc in kdu_compressed.h U32 i = FIRST_PACKET_SIZE; - while ((i < max_bytes) && (mLayers < (MAX_NB_LAYERS-1))) + while ((i < max_bytes) && (nb_layers < (MAX_NB_LAYERS-1))) { if (i == FIRST_PACKET_SIZE * 4) { // That really just means that the first layer is FIRST_PACKET_SIZE and the second is MIN_LAYER_SIZE i = MIN_LAYER_SIZE; } - layer_bytes[mLayers] = i; - mLayers++; + layer_bytes[nb_layers] = i; + nb_layers++; i *= 4; } @@ -656,18 +641,17 @@ BOOL LLImageJ2CKDU::encodeImpl(LLImageJ2C &base, const LLImageRaw &raw_image, co //codestream.access_siz()->parse_string("Cycc=no"); // In the reversible case, set the last entry of that table to 0 so that all generated bits will // indeed be output by the time the last quality layer is encountered. - layer_bytes[mLayers] = 0; + layer_bytes[nb_layers] = 0; } else { // Truncate the last quality layer if necessary so to fit the set compression ratio - layer_bytes[mLayers] = max_bytes; + layer_bytes[nb_layers] = max_bytes; } - mLayers++; + nb_layers++; - std::string layer_string = llformat("Clayers=%d",mLayers); + std::string layer_string = llformat("Clayers=%d",nb_layers); codestream.access_siz()->parse_string(layer_string.c_str()); - base.setLayers(mLayers); // Set up data ordering, markers, etc... if precincts or blocks specified // Note: This code is *not* used in the encoding made by the viewer. It is currently used only @@ -720,7 +704,7 @@ BOOL LLImageJ2CKDU::encodeImpl(LLImageJ2C &base, const LLImageRaw &raw_image, co } // Produce the compressed output - codestream.flush(layer_bytes,mLayers); + codestream.flush(layer_bytes,nb_layers); // Cleanup delete tile; diff --git a/indra/llkdu/llimagej2ckdu.h b/indra/llkdu/llimagej2ckdu.h index fab97326d4..9ab0b9e4a7 100644 --- a/indra/llkdu/llimagej2ckdu.h +++ b/indra/llkdu/llimagej2ckdu.h @@ -75,7 +75,6 @@ private: int mBlocksSize; int mPrecinctsSize; int mLevels; - int mLayers; // Temporary variables for in-progress decodes... LLImageRaw *mRawImagep; diff --git a/indra/newview/lltexlayer.cpp b/indra/newview/lltexlayer.cpp index 1693cfc9e2..467115c928 100644 --- a/indra/newview/lltexlayer.cpp +++ b/indra/newview/lltexlayer.cpp @@ -497,7 +497,6 @@ void LLTexLayerSetBuffer::doUpload() } LLPointer compressedImage = new LLImageJ2C; - compressedImage->setRate(0.f); const char* comment_text = LINDEN_J2C_COMMENT_PREFIX "RGBHM"; // writes into baked_color_data. 5 channels (rgb, heightfield/alpha, mask) if (compressedImage->encode(baked_image, comment_text)) { diff --git a/indra/newview/llviewertexturelist.cpp b/indra/newview/llviewertexturelist.cpp index 089f45ca89..54ae519422 100644 --- a/indra/newview/llviewertexturelist.cpp +++ b/indra/newview/llviewertexturelist.cpp @@ -1030,7 +1030,6 @@ LLPointer LLViewerTextureList::convertToUploadFile(LLPointerbiasedScaleToPowerOfTwo(LLViewerFetchedTexture::MAX_IMAGE_SIZE_DEFAULT); LLPointer compressedImage = new LLImageJ2C(); - compressedImage->setRate(0.f); if (gSavedSettings.getBOOL("LosslessJ2CUpload") && (raw_image->getWidth() * raw_image->getHeight() <= LL_IMAGE_REZ_LOSSLESS_CUTOFF * LL_IMAGE_REZ_LOSSLESS_CUTOFF)) -- cgit v1.3 From 172b45d5a217c7cdb922f49706b310edc412fc28 Mon Sep 17 00:00:00 2001 From: Merov Linden Date: Fri, 6 Apr 2012 18:28:47 -0700 Subject: SH-3060 : Complete new byte range computation, clean up and back pedal on some changes that didn't pay of. --- indra/llimage/llimagej2c.cpp | 11 ++++------- indra/llkdu/llimagej2ckdu.cpp | 2 +- 2 files changed, 5 insertions(+), 8 deletions(-) (limited to 'indra/llkdu/llimagej2ckdu.cpp') diff --git a/indra/llimage/llimagej2c.cpp b/indra/llimage/llimagej2c.cpp index 7894de0de5..69d261c9a6 100644 --- a/indra/llimage/llimagej2c.cpp +++ b/indra/llimage/llimagej2c.cpp @@ -256,7 +256,7 @@ BOOL LLImageJ2C::encode(const LLImageRaw *raw_imagep, const char* comment_text, //static S32 LLImageJ2C::calcHeaderSizeJ2C() { - return HTTP_PACKET_SIZE; // Hack. just needs to be >= actual header size... + return FIRST_PACKET_SIZE; // Hack. just needs to be >= actual header size... } //static @@ -267,7 +267,7 @@ S32 LLImageJ2C::calcDataSizeJ2C(S32 w, S32 h, S32 comp, S32 discard_level, F32 r // in general too big for fast fetching. // For details about the equation used here, see https://wiki.lindenlab.com/wiki/THX1138_KDU_Improvements#Byte_Range_Study - // Estimate the number of layers. This is consistent with what's done in j2c encoding + // Estimate the number of layers. This is consistent with what's done for j2c encoding in LLImageJ2CKDU::encodeImpl(). S32 nb_layers = 1; S32 surface = w*h; S32 s = 64*64; @@ -304,8 +304,6 @@ S32 LLImageJ2C::calcHeaderSize() S32 LLImageJ2C::calcDataSize(S32 discard_level) { discard_level = llclamp(discard_level, 0, MAX_DISCARD_LEVEL); - return calcDataSizeJ2C(getWidth(), getHeight(), getComponents(), discard_level, mRate); - /* if ( mAreaUsedForDataSizeCalcs != (getHeight() * getWidth()) || (mDataSizes[0] == 0)) { @@ -319,7 +317,6 @@ S32 LLImageJ2C::calcDataSize(S32 discard_level) } } return mDataSizes[discard_level]; - */ } S32 LLImageJ2C::calcDiscardLevelBytes(S32 bytes) @@ -332,8 +329,8 @@ S32 LLImageJ2C::calcDiscardLevelBytes(S32 bytes) } while (1) { - S32 bytes_needed = calcDataSize(discard_level); - if (bytes >= bytes_needed) + S32 bytes_needed = calcDataSize(discard_level); // virtual + if (bytes >= bytes_needed - (bytes_needed>>2)) // For J2c, up the res at 75% of the optimal number of bytes { break; } diff --git a/indra/llkdu/llimagej2ckdu.cpp b/indra/llkdu/llimagej2ckdu.cpp index 31db9d48e3..cbfc34ebb8 100644 --- a/indra/llkdu/llimagej2ckdu.cpp +++ b/indra/llkdu/llimagej2ckdu.cpp @@ -393,7 +393,7 @@ BOOL LLImageJ2CKDU::initDecode(LLImageJ2C &base, LLImageRaw &raw_image, F32 deco region_kdu->size.y = region[3] - region[1]; } int discard = (discard_level != -1 ? discard_level : base.getRawDiscardLevel()); - llinfos << "Merov debug : initDecode, discard used = " << discard << ", asked = " << discard_level << llendl; + //llinfos << "Merov debug : initDecode, discard used = " << discard << ", asked = " << discard_level << llendl; // Apply loading restrictions mCodeStreamp->apply_input_restrictions( first_channel, max_channel_count, discard, 0, region_kdu); -- cgit v1.3 From 005ce31f41fc60b7b9c7890d354943b3e3568ac9 Mon Sep 17 00:00:00 2001 From: Merov Linden Date: Tue, 8 May 2012 21:42:53 -0700 Subject: SH-3047 : Fix lossless compression for small textures. --- indra/llkdu/llimagej2ckdu.cpp | 31 +++++++++++-------------------- 1 file changed, 11 insertions(+), 20 deletions(-) (limited to 'indra/llkdu/llimagej2ckdu.cpp') diff --git a/indra/llkdu/llimagej2ckdu.cpp b/indra/llkdu/llimagej2ckdu.cpp index cbfc34ebb8..53d2a2f3c5 100644 --- a/indra/llkdu/llimagej2ckdu.cpp +++ b/indra/llkdu/llimagej2ckdu.cpp @@ -619,36 +619,27 @@ BOOL LLImageJ2CKDU::encodeImpl(LLImageJ2C &base, const LLImageRaw &raw_image, co // This code is where we specify the target number of bytes for each quality layer. // We're using a logarithmic spacing rule that fits with our way of fetching texture data. // Note: For more info on this layers business, read kdu_codestream::flush() doc in kdu_compressed.h - U32 i = FIRST_PACKET_SIZE; + layer_bytes[nb_layers++] = FIRST_PACKET_SIZE; + U32 i = MIN_LAYER_SIZE; while ((i < max_bytes) && (nb_layers < (MAX_NB_LAYERS-1))) { - if (i == FIRST_PACKET_SIZE * 4) - { - // That really just means that the first layer is FIRST_PACKET_SIZE and the second is MIN_LAYER_SIZE - i = MIN_LAYER_SIZE; - } - layer_bytes[nb_layers] = i; - nb_layers++; + layer_bytes[nb_layers++] = i; i *= 4; } + if (layer_bytes[nb_layers-1] < max_bytes) + { + // Set the last quality layer if necessary so to fit the preset compression ratio + // Use 0 for that last layer for reversible images so all remaining code blocks will be flushed + layer_bytes[nb_layers++] = (reversible ? 0 : max_bytes); + } if (reversible) { codestream.access_siz()->parse_string("Creversible=yes"); - // *TODO: we should use yuv in reversible mode and one res level since those images are small. - // Don't turn this on now though as both create problems on decoding for the moment - //codestream.access_siz()->parse_string("Clevels=1"); + // *TODO: we should use yuv in reversible mode + // Don't turn this on now though as it creates problems on decoding for the moment //codestream.access_siz()->parse_string("Cycc=no"); - // In the reversible case, set the last entry of that table to 0 so that all generated bits will - // indeed be output by the time the last quality layer is encountered. - layer_bytes[nb_layers] = 0; - } - else - { - // Truncate the last quality layer if necessary so to fit the set compression ratio - layer_bytes[nb_layers] = max_bytes; } - nb_layers++; std::string layer_string = llformat("Clayers=%d",nb_layers); codestream.access_siz()->parse_string(layer_string.c_str()); -- cgit v1.3 From 7fd22901774ff9dcdfbf4388550275e30200307f Mon Sep 17 00:00:00 2001 From: Merov Linden Date: Wed, 9 May 2012 17:04:02 -0700 Subject: SH-3075 : Fix reversible compression for very small textures. Also supress the forcing to reversible for small textures. --- indra/llkdu/llimagej2ckdu.cpp | 24 +++++++++++------------- 1 file changed, 11 insertions(+), 13 deletions(-) (limited to 'indra/llkdu/llimagej2ckdu.cpp') diff --git a/indra/llkdu/llimagej2ckdu.cpp b/indra/llkdu/llimagej2ckdu.cpp index 53d2a2f3c5..cf88de12b4 100644 --- a/indra/llkdu/llimagej2ckdu.cpp +++ b/indra/llkdu/llimagej2ckdu.cpp @@ -609,13 +609,6 @@ BOOL LLImageJ2CKDU::encodeImpl(LLImageJ2C &base, const LLImageRaw &raw_image, co llassert (base.mRate > 0.f); max_bytes = (U32)((F32)(max_bytes) * base.mRate); - // If the image is very small, code it in a lossless way. - // Note: it'll also have only 1 layer which is fine as there's no point reordering blocks in that case. - if (max_bytes < FIRST_PACKET_SIZE) - { - reversible = true; - } - // This code is where we specify the target number of bytes for each quality layer. // We're using a logarithmic spacing rule that fits with our way of fetching texture data. // Note: For more info on this layers business, read kdu_codestream::flush() doc in kdu_compressed.h @@ -626,27 +619,32 @@ BOOL LLImageJ2CKDU::encodeImpl(LLImageJ2C &base, const LLImageRaw &raw_image, co layer_bytes[nb_layers++] = i; i *= 4; } + // Note: for small images, we can have (max_bytes < FIRST_PACKET_SIZE), hence the test if (layer_bytes[nb_layers-1] < max_bytes) { - // Set the last quality layer if necessary so to fit the preset compression ratio - // Use 0 for that last layer for reversible images so all remaining code blocks will be flushed - layer_bytes[nb_layers++] = (reversible ? 0 : max_bytes); + // Set the last quality layer so to fit the preset compression ratio + layer_bytes[nb_layers++] = max_bytes; } if (reversible) { + // Use 0 for a last quality layer for reversible images so all remaining code blocks will be flushed + // Hack: KDU encoding for reversible images has a bug for small images that leads to j2c images that + // cannot be open or are very blurry. Avoiding that last layer prevents the problem to happen. + if ((base.getWidth() >= 32) || (base.getHeight() >= 32)) + { + layer_bytes[nb_layers++] = 0; + } codestream.access_siz()->parse_string("Creversible=yes"); // *TODO: we should use yuv in reversible mode // Don't turn this on now though as it creates problems on decoding for the moment //codestream.access_siz()->parse_string("Cycc=no"); } - + std::string layer_string = llformat("Clayers=%d",nb_layers); codestream.access_siz()->parse_string(layer_string.c_str()); // Set up data ordering, markers, etc... if precincts or blocks specified - // Note: This code is *not* used in the encoding made by the viewer. It is currently used only - // by llimage_libtest to create various j2c and test alternative compression schemes. if ((mBlocksSize != -1) || (mPrecinctsSize != -1)) { if (mPrecinctsSize != -1) -- cgit v1.3