BOOL LLImageJ2CKDU::encodeImpl(LLImageJ2C &base, const LLImageRaw &raw_image, const char* comment_text, F32 encode_time, BOOL reversible) { // Declare and set simple arguments bool transpose = false; bool vflip = true; bool hflip = false; try { // Set up input image files siz_params siz; // Should set rate someplace here LLKDUMemIn mem_in(raw_image.getData(), raw_image.getDataSize(), raw_image.getWidth(), raw_image.getHeight(), raw_image.getComponents(), &siz); base.setSize(raw_image.getWidth(), raw_image.getHeight(), raw_image.getComponents()); int num_components = raw_image.getComponents(); siz.set(Scomponents,0,0,num_components); siz.set(Sdims,0,0,base.getHeight()); // Height of first image component siz.set(Sdims,0,1,base.getWidth()); // Width of first image component siz.set(Sprecision,0,0,8); // Image samples have original bit-depth of 8 siz.set(Ssigned,0,0,false); // Image samples are originally unsigned kdu_params *siz_ref = &siz; siz_ref->finalize(); siz_params transformed_siz; // Use this one to construct code-stream transformed_siz.copy_from(&siz,-1,-1,-1,0,transpose,false,false); // Construct the `kdu_codestream' object and parse all remaining arguments U32 max_output_size = base.getWidth()*base.getHeight()*base.getComponents(); max_output_size = (max_output_size < 1000 ? 1000 : max_output_size); U8 *output_buffer = new U8[max_output_size]; U32 output_size = 0; // Address updated by LLKDUMemTarget to give the final compressed buffer size LLKDUMemTarget output(output_buffer, output_size, max_output_size); kdu_codestream codestream; codestream.create(&transformed_siz,&output); if (comment_text) { // Set the comments for the codestream kdu_codestream_comment comment = codestream.add_comment(); comment.put_text(comment_text); } if (num_components >= 3) { // Note that we always use YCC and not YUV // *TODO: Verify this doesn't screws up reversible textures (like sculpties) as YCC is not reversible but YUV is... set_default_colour_weights(codestream.access_siz()); } // Set codestream options int nb_layers = 0; kdu_long layer_bytes[MAX_NB_LAYERS]; U32 max_bytes = (U32)(base.getWidth() * base.getHeight() * base.getComponents()); // Rate is the argument passed into the LLImageJ2C which specifies the target compression rate. The default is 8:1. // *TODO: mRate is actually always 8:1 in the viewer. Test different values. llassert (base.mRate > 0.f); max_bytes = (U32)((F32)(max_bytes) * base.mRate); // This code is where we specify the target number of bytes for each quality layer. // We're using a logarithmic spacing rule that fits with our way of fetching texture data. // Note: For more info on this layers business, read kdu_codestream::flush() doc in kdu_compressed.h layer_bytes[nb_layers++] = FIRST_PACKET_SIZE; U32 i = MIN_LAYER_SIZE; while ((i < max_bytes) && (nb_layers < (MAX_NB_LAYERS-1))) { layer_bytes[nb_layers++] = i; i *= 4; } // Note: for small images, we can have (max_bytes < FIRST_PACKET_SIZE), hence the test if (layer_bytes[nb_layers-1] < max_bytes) { // Set the last quality layer so to fit the preset compression ratio layer_bytes[nb_layers++] = max_bytes; } if (reversible) { // Use 0 for a last quality layer for reversible images so all remaining code blocks will be flushed // Hack: KDU encoding for reversible images has a bug for small images that leads to j2c images that // cannot be open or are very blurry. Avoiding that last layer prevents the problem to happen. if ((base.getWidth() >= 32) || (base.getHeight() >= 32)) { layer_bytes[nb_layers++] = 0; } codestream.access_siz()->parse_string("Creversible=yes"); // *TODO: we should use yuv in reversible mode // Don't turn this on now though as it creates problems on decoding for the moment //codestream.access_siz()->parse_string("Cycc=no"); } std::string layer_string = llformat("Clayers=%d",nb_layers); codestream.access_siz()->parse_string(layer_string.c_str()); // Set up data ordering, markers, etc... if precincts or blocks specified if ((mBlocksSize != -1) || (mPrecinctsSize != -1)) { if (mPrecinctsSize != -1) { std::string precincts_string = llformat("Cprecincts={%d,%d}",mPrecinctsSize,mPrecinctsSize); codestream.access_siz()->parse_string(precincts_string.c_str()); } if (mBlocksSize != -1) { std::string blocks_string = llformat("Cblk={%d,%d}",mBlocksSize,mBlocksSize); codestream.access_siz()->parse_string(blocks_string.c_str()); } std::string ordering_string = llformat("Corder=LRCP"); codestream.access_siz()->parse_string(ordering_string.c_str()); std::string PLT_string = llformat("ORGgen_plt=yes"); codestream.access_siz()->parse_string(PLT_string.c_str()); std::string Parts_string = llformat("ORGtparts=R"); codestream.access_siz()->parse_string(Parts_string.c_str()); } // Set the number of wavelets subresolutions (aka levels) if (mLevels != 0) { std::string levels_string = llformat("Clevels=%d",mLevels); codestream.access_siz()->parse_string(levels_string.c_str()); } // Complete the encode settings codestream.access_siz()->finalize_all(); codestream.change_appearance(transpose,vflip,hflip); // Now we are ready for sample data processing kdc_flow_control *tile = new kdc_flow_control(&mem_in,codestream); bool done = false; while (!done) { // Process line by line if (tile->advance_components()) { tile->process_components(); } else { done = true; } } // Produce the compressed output codestream.flush(layer_bytes,nb_layers); // Cleanup delete tile; codestream.destroy(); // Now that we're done encoding, create the new data buffer for the compressed // image and stick it there. base.copyData(output_buffer, output_size); base.updateData(); // set width, height delete[] output_buffer; } catch(const char* msg) { base.setLastError(ll_safe_string(msg)); return FALSE; } catch( ... ) { base.setLastError( "Unknown J2C error" ); return FALSE; } return TRUE; }
BOOL LLImageJ2CKDU::encodeImpl(LLImageJ2C &base, const LLImageRaw &raw_image, const char* comment_text, F32 encode_time, BOOL reversible) { // Declare and set simple arguments bool transpose = false; bool vflip = true; bool hflip = false; try { // Set up input image files siz_params siz; // Should set rate someplace here LLKDUMemIn mem_in(raw_image.getData(), raw_image.getDataSize(), raw_image.getWidth(), raw_image.getHeight(), raw_image.getComponents(), &siz); base.setSize(raw_image.getWidth(), raw_image.getHeight(), raw_image.getComponents()); int num_components = raw_image.getComponents(); siz.set(Scomponents,0,0,num_components); siz.set(Sdims,0,0,base.getHeight()); // Height of first image component siz.set(Sdims,0,1,base.getWidth()); // Width of first image component siz.set(Sprecision,0,0,8); // Image samples have original bit-depth of 8 siz.set(Ssigned,0,0,false); // Image samples are originally unsigned kdu_params *siz_ref = &siz; siz_ref->finalize(); siz_params transformed_siz; // Use this one to construct code-stream transformed_siz.copy_from(&siz,-1,-1,-1,0,transpose,false,false); // Construct the `kdu_codestream' object and parse all remaining arguments U32 max_output_size = base.getWidth()*base.getHeight()*base.getComponents(); max_output_size = (max_output_size < 1000 ? 1000 : max_output_size); U8 *output_buffer = new U8[max_output_size]; U32 output_size = 0; // Address updated by LLKDUMemTarget to give the final compressed buffer size LLKDUMemTarget output(output_buffer, output_size, max_output_size); kdu_codestream codestream; codestream.create(&transformed_siz,&output); if (comment_text) { // Set the comments for the codestream kdu_codestream_comment comment = codestream.add_comment(); comment.put_text(comment_text); } // Set codestream options int num_layer_specs = 0; kdu_long layer_bytes[64]; U32 max_bytes = 0; if (num_components >= 3) { // Note that we always use YCC and not YUV // *TODO: Verify this doesn't screws up reversible textures (like sculpties) as YCC is not reversible but YUV is... set_default_colour_weights(codestream.access_siz()); } 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. // 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; } 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()); } } // Set up data ordering, markers, etc... if precincts or blocks specified if ((mBlocksSize != -1) || (mPrecinctsSize != -1)) { if (mPrecinctsSize != -1) { std::string precincts_string = llformat("Cprecincts={%d,%d}",mPrecinctsSize,mPrecinctsSize); codestream.access_siz()->parse_string(precincts_string.c_str()); } if (mBlocksSize != -1) { std::string blocks_string = llformat("Cblk={%d,%d}",mBlocksSize,mBlocksSize); codestream.access_siz()->parse_string(blocks_string.c_str()); } std::string ordering_string = llformat("Corder=RPCL"); codestream.access_siz()->parse_string(ordering_string.c_str()); std::string PLT_string = llformat("ORGgen_plt=yes"); codestream.access_siz()->parse_string(PLT_string.c_str()); std::string Parts_string = llformat("ORGtparts=R"); codestream.access_siz()->parse_string(Parts_string.c_str()); } if (mLevels != 0) { std::string levels_string = llformat("Clevels=%d",mLevels); codestream.access_siz()->parse_string(levels_string.c_str()); } codestream.access_siz()->finalize_all(); codestream.change_appearance(transpose,vflip,hflip); // Now we are ready for sample data processing. kdc_flow_control *tile = new kdc_flow_control(&mem_in,codestream); bool done = false; while (!done) { // Process line by line if (tile->advance_components()) { tile->process_components(); } else { done = true; } } // Produce the compressed output codestream.flush(layer_bytes,num_layer_specs); // Cleanup delete tile; codestream.destroy(); // Now that we're done encoding, create the new data buffer for the compressed // image and stick it there. base.copyData(output_buffer, output_size); base.updateData(); // set width, height delete[] output_buffer; } catch(const char* msg) { base.setLastError(ll_safe_string(msg)); return FALSE; } catch( ... ) { base.setLastError( "Unknown J2C error" ); return FALSE; } return TRUE; }