From 6de3defe7271441f6e55e1a0ed5e115a92a72606 Mon Sep 17 00:00:00 2001 From: volzhs Date: Fri, 17 Feb 2017 23:49:40 +0900 Subject: Update libwebp to 0.6.0 --- thirdparty/libwebp/dec/alpha.c | 232 --- thirdparty/libwebp/dec/alpha_dec.c | 232 +++ thirdparty/libwebp/dec/alphai.h | 54 - thirdparty/libwebp/dec/alphai_dec.h | 54 + thirdparty/libwebp/dec/buffer.c | 300 ---- thirdparty/libwebp/dec/buffer_dec.c | 300 ++++ thirdparty/libwebp/dec/common.h | 54 - thirdparty/libwebp/dec/common_dec.h | 54 + thirdparty/libwebp/dec/decode_vp8.h | 185 --- thirdparty/libwebp/dec/frame.c | 812 ---------- thirdparty/libwebp/dec/frame_dec.c | 812 ++++++++++ thirdparty/libwebp/dec/idec.c | 892 ---------- thirdparty/libwebp/dec/idec_dec.c | 892 ++++++++++ thirdparty/libwebp/dec/io.c | 624 ------- thirdparty/libwebp/dec/io_dec.c | 645 ++++++++ thirdparty/libwebp/dec/quant.c | 110 -- thirdparty/libwebp/dec/quant_dec.c | 110 ++ thirdparty/libwebp/dec/tree.c | 525 ------ thirdparty/libwebp/dec/tree_dec.c | 528 ++++++ thirdparty/libwebp/dec/vp8.c | 667 -------- thirdparty/libwebp/dec/vp8_dec.c | 721 +++++++++ thirdparty/libwebp/dec/vp8_dec.h | 185 +++ thirdparty/libwebp/dec/vp8i.h | 320 ---- thirdparty/libwebp/dec/vp8i_dec.h | 320 ++++ thirdparty/libwebp/dec/vp8l.c | 1660 ------------------- thirdparty/libwebp/dec/vp8l_dec.c | 1671 +++++++++++++++++++ thirdparty/libwebp/dec/vp8li.h | 135 -- thirdparty/libwebp/dec/vp8li_dec.h | 135 ++ thirdparty/libwebp/dec/webp.c | 846 ---------- thirdparty/libwebp/dec/webp_dec.c | 843 ++++++++++ thirdparty/libwebp/dec/webpi.h | 142 -- thirdparty/libwebp/dec/webpi_dec.h | 133 ++ thirdparty/libwebp/demux/demux.c | 5 +- thirdparty/libwebp/dsp/alpha_processing.c | 38 +- thirdparty/libwebp/dsp/alpha_processing_neon.c | 191 +++ thirdparty/libwebp/dsp/alpha_processing_sse2.c | 141 +- thirdparty/libwebp/dsp/common_sse2.h | 85 + thirdparty/libwebp/dsp/cost.c | 2 +- thirdparty/libwebp/dsp/cost_mips32.c | 2 +- thirdparty/libwebp/dsp/cost_mips_dsp_r2.c | 2 +- thirdparty/libwebp/dsp/cost_sse2.c | 4 +- thirdparty/libwebp/dsp/cpu.c | 45 +- thirdparty/libwebp/dsp/dec.c | 2 +- thirdparty/libwebp/dsp/dec_clip_tables.c | 8 +- thirdparty/libwebp/dsp/dec_msa.c | 847 ++++++++++ thirdparty/libwebp/dsp/dec_neon.c | 40 +- thirdparty/libwebp/dsp/dec_sse2.c | 4 +- thirdparty/libwebp/dsp/dec_sse41.c | 2 +- thirdparty/libwebp/dsp/dsp.h | 57 +- thirdparty/libwebp/dsp/enc.c | 178 +- thirdparty/libwebp/dsp/enc_mips32.c | 4 +- thirdparty/libwebp/dsp/enc_mips_dsp_r2.c | 4 +- thirdparty/libwebp/dsp/enc_msa.c | 892 ++++++++++ thirdparty/libwebp/dsp/enc_neon.c | 42 +- thirdparty/libwebp/dsp/enc_sse2.c | 182 ++- thirdparty/libwebp/dsp/enc_sse41.c | 2 +- thirdparty/libwebp/dsp/filters.c | 12 + thirdparty/libwebp/dsp/filters_msa.c | 202 +++ thirdparty/libwebp/dsp/filters_neon.c | 327 ++++ thirdparty/libwebp/dsp/lossless.c | 186 ++- thirdparty/libwebp/dsp/lossless.h | 225 +-- thirdparty/libwebp/dsp/lossless_common.h | 210 +++ thirdparty/libwebp/dsp/lossless_enc.c | 953 +++-------- thirdparty/libwebp/dsp/lossless_enc_mips32.c | 47 +- thirdparty/libwebp/dsp/lossless_enc_msa.c | 147 ++ thirdparty/libwebp/dsp/lossless_enc_sse2.c | 320 +++- thirdparty/libwebp/dsp/lossless_enc_sse41.c | 4 +- thirdparty/libwebp/dsp/lossless_mips_dsp_r2.c | 79 +- thirdparty/libwebp/dsp/lossless_msa.c | 355 ++++ thirdparty/libwebp/dsp/lossless_neon.c | 395 ++++- thirdparty/libwebp/dsp/lossless_sse2.c | 389 ++++- thirdparty/libwebp/dsp/msa_macro.h | 1097 +++++++++++-- thirdparty/libwebp/dsp/neon.h | 18 + thirdparty/libwebp/dsp/rescaler.c | 8 +- thirdparty/libwebp/dsp/rescaler_mips32.c | 2 +- thirdparty/libwebp/dsp/rescaler_mips_dsp_r2.c | 2 +- thirdparty/libwebp/dsp/rescaler_msa.c | 444 +++++ thirdparty/libwebp/dsp/rescaler_neon.c | 2 +- thirdparty/libwebp/dsp/rescaler_sse2.c | 2 +- thirdparty/libwebp/dsp/upsampling.c | 6 + thirdparty/libwebp/dsp/upsampling_msa.c | 678 ++++++++ thirdparty/libwebp/dsp/upsampling_neon.c | 77 +- thirdparty/libwebp/dsp/yuv.c | 57 + thirdparty/libwebp/dsp/yuv.h | 2 +- thirdparty/libwebp/dsp/yuv_sse2.c | 304 ++-- thirdparty/libwebp/dsp/yuv_tables_sse2.h | 536 ------ thirdparty/libwebp/enc/alpha.c | 433 ----- thirdparty/libwebp/enc/alpha_enc.c | 433 +++++ thirdparty/libwebp/enc/analysis.c | 506 ------ thirdparty/libwebp/enc/analysis_enc.c | 533 ++++++ thirdparty/libwebp/enc/backward_references.c | 1715 -------------------- thirdparty/libwebp/enc/backward_references.h | 206 --- thirdparty/libwebp/enc/backward_references_enc.c | 1800 +++++++++++++++++++++ thirdparty/libwebp/enc/backward_references_enc.h | 207 +++ thirdparty/libwebp/enc/config.c | 173 -- thirdparty/libwebp/enc/config_enc.c | 152 ++ thirdparty/libwebp/enc/cost.c | 342 ---- thirdparty/libwebp/enc/cost.h | 82 - thirdparty/libwebp/enc/cost_enc.c | 342 ++++ thirdparty/libwebp/enc/cost_enc.h | 82 + thirdparty/libwebp/enc/delta_palettization.c | 455 ------ thirdparty/libwebp/enc/delta_palettization.h | 25 - thirdparty/libwebp/enc/delta_palettization_enc.c | 455 ++++++ thirdparty/libwebp/enc/delta_palettization_enc.h | 25 + thirdparty/libwebp/enc/filter.c | 306 ---- thirdparty/libwebp/enc/filter_enc.c | 219 +++ thirdparty/libwebp/enc/frame.c | 852 ---------- thirdparty/libwebp/enc/frame_enc.c | 854 ++++++++++ thirdparty/libwebp/enc/histogram.c | 938 ----------- thirdparty/libwebp/enc/histogram.h | 123 -- thirdparty/libwebp/enc/histogram_enc.c | 990 ++++++++++++ thirdparty/libwebp/enc/histogram_enc.h | 123 ++ thirdparty/libwebp/enc/iterator.c | 456 ------ thirdparty/libwebp/enc/iterator_enc.c | 453 ++++++ thirdparty/libwebp/enc/near_lossless.c | 122 -- thirdparty/libwebp/enc/near_lossless_enc.c | 122 ++ thirdparty/libwebp/enc/picture.c | 293 ---- thirdparty/libwebp/enc/picture_csp.c | 1192 -------------- thirdparty/libwebp/enc/picture_csp_enc.c | 1175 ++++++++++++++ thirdparty/libwebp/enc/picture_enc.c | 293 ++++ thirdparty/libwebp/enc/picture_psnr.c | 177 -- thirdparty/libwebp/enc/picture_psnr_enc.c | 213 +++ thirdparty/libwebp/enc/picture_rescale.c | 264 --- thirdparty/libwebp/enc/picture_rescale_enc.c | 264 +++ thirdparty/libwebp/enc/picture_tools.c | 226 --- thirdparty/libwebp/enc/picture_tools_enc.c | 226 +++ thirdparty/libwebp/enc/predictor_enc.c | 750 +++++++++ thirdparty/libwebp/enc/quant.c | 1284 --------------- thirdparty/libwebp/enc/quant_enc.c | 1283 +++++++++++++++ thirdparty/libwebp/enc/syntax.c | 383 ----- thirdparty/libwebp/enc/syntax_enc.c | 382 +++++ thirdparty/libwebp/enc/token.c | 293 ---- thirdparty/libwebp/enc/token_enc.c | 294 ++++ thirdparty/libwebp/enc/tree.c | 504 ------ thirdparty/libwebp/enc/tree_enc.c | 504 ++++++ thirdparty/libwebp/enc/vp8enci.h | 529 ------ thirdparty/libwebp/enc/vp8i_enc.h | 520 ++++++ thirdparty/libwebp/enc/vp8l.c | 1602 ------------------ thirdparty/libwebp/enc/vp8l_enc.c | 1667 +++++++++++++++++++ thirdparty/libwebp/enc/vp8li.h | 81 - thirdparty/libwebp/enc/vp8li_enc.h | 95 ++ thirdparty/libwebp/enc/webp_enc.c | 398 +++++ thirdparty/libwebp/enc/webpenc.c | 395 ----- thirdparty/libwebp/mux/anim_encode.c | 51 +- thirdparty/libwebp/mux/animi.h | 43 + thirdparty/libwebp/mux/muxedit.c | 157 +- thirdparty/libwebp/mux/muxi.h | 16 +- thirdparty/libwebp/mux/muxinternal.c | 21 +- thirdparty/libwebp/mux/muxread.c | 48 +- thirdparty/libwebp/utils/bit_reader.c | 229 --- thirdparty/libwebp/utils/bit_reader.h | 184 --- thirdparty/libwebp/utils/bit_reader_inl.h | 173 -- thirdparty/libwebp/utils/bit_reader_inl_utils.h | 190 +++ thirdparty/libwebp/utils/bit_reader_utils.c | 229 +++ thirdparty/libwebp/utils/bit_reader_utils.h | 184 +++ thirdparty/libwebp/utils/bit_writer.c | 319 ---- thirdparty/libwebp/utils/bit_writer.h | 145 -- thirdparty/libwebp/utils/bit_writer_utils.c | 319 ++++ thirdparty/libwebp/utils/bit_writer_utils.h | 146 ++ thirdparty/libwebp/utils/color_cache.c | 49 - thirdparty/libwebp/utils/color_cache.h | 80 - thirdparty/libwebp/utils/color_cache_utils.c | 49 + thirdparty/libwebp/utils/color_cache_utils.h | 85 + thirdparty/libwebp/utils/endian_inl.h | 100 -- thirdparty/libwebp/utils/endian_inl_utils.h | 100 ++ thirdparty/libwebp/utils/filters.c | 76 - thirdparty/libwebp/utils/filters.h | 32 - thirdparty/libwebp/utils/filters_utils.c | 76 + thirdparty/libwebp/utils/filters_utils.h | 32 + thirdparty/libwebp/utils/huffman.c | 205 --- thirdparty/libwebp/utils/huffman.h | 88 - thirdparty/libwebp/utils/huffman_encode.c | 417 ----- thirdparty/libwebp/utils/huffman_encode.h | 60 - thirdparty/libwebp/utils/huffman_encode_utils.c | 417 +++++ thirdparty/libwebp/utils/huffman_encode_utils.h | 60 + thirdparty/libwebp/utils/huffman_utils.c | 223 +++ thirdparty/libwebp/utils/huffman_utils.h | 88 + thirdparty/libwebp/utils/quant_levels.c | 140 -- thirdparty/libwebp/utils/quant_levels.h | 36 - thirdparty/libwebp/utils/quant_levels_dec.c | 284 ---- thirdparty/libwebp/utils/quant_levels_dec.h | 35 - thirdparty/libwebp/utils/quant_levels_dec_utils.c | 284 ++++ thirdparty/libwebp/utils/quant_levels_dec_utils.h | 35 + thirdparty/libwebp/utils/quant_levels_utils.c | 140 ++ thirdparty/libwebp/utils/quant_levels_utils.h | 36 + thirdparty/libwebp/utils/random.c | 43 - thirdparty/libwebp/utils/random.h | 63 - thirdparty/libwebp/utils/random_utils.c | 43 + thirdparty/libwebp/utils/random_utils.h | 63 + thirdparty/libwebp/utils/rescaler.c | 146 -- thirdparty/libwebp/utils/rescaler.h | 101 -- thirdparty/libwebp/utils/rescaler_utils.c | 146 ++ thirdparty/libwebp/utils/rescaler_utils.h | 101 ++ thirdparty/libwebp/utils/thread.c | 358 ---- thirdparty/libwebp/utils/thread.h | 93 -- thirdparty/libwebp/utils/thread_utils.c | 356 ++++ thirdparty/libwebp/utils/thread_utils.h | 93 ++ thirdparty/libwebp/utils/utils.c | 37 +- thirdparty/libwebp/utils/utils.h | 50 +- thirdparty/libwebp/webp/config.h | 150 -- thirdparty/libwebp/webp/encode.h | 35 +- thirdparty/libwebp/webp/format_constants.h | 3 +- thirdparty/libwebp/webp/mux.h | 50 +- thirdparty/libwebp/webp/mux_types.h | 5 +- 204 files changed, 33988 insertions(+), 27498 deletions(-) delete mode 100644 thirdparty/libwebp/dec/alpha.c create mode 100644 thirdparty/libwebp/dec/alpha_dec.c delete mode 100644 thirdparty/libwebp/dec/alphai.h create mode 100644 thirdparty/libwebp/dec/alphai_dec.h delete mode 100644 thirdparty/libwebp/dec/buffer.c create mode 100644 thirdparty/libwebp/dec/buffer_dec.c delete mode 100644 thirdparty/libwebp/dec/common.h create mode 100644 thirdparty/libwebp/dec/common_dec.h delete mode 100644 thirdparty/libwebp/dec/decode_vp8.h delete mode 100644 thirdparty/libwebp/dec/frame.c create mode 100644 thirdparty/libwebp/dec/frame_dec.c delete mode 100644 thirdparty/libwebp/dec/idec.c create mode 100644 thirdparty/libwebp/dec/idec_dec.c delete mode 100644 thirdparty/libwebp/dec/io.c create mode 100644 thirdparty/libwebp/dec/io_dec.c delete mode 100644 thirdparty/libwebp/dec/quant.c create mode 100644 thirdparty/libwebp/dec/quant_dec.c delete mode 100644 thirdparty/libwebp/dec/tree.c create mode 100644 thirdparty/libwebp/dec/tree_dec.c delete mode 100644 thirdparty/libwebp/dec/vp8.c create mode 100644 thirdparty/libwebp/dec/vp8_dec.c create mode 100644 thirdparty/libwebp/dec/vp8_dec.h delete mode 100644 thirdparty/libwebp/dec/vp8i.h create mode 100644 thirdparty/libwebp/dec/vp8i_dec.h delete mode 100644 thirdparty/libwebp/dec/vp8l.c create mode 100644 thirdparty/libwebp/dec/vp8l_dec.c delete mode 100644 thirdparty/libwebp/dec/vp8li.h create mode 100644 thirdparty/libwebp/dec/vp8li_dec.h delete mode 100644 thirdparty/libwebp/dec/webp.c create mode 100644 thirdparty/libwebp/dec/webp_dec.c delete mode 100644 thirdparty/libwebp/dec/webpi.h create mode 100644 thirdparty/libwebp/dec/webpi_dec.h create mode 100644 thirdparty/libwebp/dsp/alpha_processing_neon.c create mode 100644 thirdparty/libwebp/dsp/enc_msa.c create mode 100644 thirdparty/libwebp/dsp/filters_msa.c create mode 100644 thirdparty/libwebp/dsp/filters_neon.c create mode 100644 thirdparty/libwebp/dsp/lossless_common.h create mode 100644 thirdparty/libwebp/dsp/lossless_enc_msa.c create mode 100644 thirdparty/libwebp/dsp/lossless_msa.c create mode 100644 thirdparty/libwebp/dsp/rescaler_msa.c create mode 100644 thirdparty/libwebp/dsp/upsampling_msa.c delete mode 100644 thirdparty/libwebp/dsp/yuv_tables_sse2.h delete mode 100644 thirdparty/libwebp/enc/alpha.c create mode 100644 thirdparty/libwebp/enc/alpha_enc.c delete mode 100644 thirdparty/libwebp/enc/analysis.c create mode 100644 thirdparty/libwebp/enc/analysis_enc.c delete mode 100644 thirdparty/libwebp/enc/backward_references.c delete mode 100644 thirdparty/libwebp/enc/backward_references.h create mode 100644 thirdparty/libwebp/enc/backward_references_enc.c create mode 100644 thirdparty/libwebp/enc/backward_references_enc.h delete mode 100644 thirdparty/libwebp/enc/config.c create mode 100644 thirdparty/libwebp/enc/config_enc.c delete mode 100644 thirdparty/libwebp/enc/cost.c delete mode 100644 thirdparty/libwebp/enc/cost.h create mode 100644 thirdparty/libwebp/enc/cost_enc.c create mode 100644 thirdparty/libwebp/enc/cost_enc.h delete mode 100644 thirdparty/libwebp/enc/delta_palettization.c delete mode 100644 thirdparty/libwebp/enc/delta_palettization.h create mode 100644 thirdparty/libwebp/enc/delta_palettization_enc.c create mode 100644 thirdparty/libwebp/enc/delta_palettization_enc.h delete mode 100644 thirdparty/libwebp/enc/filter.c create mode 100644 thirdparty/libwebp/enc/filter_enc.c delete mode 100644 thirdparty/libwebp/enc/frame.c create mode 100644 thirdparty/libwebp/enc/frame_enc.c delete mode 100644 thirdparty/libwebp/enc/histogram.c delete mode 100644 thirdparty/libwebp/enc/histogram.h create mode 100644 thirdparty/libwebp/enc/histogram_enc.c create mode 100644 thirdparty/libwebp/enc/histogram_enc.h delete mode 100644 thirdparty/libwebp/enc/iterator.c create mode 100644 thirdparty/libwebp/enc/iterator_enc.c delete mode 100644 thirdparty/libwebp/enc/near_lossless.c create mode 100644 thirdparty/libwebp/enc/near_lossless_enc.c delete mode 100644 thirdparty/libwebp/enc/picture.c delete mode 100644 thirdparty/libwebp/enc/picture_csp.c create mode 100644 thirdparty/libwebp/enc/picture_csp_enc.c create mode 100644 thirdparty/libwebp/enc/picture_enc.c delete mode 100644 thirdparty/libwebp/enc/picture_psnr.c create mode 100644 thirdparty/libwebp/enc/picture_psnr_enc.c delete mode 100644 thirdparty/libwebp/enc/picture_rescale.c create mode 100644 thirdparty/libwebp/enc/picture_rescale_enc.c delete mode 100644 thirdparty/libwebp/enc/picture_tools.c create mode 100644 thirdparty/libwebp/enc/picture_tools_enc.c create mode 100644 thirdparty/libwebp/enc/predictor_enc.c delete mode 100644 thirdparty/libwebp/enc/quant.c create mode 100644 thirdparty/libwebp/enc/quant_enc.c delete mode 100644 thirdparty/libwebp/enc/syntax.c create mode 100644 thirdparty/libwebp/enc/syntax_enc.c delete mode 100644 thirdparty/libwebp/enc/token.c create mode 100644 thirdparty/libwebp/enc/token_enc.c delete mode 100644 thirdparty/libwebp/enc/tree.c create mode 100644 thirdparty/libwebp/enc/tree_enc.c delete mode 100644 thirdparty/libwebp/enc/vp8enci.h create mode 100644 thirdparty/libwebp/enc/vp8i_enc.h delete mode 100644 thirdparty/libwebp/enc/vp8l.c create mode 100644 thirdparty/libwebp/enc/vp8l_enc.c delete mode 100644 thirdparty/libwebp/enc/vp8li.h create mode 100644 thirdparty/libwebp/enc/vp8li_enc.h create mode 100644 thirdparty/libwebp/enc/webp_enc.c delete mode 100644 thirdparty/libwebp/enc/webpenc.c create mode 100644 thirdparty/libwebp/mux/animi.h delete mode 100644 thirdparty/libwebp/utils/bit_reader.c delete mode 100644 thirdparty/libwebp/utils/bit_reader.h delete mode 100644 thirdparty/libwebp/utils/bit_reader_inl.h create mode 100644 thirdparty/libwebp/utils/bit_reader_inl_utils.h create mode 100644 thirdparty/libwebp/utils/bit_reader_utils.c create mode 100644 thirdparty/libwebp/utils/bit_reader_utils.h delete mode 100644 thirdparty/libwebp/utils/bit_writer.c delete mode 100644 thirdparty/libwebp/utils/bit_writer.h create mode 100644 thirdparty/libwebp/utils/bit_writer_utils.c create mode 100644 thirdparty/libwebp/utils/bit_writer_utils.h delete mode 100644 thirdparty/libwebp/utils/color_cache.c delete mode 100644 thirdparty/libwebp/utils/color_cache.h create mode 100644 thirdparty/libwebp/utils/color_cache_utils.c create mode 100644 thirdparty/libwebp/utils/color_cache_utils.h delete mode 100644 thirdparty/libwebp/utils/endian_inl.h create mode 100644 thirdparty/libwebp/utils/endian_inl_utils.h delete mode 100644 thirdparty/libwebp/utils/filters.c delete mode 100644 thirdparty/libwebp/utils/filters.h create mode 100644 thirdparty/libwebp/utils/filters_utils.c create mode 100644 thirdparty/libwebp/utils/filters_utils.h delete mode 100644 thirdparty/libwebp/utils/huffman.c delete mode 100644 thirdparty/libwebp/utils/huffman.h delete mode 100644 thirdparty/libwebp/utils/huffman_encode.c delete mode 100644 thirdparty/libwebp/utils/huffman_encode.h create mode 100644 thirdparty/libwebp/utils/huffman_encode_utils.c create mode 100644 thirdparty/libwebp/utils/huffman_encode_utils.h create mode 100644 thirdparty/libwebp/utils/huffman_utils.c create mode 100644 thirdparty/libwebp/utils/huffman_utils.h delete mode 100644 thirdparty/libwebp/utils/quant_levels.c delete mode 100644 thirdparty/libwebp/utils/quant_levels.h delete mode 100644 thirdparty/libwebp/utils/quant_levels_dec.c delete mode 100644 thirdparty/libwebp/utils/quant_levels_dec.h create mode 100644 thirdparty/libwebp/utils/quant_levels_dec_utils.c create mode 100644 thirdparty/libwebp/utils/quant_levels_dec_utils.h create mode 100644 thirdparty/libwebp/utils/quant_levels_utils.c create mode 100644 thirdparty/libwebp/utils/quant_levels_utils.h delete mode 100644 thirdparty/libwebp/utils/random.c delete mode 100644 thirdparty/libwebp/utils/random.h create mode 100644 thirdparty/libwebp/utils/random_utils.c create mode 100644 thirdparty/libwebp/utils/random_utils.h delete mode 100644 thirdparty/libwebp/utils/rescaler.c delete mode 100644 thirdparty/libwebp/utils/rescaler.h create mode 100644 thirdparty/libwebp/utils/rescaler_utils.c create mode 100644 thirdparty/libwebp/utils/rescaler_utils.h delete mode 100644 thirdparty/libwebp/utils/thread.c delete mode 100644 thirdparty/libwebp/utils/thread.h create mode 100644 thirdparty/libwebp/utils/thread_utils.c create mode 100644 thirdparty/libwebp/utils/thread_utils.h delete mode 100644 thirdparty/libwebp/webp/config.h (limited to 'thirdparty/libwebp') diff --git a/thirdparty/libwebp/dec/alpha.c b/thirdparty/libwebp/dec/alpha.c deleted file mode 100644 index d88f01d8de..0000000000 --- a/thirdparty/libwebp/dec/alpha.c +++ /dev/null @@ -1,232 +0,0 @@ -// Copyright 2011 Google Inc. All Rights Reserved. -// -// Use of this source code is governed by a BSD-style license -// that can be found in the COPYING file in the root of the source -// tree. An additional intellectual property rights grant can be found -// in the file PATENTS. All contributing project authors may -// be found in the AUTHORS file in the root of the source tree. -// ----------------------------------------------------------------------------- -// -// Alpha-plane decompression. -// -// Author: Skal (pascal.massimino@gmail.com) - -#include -#include "./alphai.h" -#include "./vp8i.h" -#include "./vp8li.h" -#include "../dsp/dsp.h" -#include "../utils/quant_levels_dec.h" -#include "../utils/utils.h" -#include "../webp/format_constants.h" - -//------------------------------------------------------------------------------ -// ALPHDecoder object. - -// Allocates a new alpha decoder instance. -static ALPHDecoder* ALPHNew(void) { - ALPHDecoder* const dec = (ALPHDecoder*)WebPSafeCalloc(1ULL, sizeof(*dec)); - return dec; -} - -// Clears and deallocates an alpha decoder instance. -static void ALPHDelete(ALPHDecoder* const dec) { - if (dec != NULL) { - VP8LDelete(dec->vp8l_dec_); - dec->vp8l_dec_ = NULL; - WebPSafeFree(dec); - } -} - -//------------------------------------------------------------------------------ -// Decoding. - -// Initialize alpha decoding by parsing the alpha header and decoding the image -// header for alpha data stored using lossless compression. -// Returns false in case of error in alpha header (data too short, invalid -// compression method or filter, error in lossless header data etc). -static int ALPHInit(ALPHDecoder* const dec, const uint8_t* data, - size_t data_size, const VP8Io* const src_io, - uint8_t* output) { - int ok = 0; - const uint8_t* const alpha_data = data + ALPHA_HEADER_LEN; - const size_t alpha_data_size = data_size - ALPHA_HEADER_LEN; - int rsrv; - VP8Io* const io = &dec->io_; - - assert(data != NULL && output != NULL && src_io != NULL); - - VP8FiltersInit(); - dec->output_ = output; - dec->width_ = src_io->width; - dec->height_ = src_io->height; - assert(dec->width_ > 0 && dec->height_ > 0); - - if (data_size <= ALPHA_HEADER_LEN) { - return 0; - } - - dec->method_ = (data[0] >> 0) & 0x03; - dec->filter_ = (WEBP_FILTER_TYPE)((data[0] >> 2) & 0x03); - dec->pre_processing_ = (data[0] >> 4) & 0x03; - rsrv = (data[0] >> 6) & 0x03; - if (dec->method_ < ALPHA_NO_COMPRESSION || - dec->method_ > ALPHA_LOSSLESS_COMPRESSION || - dec->filter_ >= WEBP_FILTER_LAST || - dec->pre_processing_ > ALPHA_PREPROCESSED_LEVELS || - rsrv != 0) { - return 0; - } - - // Copy the necessary parameters from src_io to io - VP8InitIo(io); - WebPInitCustomIo(NULL, io); - io->opaque = dec; - io->width = src_io->width; - io->height = src_io->height; - - io->use_cropping = src_io->use_cropping; - io->crop_left = src_io->crop_left; - io->crop_right = src_io->crop_right; - io->crop_top = src_io->crop_top; - io->crop_bottom = src_io->crop_bottom; - // No need to copy the scaling parameters. - - if (dec->method_ == ALPHA_NO_COMPRESSION) { - const size_t alpha_decoded_size = dec->width_ * dec->height_; - ok = (alpha_data_size >= alpha_decoded_size); - } else { - assert(dec->method_ == ALPHA_LOSSLESS_COMPRESSION); - ok = VP8LDecodeAlphaHeader(dec, alpha_data, alpha_data_size); - } - - return ok; -} - -// Decodes, unfilters and dequantizes *at least* 'num_rows' rows of alpha -// starting from row number 'row'. It assumes that rows up to (row - 1) have -// already been decoded. -// Returns false in case of bitstream error. -static int ALPHDecode(VP8Decoder* const dec, int row, int num_rows) { - ALPHDecoder* const alph_dec = dec->alph_dec_; - const int width = alph_dec->width_; - const int height = alph_dec->io_.crop_bottom; - if (alph_dec->method_ == ALPHA_NO_COMPRESSION) { - int y; - const uint8_t* prev_line = dec->alpha_prev_line_; - const uint8_t* deltas = dec->alpha_data_ + ALPHA_HEADER_LEN + row * width; - uint8_t* dst = dec->alpha_plane_ + row * width; - assert(deltas <= &dec->alpha_data_[dec->alpha_data_size_]); - if (alph_dec->filter_ != WEBP_FILTER_NONE) { - assert(WebPUnfilters[alph_dec->filter_] != NULL); - for (y = 0; y < num_rows; ++y) { - WebPUnfilters[alph_dec->filter_](prev_line, deltas, dst, width); - prev_line = dst; - dst += width; - deltas += width; - } - } else { - for (y = 0; y < num_rows; ++y) { - memcpy(dst, deltas, width * sizeof(*dst)); - prev_line = dst; - dst += width; - deltas += width; - } - } - dec->alpha_prev_line_ = prev_line; - } else { // alph_dec->method_ == ALPHA_LOSSLESS_COMPRESSION - assert(alph_dec->vp8l_dec_ != NULL); - if (!VP8LDecodeAlphaImageStream(alph_dec, row + num_rows)) { - return 0; - } - } - - if (row + num_rows >= height) { - dec->is_alpha_decoded_ = 1; - } - return 1; -} - -static int AllocateAlphaPlane(VP8Decoder* const dec, const VP8Io* const io) { - const int stride = io->width; - const int height = io->crop_bottom; - const uint64_t alpha_size = (uint64_t)stride * height; - assert(dec->alpha_plane_mem_ == NULL); - dec->alpha_plane_mem_ = - (uint8_t*)WebPSafeMalloc(alpha_size, sizeof(*dec->alpha_plane_)); - if (dec->alpha_plane_mem_ == NULL) { - return 0; - } - dec->alpha_plane_ = dec->alpha_plane_mem_; - dec->alpha_prev_line_ = NULL; - return 1; -} - -void WebPDeallocateAlphaMemory(VP8Decoder* const dec) { - assert(dec != NULL); - WebPSafeFree(dec->alpha_plane_mem_); - dec->alpha_plane_mem_ = NULL; - dec->alpha_plane_ = NULL; - ALPHDelete(dec->alph_dec_); - dec->alph_dec_ = NULL; -} - -//------------------------------------------------------------------------------ -// Main entry point. - -const uint8_t* VP8DecompressAlphaRows(VP8Decoder* const dec, - const VP8Io* const io, - int row, int num_rows) { - const int width = io->width; - const int height = io->crop_bottom; - - assert(dec != NULL && io != NULL); - - if (row < 0 || num_rows <= 0 || row + num_rows > height) { - return NULL; // sanity check. - } - - if (!dec->is_alpha_decoded_) { - if (dec->alph_dec_ == NULL) { // Initialize decoder. - dec->alph_dec_ = ALPHNew(); - if (dec->alph_dec_ == NULL) return NULL; - if (!AllocateAlphaPlane(dec, io)) goto Error; - if (!ALPHInit(dec->alph_dec_, dec->alpha_data_, dec->alpha_data_size_, - io, dec->alpha_plane_)) { - goto Error; - } - // if we allowed use of alpha dithering, check whether it's needed at all - if (dec->alph_dec_->pre_processing_ != ALPHA_PREPROCESSED_LEVELS) { - dec->alpha_dithering_ = 0; // disable dithering - } else { - num_rows = height - row; // decode everything in one pass - } - } - - assert(dec->alph_dec_ != NULL); - assert(row + num_rows <= height); - if (!ALPHDecode(dec, row, num_rows)) goto Error; - - if (dec->is_alpha_decoded_) { // finished? - ALPHDelete(dec->alph_dec_); - dec->alph_dec_ = NULL; - if (dec->alpha_dithering_ > 0) { - uint8_t* const alpha = dec->alpha_plane_ + io->crop_top * width - + io->crop_left; - if (!WebPDequantizeLevels(alpha, - io->crop_right - io->crop_left, - io->crop_bottom - io->crop_top, - width, dec->alpha_dithering_)) { - goto Error; - } - } - } - } - - // Return a pointer to the current decoded row. - return dec->alpha_plane_ + row * width; - - Error: - WebPDeallocateAlphaMemory(dec); - return NULL; -} diff --git a/thirdparty/libwebp/dec/alpha_dec.c b/thirdparty/libwebp/dec/alpha_dec.c new file mode 100644 index 0000000000..83ffd4b609 --- /dev/null +++ b/thirdparty/libwebp/dec/alpha_dec.c @@ -0,0 +1,232 @@ +// Copyright 2011 Google Inc. All Rights Reserved. +// +// Use of this source code is governed by a BSD-style license +// that can be found in the COPYING file in the root of the source +// tree. An additional intellectual property rights grant can be found +// in the file PATENTS. All contributing project authors may +// be found in the AUTHORS file in the root of the source tree. +// ----------------------------------------------------------------------------- +// +// Alpha-plane decompression. +// +// Author: Skal (pascal.massimino@gmail.com) + +#include +#include "./alphai_dec.h" +#include "./vp8i_dec.h" +#include "./vp8li_dec.h" +#include "../dsp/dsp.h" +#include "../utils/quant_levels_dec_utils.h" +#include "../utils/utils.h" +#include "../webp/format_constants.h" + +//------------------------------------------------------------------------------ +// ALPHDecoder object. + +// Allocates a new alpha decoder instance. +static ALPHDecoder* ALPHNew(void) { + ALPHDecoder* const dec = (ALPHDecoder*)WebPSafeCalloc(1ULL, sizeof(*dec)); + return dec; +} + +// Clears and deallocates an alpha decoder instance. +static void ALPHDelete(ALPHDecoder* const dec) { + if (dec != NULL) { + VP8LDelete(dec->vp8l_dec_); + dec->vp8l_dec_ = NULL; + WebPSafeFree(dec); + } +} + +//------------------------------------------------------------------------------ +// Decoding. + +// Initialize alpha decoding by parsing the alpha header and decoding the image +// header for alpha data stored using lossless compression. +// Returns false in case of error in alpha header (data too short, invalid +// compression method or filter, error in lossless header data etc). +static int ALPHInit(ALPHDecoder* const dec, const uint8_t* data, + size_t data_size, const VP8Io* const src_io, + uint8_t* output) { + int ok = 0; + const uint8_t* const alpha_data = data + ALPHA_HEADER_LEN; + const size_t alpha_data_size = data_size - ALPHA_HEADER_LEN; + int rsrv; + VP8Io* const io = &dec->io_; + + assert(data != NULL && output != NULL && src_io != NULL); + + VP8FiltersInit(); + dec->output_ = output; + dec->width_ = src_io->width; + dec->height_ = src_io->height; + assert(dec->width_ > 0 && dec->height_ > 0); + + if (data_size <= ALPHA_HEADER_LEN) { + return 0; + } + + dec->method_ = (data[0] >> 0) & 0x03; + dec->filter_ = (WEBP_FILTER_TYPE)((data[0] >> 2) & 0x03); + dec->pre_processing_ = (data[0] >> 4) & 0x03; + rsrv = (data[0] >> 6) & 0x03; + if (dec->method_ < ALPHA_NO_COMPRESSION || + dec->method_ > ALPHA_LOSSLESS_COMPRESSION || + dec->filter_ >= WEBP_FILTER_LAST || + dec->pre_processing_ > ALPHA_PREPROCESSED_LEVELS || + rsrv != 0) { + return 0; + } + + // Copy the necessary parameters from src_io to io + VP8InitIo(io); + WebPInitCustomIo(NULL, io); + io->opaque = dec; + io->width = src_io->width; + io->height = src_io->height; + + io->use_cropping = src_io->use_cropping; + io->crop_left = src_io->crop_left; + io->crop_right = src_io->crop_right; + io->crop_top = src_io->crop_top; + io->crop_bottom = src_io->crop_bottom; + // No need to copy the scaling parameters. + + if (dec->method_ == ALPHA_NO_COMPRESSION) { + const size_t alpha_decoded_size = dec->width_ * dec->height_; + ok = (alpha_data_size >= alpha_decoded_size); + } else { + assert(dec->method_ == ALPHA_LOSSLESS_COMPRESSION); + ok = VP8LDecodeAlphaHeader(dec, alpha_data, alpha_data_size); + } + + return ok; +} + +// Decodes, unfilters and dequantizes *at least* 'num_rows' rows of alpha +// starting from row number 'row'. It assumes that rows up to (row - 1) have +// already been decoded. +// Returns false in case of bitstream error. +static int ALPHDecode(VP8Decoder* const dec, int row, int num_rows) { + ALPHDecoder* const alph_dec = dec->alph_dec_; + const int width = alph_dec->width_; + const int height = alph_dec->io_.crop_bottom; + if (alph_dec->method_ == ALPHA_NO_COMPRESSION) { + int y; + const uint8_t* prev_line = dec->alpha_prev_line_; + const uint8_t* deltas = dec->alpha_data_ + ALPHA_HEADER_LEN + row * width; + uint8_t* dst = dec->alpha_plane_ + row * width; + assert(deltas <= &dec->alpha_data_[dec->alpha_data_size_]); + if (alph_dec->filter_ != WEBP_FILTER_NONE) { + assert(WebPUnfilters[alph_dec->filter_] != NULL); + for (y = 0; y < num_rows; ++y) { + WebPUnfilters[alph_dec->filter_](prev_line, deltas, dst, width); + prev_line = dst; + dst += width; + deltas += width; + } + } else { + for (y = 0; y < num_rows; ++y) { + memcpy(dst, deltas, width * sizeof(*dst)); + prev_line = dst; + dst += width; + deltas += width; + } + } + dec->alpha_prev_line_ = prev_line; + } else { // alph_dec->method_ == ALPHA_LOSSLESS_COMPRESSION + assert(alph_dec->vp8l_dec_ != NULL); + if (!VP8LDecodeAlphaImageStream(alph_dec, row + num_rows)) { + return 0; + } + } + + if (row + num_rows >= height) { + dec->is_alpha_decoded_ = 1; + } + return 1; +} + +static int AllocateAlphaPlane(VP8Decoder* const dec, const VP8Io* const io) { + const int stride = io->width; + const int height = io->crop_bottom; + const uint64_t alpha_size = (uint64_t)stride * height; + assert(dec->alpha_plane_mem_ == NULL); + dec->alpha_plane_mem_ = + (uint8_t*)WebPSafeMalloc(alpha_size, sizeof(*dec->alpha_plane_)); + if (dec->alpha_plane_mem_ == NULL) { + return 0; + } + dec->alpha_plane_ = dec->alpha_plane_mem_; + dec->alpha_prev_line_ = NULL; + return 1; +} + +void WebPDeallocateAlphaMemory(VP8Decoder* const dec) { + assert(dec != NULL); + WebPSafeFree(dec->alpha_plane_mem_); + dec->alpha_plane_mem_ = NULL; + dec->alpha_plane_ = NULL; + ALPHDelete(dec->alph_dec_); + dec->alph_dec_ = NULL; +} + +//------------------------------------------------------------------------------ +// Main entry point. + +const uint8_t* VP8DecompressAlphaRows(VP8Decoder* const dec, + const VP8Io* const io, + int row, int num_rows) { + const int width = io->width; + const int height = io->crop_bottom; + + assert(dec != NULL && io != NULL); + + if (row < 0 || num_rows <= 0 || row + num_rows > height) { + return NULL; // sanity check. + } + + if (!dec->is_alpha_decoded_) { + if (dec->alph_dec_ == NULL) { // Initialize decoder. + dec->alph_dec_ = ALPHNew(); + if (dec->alph_dec_ == NULL) return NULL; + if (!AllocateAlphaPlane(dec, io)) goto Error; + if (!ALPHInit(dec->alph_dec_, dec->alpha_data_, dec->alpha_data_size_, + io, dec->alpha_plane_)) { + goto Error; + } + // if we allowed use of alpha dithering, check whether it's needed at all + if (dec->alph_dec_->pre_processing_ != ALPHA_PREPROCESSED_LEVELS) { + dec->alpha_dithering_ = 0; // disable dithering + } else { + num_rows = height - row; // decode everything in one pass + } + } + + assert(dec->alph_dec_ != NULL); + assert(row + num_rows <= height); + if (!ALPHDecode(dec, row, num_rows)) goto Error; + + if (dec->is_alpha_decoded_) { // finished? + ALPHDelete(dec->alph_dec_); + dec->alph_dec_ = NULL; + if (dec->alpha_dithering_ > 0) { + uint8_t* const alpha = dec->alpha_plane_ + io->crop_top * width + + io->crop_left; + if (!WebPDequantizeLevels(alpha, + io->crop_right - io->crop_left, + io->crop_bottom - io->crop_top, + width, dec->alpha_dithering_)) { + goto Error; + } + } + } + } + + // Return a pointer to the current decoded row. + return dec->alpha_plane_ + row * width; + + Error: + WebPDeallocateAlphaMemory(dec); + return NULL; +} diff --git a/thirdparty/libwebp/dec/alphai.h b/thirdparty/libwebp/dec/alphai.h deleted file mode 100644 index 69dd7c0f5d..0000000000 --- a/thirdparty/libwebp/dec/alphai.h +++ /dev/null @@ -1,54 +0,0 @@ -// Copyright 2013 Google Inc. All Rights Reserved. -// -// Use of this source code is governed by a BSD-style license -// that can be found in the COPYING file in the root of the source -// tree. An additional intellectual property rights grant can be found -// in the file PATENTS. All contributing project authors may -// be found in the AUTHORS file in the root of the source tree. -// ----------------------------------------------------------------------------- -// -// Alpha decoder: internal header. -// -// Author: Urvang (urvang@google.com) - -#ifndef WEBP_DEC_ALPHAI_H_ -#define WEBP_DEC_ALPHAI_H_ - -#include "./webpi.h" -#include "../utils/filters.h" - -#ifdef __cplusplus -extern "C" { -#endif - -struct VP8LDecoder; // Defined in dec/vp8li.h. - -typedef struct ALPHDecoder ALPHDecoder; -struct ALPHDecoder { - int width_; - int height_; - int method_; - WEBP_FILTER_TYPE filter_; - int pre_processing_; - struct VP8LDecoder* vp8l_dec_; - VP8Io io_; - int use_8b_decode_; // Although alpha channel requires only 1 byte per - // pixel, sometimes VP8LDecoder may need to allocate - // 4 bytes per pixel internally during decode. - uint8_t* output_; - const uint8_t* prev_line_; // last output row (or NULL) -}; - -//------------------------------------------------------------------------------ -// internal functions. Not public. - -// Deallocate memory associated to dec->alpha_plane_ decoding -void WebPDeallocateAlphaMemory(VP8Decoder* const dec); - -//------------------------------------------------------------------------------ - -#ifdef __cplusplus -} // extern "C" -#endif - -#endif /* WEBP_DEC_ALPHAI_H_ */ diff --git a/thirdparty/libwebp/dec/alphai_dec.h b/thirdparty/libwebp/dec/alphai_dec.h new file mode 100644 index 0000000000..561e8151ee --- /dev/null +++ b/thirdparty/libwebp/dec/alphai_dec.h @@ -0,0 +1,54 @@ +// Copyright 2013 Google Inc. All Rights Reserved. +// +// Use of this source code is governed by a BSD-style license +// that can be found in the COPYING file in the root of the source +// tree. An additional intellectual property rights grant can be found +// in the file PATENTS. All contributing project authors may +// be found in the AUTHORS file in the root of the source tree. +// ----------------------------------------------------------------------------- +// +// Alpha decoder: internal header. +// +// Author: Urvang (urvang@google.com) + +#ifndef WEBP_DEC_ALPHAI_H_ +#define WEBP_DEC_ALPHAI_H_ + +#include "./webpi_dec.h" +#include "../utils/filters_utils.h" + +#ifdef __cplusplus +extern "C" { +#endif + +struct VP8LDecoder; // Defined in dec/vp8li.h. + +typedef struct ALPHDecoder ALPHDecoder; +struct ALPHDecoder { + int width_; + int height_; + int method_; + WEBP_FILTER_TYPE filter_; + int pre_processing_; + struct VP8LDecoder* vp8l_dec_; + VP8Io io_; + int use_8b_decode_; // Although alpha channel requires only 1 byte per + // pixel, sometimes VP8LDecoder may need to allocate + // 4 bytes per pixel internally during decode. + uint8_t* output_; + const uint8_t* prev_line_; // last output row (or NULL) +}; + +//------------------------------------------------------------------------------ +// internal functions. Not public. + +// Deallocate memory associated to dec->alpha_plane_ decoding +void WebPDeallocateAlphaMemory(VP8Decoder* const dec); + +//------------------------------------------------------------------------------ + +#ifdef __cplusplus +} // extern "C" +#endif + +#endif /* WEBP_DEC_ALPHAI_H_ */ diff --git a/thirdparty/libwebp/dec/buffer.c b/thirdparty/libwebp/dec/buffer.c deleted file mode 100644 index 547e69b434..0000000000 --- a/thirdparty/libwebp/dec/buffer.c +++ /dev/null @@ -1,300 +0,0 @@ -// Copyright 2011 Google Inc. All Rights Reserved. -// -// Use of this source code is governed by a BSD-style license -// that can be found in the COPYING file in the root of the source -// tree. An additional intellectual property rights grant can be found -// in the file PATENTS. All contributing project authors may -// be found in the AUTHORS file in the root of the source tree. -// ----------------------------------------------------------------------------- -// -// Everything about WebPDecBuffer -// -// Author: Skal (pascal.massimino@gmail.com) - -#include - -#include "./vp8i.h" -#include "./webpi.h" -#include "../utils/utils.h" - -//------------------------------------------------------------------------------ -// WebPDecBuffer - -// Number of bytes per pixel for the different color-spaces. -static const int kModeBpp[MODE_LAST] = { - 3, 4, 3, 4, 4, 2, 2, - 4, 4, 4, 2, // pre-multiplied modes - 1, 1 }; - -// Check that webp_csp_mode is within the bounds of WEBP_CSP_MODE. -// Convert to an integer to handle both the unsigned/signed enum cases -// without the need for casting to remove type limit warnings. -static int IsValidColorspace(int webp_csp_mode) { - return (webp_csp_mode >= MODE_RGB && webp_csp_mode < MODE_LAST); -} - -// strictly speaking, the very last (or first, if flipped) row -// doesn't require padding. -#define MIN_BUFFER_SIZE(WIDTH, HEIGHT, STRIDE) \ - (uint64_t)(STRIDE) * ((HEIGHT) - 1) + (WIDTH) - -static VP8StatusCode CheckDecBuffer(const WebPDecBuffer* const buffer) { - int ok = 1; - const WEBP_CSP_MODE mode = buffer->colorspace; - const int width = buffer->width; - const int height = buffer->height; - if (!IsValidColorspace(mode)) { - ok = 0; - } else if (!WebPIsRGBMode(mode)) { // YUV checks - const WebPYUVABuffer* const buf = &buffer->u.YUVA; - const int uv_width = (width + 1) / 2; - const int uv_height = (height + 1) / 2; - const int y_stride = abs(buf->y_stride); - const int u_stride = abs(buf->u_stride); - const int v_stride = abs(buf->v_stride); - const int a_stride = abs(buf->a_stride); - const uint64_t y_size = MIN_BUFFER_SIZE(width, height, y_stride); - const uint64_t u_size = MIN_BUFFER_SIZE(uv_width, uv_height, u_stride); - const uint64_t v_size = MIN_BUFFER_SIZE(uv_width, uv_height, v_stride); - const uint64_t a_size = MIN_BUFFER_SIZE(width, height, a_stride); - ok &= (y_size <= buf->y_size); - ok &= (u_size <= buf->u_size); - ok &= (v_size <= buf->v_size); - ok &= (y_stride >= width); - ok &= (u_stride >= uv_width); - ok &= (v_stride >= uv_width); - ok &= (buf->y != NULL); - ok &= (buf->u != NULL); - ok &= (buf->v != NULL); - if (mode == MODE_YUVA) { - ok &= (a_stride >= width); - ok &= (a_size <= buf->a_size); - ok &= (buf->a != NULL); - } - } else { // RGB checks - const WebPRGBABuffer* const buf = &buffer->u.RGBA; - const int stride = abs(buf->stride); - const uint64_t size = MIN_BUFFER_SIZE(width, height, stride); - ok &= (size <= buf->size); - ok &= (stride >= width * kModeBpp[mode]); - ok &= (buf->rgba != NULL); - } - return ok ? VP8_STATUS_OK : VP8_STATUS_INVALID_PARAM; -} -#undef MIN_BUFFER_SIZE - -static VP8StatusCode AllocateBuffer(WebPDecBuffer* const buffer) { - const int w = buffer->width; - const int h = buffer->height; - const WEBP_CSP_MODE mode = buffer->colorspace; - - if (w <= 0 || h <= 0 || !IsValidColorspace(mode)) { - return VP8_STATUS_INVALID_PARAM; - } - - if (buffer->is_external_memory <= 0 && buffer->private_memory == NULL) { - uint8_t* output; - int uv_stride = 0, a_stride = 0; - uint64_t uv_size = 0, a_size = 0, total_size; - // We need memory and it hasn't been allocated yet. - // => initialize output buffer, now that dimensions are known. - const int stride = w * kModeBpp[mode]; - const uint64_t size = (uint64_t)stride * h; - - if (!WebPIsRGBMode(mode)) { - uv_stride = (w + 1) / 2; - uv_size = (uint64_t)uv_stride * ((h + 1) / 2); - if (mode == MODE_YUVA) { - a_stride = w; - a_size = (uint64_t)a_stride * h; - } - } - total_size = size + 2 * uv_size + a_size; - - // Security/sanity checks - output = (uint8_t*)WebPSafeMalloc(total_size, sizeof(*output)); - if (output == NULL) { - return VP8_STATUS_OUT_OF_MEMORY; - } - buffer->private_memory = output; - - if (!WebPIsRGBMode(mode)) { // YUVA initialization - WebPYUVABuffer* const buf = &buffer->u.YUVA; - buf->y = output; - buf->y_stride = stride; - buf->y_size = (size_t)size; - buf->u = output + size; - buf->u_stride = uv_stride; - buf->u_size = (size_t)uv_size; - buf->v = output + size + uv_size; - buf->v_stride = uv_stride; - buf->v_size = (size_t)uv_size; - if (mode == MODE_YUVA) { - buf->a = output + size + 2 * uv_size; - } - buf->a_size = (size_t)a_size; - buf->a_stride = a_stride; - } else { // RGBA initialization - WebPRGBABuffer* const buf = &buffer->u.RGBA; - buf->rgba = output; - buf->stride = stride; - buf->size = (size_t)size; - } - } - return CheckDecBuffer(buffer); -} - -VP8StatusCode WebPFlipBuffer(WebPDecBuffer* const buffer) { - if (buffer == NULL) { - return VP8_STATUS_INVALID_PARAM; - } - if (WebPIsRGBMode(buffer->colorspace)) { - WebPRGBABuffer* const buf = &buffer->u.RGBA; - buf->rgba += (buffer->height - 1) * buf->stride; - buf->stride = -buf->stride; - } else { - WebPYUVABuffer* const buf = &buffer->u.YUVA; - const int H = buffer->height; - buf->y += (H - 1) * buf->y_stride; - buf->y_stride = -buf->y_stride; - buf->u += ((H - 1) >> 1) * buf->u_stride; - buf->u_stride = -buf->u_stride; - buf->v += ((H - 1) >> 1) * buf->v_stride; - buf->v_stride = -buf->v_stride; - if (buf->a != NULL) { - buf->a += (H - 1) * buf->a_stride; - buf->a_stride = -buf->a_stride; - } - } - return VP8_STATUS_OK; -} - -VP8StatusCode WebPAllocateDecBuffer(int w, int h, - const WebPDecoderOptions* const options, - WebPDecBuffer* const out) { - VP8StatusCode status; - if (out == NULL || w <= 0 || h <= 0) { - return VP8_STATUS_INVALID_PARAM; - } - if (options != NULL) { // First, apply options if there is any. - if (options->use_cropping) { - const int cw = options->crop_width; - const int ch = options->crop_height; - const int x = options->crop_left & ~1; - const int y = options->crop_top & ~1; - if (x < 0 || y < 0 || cw <= 0 || ch <= 0 || x + cw > w || y + ch > h) { - return VP8_STATUS_INVALID_PARAM; // out of frame boundary. - } - w = cw; - h = ch; - } - if (options->use_scaling) { - int scaled_width = options->scaled_width; - int scaled_height = options->scaled_height; - if (!WebPRescalerGetScaledDimensions( - w, h, &scaled_width, &scaled_height)) { - return VP8_STATUS_INVALID_PARAM; - } - w = scaled_width; - h = scaled_height; - } - } - out->width = w; - out->height = h; - - // Then, allocate buffer for real. - status = AllocateBuffer(out); - if (status != VP8_STATUS_OK) return status; - - // Use the stride trick if vertical flip is needed. - if (options != NULL && options->flip) { - status = WebPFlipBuffer(out); - } - return status; -} - -//------------------------------------------------------------------------------ -// constructors / destructors - -int WebPInitDecBufferInternal(WebPDecBuffer* buffer, int version) { - if (WEBP_ABI_IS_INCOMPATIBLE(version, WEBP_DECODER_ABI_VERSION)) { - return 0; // version mismatch - } - if (buffer == NULL) return 0; - memset(buffer, 0, sizeof(*buffer)); - return 1; -} - -void WebPFreeDecBuffer(WebPDecBuffer* buffer) { - if (buffer != NULL) { - if (buffer->is_external_memory <= 0) { - WebPSafeFree(buffer->private_memory); - } - buffer->private_memory = NULL; - } -} - -void WebPCopyDecBuffer(const WebPDecBuffer* const src, - WebPDecBuffer* const dst) { - if (src != NULL && dst != NULL) { - *dst = *src; - if (src->private_memory != NULL) { - dst->is_external_memory = 1; // dst buffer doesn't own the memory. - dst->private_memory = NULL; - } - } -} - -// Copy and transfer ownership from src to dst (beware of parameter order!) -void WebPGrabDecBuffer(WebPDecBuffer* const src, WebPDecBuffer* const dst) { - if (src != NULL && dst != NULL) { - *dst = *src; - if (src->private_memory != NULL) { - src->is_external_memory = 1; // src relinquishes ownership - src->private_memory = NULL; - } - } -} - -VP8StatusCode WebPCopyDecBufferPixels(const WebPDecBuffer* const src_buf, - WebPDecBuffer* const dst_buf) { - assert(src_buf != NULL && dst_buf != NULL); - assert(src_buf->colorspace == dst_buf->colorspace); - - dst_buf->width = src_buf->width; - dst_buf->height = src_buf->height; - if (CheckDecBuffer(dst_buf) != VP8_STATUS_OK) { - return VP8_STATUS_INVALID_PARAM; - } - if (WebPIsRGBMode(src_buf->colorspace)) { - const WebPRGBABuffer* const src = &src_buf->u.RGBA; - const WebPRGBABuffer* const dst = &dst_buf->u.RGBA; - WebPCopyPlane(src->rgba, src->stride, dst->rgba, dst->stride, - src_buf->width * kModeBpp[src_buf->colorspace], - src_buf->height); - } else { - const WebPYUVABuffer* const src = &src_buf->u.YUVA; - const WebPYUVABuffer* const dst = &dst_buf->u.YUVA; - WebPCopyPlane(src->y, src->y_stride, dst->y, dst->y_stride, - src_buf->width, src_buf->height); - WebPCopyPlane(src->u, src->u_stride, dst->u, dst->u_stride, - (src_buf->width + 1) / 2, (src_buf->height + 1) / 2); - WebPCopyPlane(src->v, src->v_stride, dst->v, dst->v_stride, - (src_buf->width + 1) / 2, (src_buf->height + 1) / 2); - if (WebPIsAlphaMode(src_buf->colorspace)) { - WebPCopyPlane(src->a, src->a_stride, dst->a, dst->a_stride, - src_buf->width, src_buf->height); - } - } - return VP8_STATUS_OK; -} - -int WebPAvoidSlowMemory(const WebPDecBuffer* const output, - const WebPBitstreamFeatures* const features) { - assert(output != NULL); - return (output->is_external_memory >= 2) && - WebPIsPremultipliedMode(output->colorspace) && - (features != NULL && features->has_alpha); -} - -//------------------------------------------------------------------------------ diff --git a/thirdparty/libwebp/dec/buffer_dec.c b/thirdparty/libwebp/dec/buffer_dec.c new file mode 100644 index 0000000000..c685fd5646 --- /dev/null +++ b/thirdparty/libwebp/dec/buffer_dec.c @@ -0,0 +1,300 @@ +// Copyright 2011 Google Inc. All Rights Reserved. +// +// Use of this source code is governed by a BSD-style license +// that can be found in the COPYING file in the root of the source +// tree. An additional intellectual property rights grant can be found +// in the file PATENTS. All contributing project authors may +// be found in the AUTHORS file in the root of the source tree. +// ----------------------------------------------------------------------------- +// +// Everything about WebPDecBuffer +// +// Author: Skal (pascal.massimino@gmail.com) + +#include + +#include "./vp8i_dec.h" +#include "./webpi_dec.h" +#include "../utils/utils.h" + +//------------------------------------------------------------------------------ +// WebPDecBuffer + +// Number of bytes per pixel for the different color-spaces. +static const int kModeBpp[MODE_LAST] = { + 3, 4, 3, 4, 4, 2, 2, + 4, 4, 4, 2, // pre-multiplied modes + 1, 1 }; + +// Check that webp_csp_mode is within the bounds of WEBP_CSP_MODE. +// Convert to an integer to handle both the unsigned/signed enum cases +// without the need for casting to remove type limit warnings. +static int IsValidColorspace(int webp_csp_mode) { + return (webp_csp_mode >= MODE_RGB && webp_csp_mode < MODE_LAST); +} + +// strictly speaking, the very last (or first, if flipped) row +// doesn't require padding. +#define MIN_BUFFER_SIZE(WIDTH, HEIGHT, STRIDE) \ + (uint64_t)(STRIDE) * ((HEIGHT) - 1) + (WIDTH) + +static VP8StatusCode CheckDecBuffer(const WebPDecBuffer* const buffer) { + int ok = 1; + const WEBP_CSP_MODE mode = buffer->colorspace; + const int width = buffer->width; + const int height = buffer->height; + if (!IsValidColorspace(mode)) { + ok = 0; + } else if (!WebPIsRGBMode(mode)) { // YUV checks + const WebPYUVABuffer* const buf = &buffer->u.YUVA; + const int uv_width = (width + 1) / 2; + const int uv_height = (height + 1) / 2; + const int y_stride = abs(buf->y_stride); + const int u_stride = abs(buf->u_stride); + const int v_stride = abs(buf->v_stride); + const int a_stride = abs(buf->a_stride); + const uint64_t y_size = MIN_BUFFER_SIZE(width, height, y_stride); + const uint64_t u_size = MIN_BUFFER_SIZE(uv_width, uv_height, u_stride); + const uint64_t v_size = MIN_BUFFER_SIZE(uv_width, uv_height, v_stride); + const uint64_t a_size = MIN_BUFFER_SIZE(width, height, a_stride); + ok &= (y_size <= buf->y_size); + ok &= (u_size <= buf->u_size); + ok &= (v_size <= buf->v_size); + ok &= (y_stride >= width); + ok &= (u_stride >= uv_width); + ok &= (v_stride >= uv_width); + ok &= (buf->y != NULL); + ok &= (buf->u != NULL); + ok &= (buf->v != NULL); + if (mode == MODE_YUVA) { + ok &= (a_stride >= width); + ok &= (a_size <= buf->a_size); + ok &= (buf->a != NULL); + } + } else { // RGB checks + const WebPRGBABuffer* const buf = &buffer->u.RGBA; + const int stride = abs(buf->stride); + const uint64_t size = MIN_BUFFER_SIZE(width, height, stride); + ok &= (size <= buf->size); + ok &= (stride >= width * kModeBpp[mode]); + ok &= (buf->rgba != NULL); + } + return ok ? VP8_STATUS_OK : VP8_STATUS_INVALID_PARAM; +} +#undef MIN_BUFFER_SIZE + +static VP8StatusCode AllocateBuffer(WebPDecBuffer* const buffer) { + const int w = buffer->width; + const int h = buffer->height; + const WEBP_CSP_MODE mode = buffer->colorspace; + + if (w <= 0 || h <= 0 || !IsValidColorspace(mode)) { + return VP8_STATUS_INVALID_PARAM; + } + + if (buffer->is_external_memory <= 0 && buffer->private_memory == NULL) { + uint8_t* output; + int uv_stride = 0, a_stride = 0; + uint64_t uv_size = 0, a_size = 0, total_size; + // We need memory and it hasn't been allocated yet. + // => initialize output buffer, now that dimensions are known. + const int stride = w * kModeBpp[mode]; + const uint64_t size = (uint64_t)stride * h; + + if (!WebPIsRGBMode(mode)) { + uv_stride = (w + 1) / 2; + uv_size = (uint64_t)uv_stride * ((h + 1) / 2); + if (mode == MODE_YUVA) { + a_stride = w; + a_size = (uint64_t)a_stride * h; + } + } + total_size = size + 2 * uv_size + a_size; + + // Security/sanity checks + output = (uint8_t*)WebPSafeMalloc(total_size, sizeof(*output)); + if (output == NULL) { + return VP8_STATUS_OUT_OF_MEMORY; + } + buffer->private_memory = output; + + if (!WebPIsRGBMode(mode)) { // YUVA initialization + WebPYUVABuffer* const buf = &buffer->u.YUVA; + buf->y = output; + buf->y_stride = stride; + buf->y_size = (size_t)size; + buf->u = output + size; + buf->u_stride = uv_stride; + buf->u_size = (size_t)uv_size; + buf->v = output + size + uv_size; + buf->v_stride = uv_stride; + buf->v_size = (size_t)uv_size; + if (mode == MODE_YUVA) { + buf->a = output + size + 2 * uv_size; + } + buf->a_size = (size_t)a_size; + buf->a_stride = a_stride; + } else { // RGBA initialization + WebPRGBABuffer* const buf = &buffer->u.RGBA; + buf->rgba = output; + buf->stride = stride; + buf->size = (size_t)size; + } + } + return CheckDecBuffer(buffer); +} + +VP8StatusCode WebPFlipBuffer(WebPDecBuffer* const buffer) { + if (buffer == NULL) { + return VP8_STATUS_INVALID_PARAM; + } + if (WebPIsRGBMode(buffer->colorspace)) { + WebPRGBABuffer* const buf = &buffer->u.RGBA; + buf->rgba += (buffer->height - 1) * buf->stride; + buf->stride = -buf->stride; + } else { + WebPYUVABuffer* const buf = &buffer->u.YUVA; + const int H = buffer->height; + buf->y += (H - 1) * buf->y_stride; + buf->y_stride = -buf->y_stride; + buf->u += ((H - 1) >> 1) * buf->u_stride; + buf->u_stride = -buf->u_stride; + buf->v += ((H - 1) >> 1) * buf->v_stride; + buf->v_stride = -buf->v_stride; + if (buf->a != NULL) { + buf->a += (H - 1) * buf->a_stride; + buf->a_stride = -buf->a_stride; + } + } + return VP8_STATUS_OK; +} + +VP8StatusCode WebPAllocateDecBuffer(int w, int h, + const WebPDecoderOptions* const options, + WebPDecBuffer* const out) { + VP8StatusCode status; + if (out == NULL || w <= 0 || h <= 0) { + return VP8_STATUS_INVALID_PARAM; + } + if (options != NULL) { // First, apply options if there is any. + if (options->use_cropping) { + const int cw = options->crop_width; + const int ch = options->crop_height; + const int x = options->crop_left & ~1; + const int y = options->crop_top & ~1; + if (x < 0 || y < 0 || cw <= 0 || ch <= 0 || x + cw > w || y + ch > h) { + return VP8_STATUS_INVALID_PARAM; // out of frame boundary. + } + w = cw; + h = ch; + } + if (options->use_scaling) { + int scaled_width = options->scaled_width; + int scaled_height = options->scaled_height; + if (!WebPRescalerGetScaledDimensions( + w, h, &scaled_width, &scaled_height)) { + return VP8_STATUS_INVALID_PARAM; + } + w = scaled_width; + h = scaled_height; + } + } + out->width = w; + out->height = h; + + // Then, allocate buffer for real. + status = AllocateBuffer(out); + if (status != VP8_STATUS_OK) return status; + + // Use the stride trick if vertical flip is needed. + if (options != NULL && options->flip) { + status = WebPFlipBuffer(out); + } + return status; +} + +//------------------------------------------------------------------------------ +// constructors / destructors + +int WebPInitDecBufferInternal(WebPDecBuffer* buffer, int version) { + if (WEBP_ABI_IS_INCOMPATIBLE(version, WEBP_DECODER_ABI_VERSION)) { + return 0; // version mismatch + } + if (buffer == NULL) return 0; + memset(buffer, 0, sizeof(*buffer)); + return 1; +} + +void WebPFreeDecBuffer(WebPDecBuffer* buffer) { + if (buffer != NULL) { + if (buffer->is_external_memory <= 0) { + WebPSafeFree(buffer->private_memory); + } + buffer->private_memory = NULL; + } +} + +void WebPCopyDecBuffer(const WebPDecBuffer* const src, + WebPDecBuffer* const dst) { + if (src != NULL && dst != NULL) { + *dst = *src; + if (src->private_memory != NULL) { + dst->is_external_memory = 1; // dst buffer doesn't own the memory. + dst->private_memory = NULL; + } + } +} + +// Copy and transfer ownership from src to dst (beware of parameter order!) +void WebPGrabDecBuffer(WebPDecBuffer* const src, WebPDecBuffer* const dst) { + if (src != NULL && dst != NULL) { + *dst = *src; + if (src->private_memory != NULL) { + src->is_external_memory = 1; // src relinquishes ownership + src->private_memory = NULL; + } + } +} + +VP8StatusCode WebPCopyDecBufferPixels(const WebPDecBuffer* const src_buf, + WebPDecBuffer* const dst_buf) { + assert(src_buf != NULL && dst_buf != NULL); + assert(src_buf->colorspace == dst_buf->colorspace); + + dst_buf->width = src_buf->width; + dst_buf->height = src_buf->height; + if (CheckDecBuffer(dst_buf) != VP8_STATUS_OK) { + return VP8_STATUS_INVALID_PARAM; + } + if (WebPIsRGBMode(src_buf->colorspace)) { + const WebPRGBABuffer* const src = &src_buf->u.RGBA; + const WebPRGBABuffer* const dst = &dst_buf->u.RGBA; + WebPCopyPlane(src->rgba, src->stride, dst->rgba, dst->stride, + src_buf->width * kModeBpp[src_buf->colorspace], + src_buf->height); + } else { + const WebPYUVABuffer* const src = &src_buf->u.YUVA; + const WebPYUVABuffer* const dst = &dst_buf->u.YUVA; + WebPCopyPlane(src->y, src->y_stride, dst->y, dst->y_stride, + src_buf->width, src_buf->height); + WebPCopyPlane(src->u, src->u_stride, dst->u, dst->u_stride, + (src_buf->width + 1) / 2, (src_buf->height + 1) / 2); + WebPCopyPlane(src->v, src->v_stride, dst->v, dst->v_stride, + (src_buf->width + 1) / 2, (src_buf->height + 1) / 2); + if (WebPIsAlphaMode(src_buf->colorspace)) { + WebPCopyPlane(src->a, src->a_stride, dst->a, dst->a_stride, + src_buf->width, src_buf->height); + } + } + return VP8_STATUS_OK; +} + +int WebPAvoidSlowMemory(const WebPDecBuffer* const output, + const WebPBitstreamFeatures* const features) { + assert(output != NULL); + return (output->is_external_memory >= 2) && + WebPIsPremultipliedMode(output->colorspace) && + (features != NULL && features->has_alpha); +} + +//------------------------------------------------------------------------------ diff --git a/thirdparty/libwebp/dec/common.h b/thirdparty/libwebp/dec/common.h deleted file mode 100644 index 6961e22470..0000000000 --- a/thirdparty/libwebp/dec/common.h +++ /dev/null @@ -1,54 +0,0 @@ -// Copyright 2015 Google Inc. All Rights Reserved. -// -// Use of this source code is governed by a BSD-style license -// that can be found in the COPYING file in the root of the source -// tree. An additional intellectual property rights grant can be found -// in the file PATENTS. All contributing project authors may -// be found in the AUTHORS file in the root of the source tree. -// ----------------------------------------------------------------------------- -// -// Definitions and macros common to encoding and decoding -// -// Author: Skal (pascal.massimino@gmail.com) - -#ifndef WEBP_DEC_COMMON_H_ -#define WEBP_DEC_COMMON_H_ - -// intra prediction modes -enum { B_DC_PRED = 0, // 4x4 modes - B_TM_PRED = 1, - B_VE_PRED = 2, - B_HE_PRED = 3, - B_RD_PRED = 4, - B_VR_PRED = 5, - B_LD_PRED = 6, - B_VL_PRED = 7, - B_HD_PRED = 8, - B_HU_PRED = 9, - NUM_BMODES = B_HU_PRED + 1 - B_DC_PRED, // = 10 - - // Luma16 or UV modes - DC_PRED = B_DC_PRED, V_PRED = B_VE_PRED, - H_PRED = B_HE_PRED, TM_PRED = B_TM_PRED, - B_PRED = NUM_BMODES, // refined I4x4 mode - NUM_PRED_MODES = 4, - - // special modes - B_DC_PRED_NOTOP = 4, - B_DC_PRED_NOLEFT = 5, - B_DC_PRED_NOTOPLEFT = 6, - NUM_B_DC_MODES = 7 }; - -enum { MB_FEATURE_TREE_PROBS = 3, - NUM_MB_SEGMENTS = 4, - NUM_REF_LF_DELTAS = 4, - NUM_MODE_LF_DELTAS = 4, // I4x4, ZERO, *, SPLIT - MAX_NUM_PARTITIONS = 8, - // Probabilities - NUM_TYPES = 4, // 0: i16-AC, 1: i16-DC, 2:chroma-AC, 3:i4-AC - NUM_BANDS = 8, - NUM_CTX = 3, - NUM_PROBAS = 11 - }; - -#endif // WEBP_DEC_COMMON_H_ diff --git a/thirdparty/libwebp/dec/common_dec.h b/thirdparty/libwebp/dec/common_dec.h new file mode 100644 index 0000000000..6961e22470 --- /dev/null +++ b/thirdparty/libwebp/dec/common_dec.h @@ -0,0 +1,54 @@ +// Copyright 2015 Google Inc. All Rights Reserved. +// +// Use of this source code is governed by a BSD-style license +// that can be found in the COPYING file in the root of the source +// tree. An additional intellectual property rights grant can be found +// in the file PATENTS. All contributing project authors may +// be found in the AUTHORS file in the root of the source tree. +// ----------------------------------------------------------------------------- +// +// Definitions and macros common to encoding and decoding +// +// Author: Skal (pascal.massimino@gmail.com) + +#ifndef WEBP_DEC_COMMON_H_ +#define WEBP_DEC_COMMON_H_ + +// intra prediction modes +enum { B_DC_PRED = 0, // 4x4 modes + B_TM_PRED = 1, + B_VE_PRED = 2, + B_HE_PRED = 3, + B_RD_PRED = 4, + B_VR_PRED = 5, + B_LD_PRED = 6, + B_VL_PRED = 7, + B_HD_PRED = 8, + B_HU_PRED = 9, + NUM_BMODES = B_HU_PRED + 1 - B_DC_PRED, // = 10 + + // Luma16 or UV modes + DC_PRED = B_DC_PRED, V_PRED = B_VE_PRED, + H_PRED = B_HE_PRED, TM_PRED = B_TM_PRED, + B_PRED = NUM_BMODES, // refined I4x4 mode + NUM_PRED_MODES = 4, + + // special modes + B_DC_PRED_NOTOP = 4, + B_DC_PRED_NOLEFT = 5, + B_DC_PRED_NOTOPLEFT = 6, + NUM_B_DC_MODES = 7 }; + +enum { MB_FEATURE_TREE_PROBS = 3, + NUM_MB_SEGMENTS = 4, + NUM_REF_LF_DELTAS = 4, + NUM_MODE_LF_DELTAS = 4, // I4x4, ZERO, *, SPLIT + MAX_NUM_PARTITIONS = 8, + // Probabilities + NUM_TYPES = 4, // 0: i16-AC, 1: i16-DC, 2:chroma-AC, 3:i4-AC + NUM_BANDS = 8, + NUM_CTX = 3, + NUM_PROBAS = 11 + }; + +#endif // WEBP_DEC_COMMON_H_ diff --git a/thirdparty/libwebp/dec/decode_vp8.h b/thirdparty/libwebp/dec/decode_vp8.h deleted file mode 100644 index b9337bbec0..0000000000 --- a/thirdparty/libwebp/dec/decode_vp8.h +++ /dev/null @@ -1,185 +0,0 @@ -// Copyright 2010 Google Inc. All Rights Reserved. -// -// Use of this source code is governed by a BSD-style license -// that can be found in the COPYING file in the root of the source -// tree. An additional intellectual property rights grant can be found -// in the file PATENTS. All contributing project authors may -// be found in the AUTHORS file in the root of the source tree. -// ----------------------------------------------------------------------------- -// -// Low-level API for VP8 decoder -// -// Author: Skal (pascal.massimino@gmail.com) - -#ifndef WEBP_WEBP_DECODE_VP8_H_ -#define WEBP_WEBP_DECODE_VP8_H_ - -#include "../webp/decode.h" - -#ifdef __cplusplus -extern "C" { -#endif - -//------------------------------------------------------------------------------ -// Lower-level API -// -// These functions provide fine-grained control of the decoding process. -// The call flow should resemble: -// -// VP8Io io; -// VP8InitIo(&io); -// io.data = data; -// io.data_size = size; -// /* customize io's functions (setup()/put()/teardown()) if needed. */ -// -// VP8Decoder* dec = VP8New(); -// bool ok = VP8Decode(dec); -// if (!ok) printf("Error: %s\n", VP8StatusMessage(dec)); -// VP8Delete(dec); -// return ok; - -// Input / Output -typedef struct VP8Io VP8Io; -typedef int (*VP8IoPutHook)(const VP8Io* io); -typedef int (*VP8IoSetupHook)(VP8Io* io); -typedef void (*VP8IoTeardownHook)(const VP8Io* io); - -struct VP8Io { - // set by VP8GetHeaders() - int width, height; // picture dimensions, in pixels (invariable). - // These are the original, uncropped dimensions. - // The actual area passed to put() is stored - // in mb_w / mb_h fields. - - // set before calling put() - int mb_y; // position of the current rows (in pixels) - int mb_w; // number of columns in the sample - int mb_h; // number of rows in the sample - const uint8_t* y, *u, *v; // rows to copy (in yuv420 format) - int y_stride; // row stride for luma - int uv_stride; // row stride for chroma - - void* opaque; // user data - - // called when fresh samples are available. Currently, samples are in - // YUV420 format, and can be up to width x 24 in size (depending on the - // in-loop filtering level, e.g.). Should return false in case of error - // or abort request. The actual size of the area to update is mb_w x mb_h - // in size, taking cropping into account. - VP8IoPutHook put; - - // called just before starting to decode the blocks. - // Must return false in case of setup error, true otherwise. If false is - // returned, teardown() will NOT be called. But if the setup succeeded - // and true is returned, then teardown() will always be called afterward. - VP8IoSetupHook setup; - - // Called just after block decoding is finished (or when an error occurred - // during put()). Is NOT called if setup() failed. - VP8IoTeardownHook teardown; - - // this is a recommendation for the user-side yuv->rgb converter. This flag - // is set when calling setup() hook and can be overwritten by it. It then - // can be taken into consideration during the put() method. - int fancy_upsampling; - - // Input buffer. - size_t data_size; - const uint8_t* data; - - // If true, in-loop filtering will not be performed even if present in the - // bitstream. Switching off filtering may speed up decoding at the expense - // of more visible blocking. Note that output will also be non-compliant - // with the VP8 specifications. - int bypass_filtering; - - // Cropping parameters. - int use_cropping; - int crop_left, crop_right, crop_top, crop_bottom; - - // Scaling parameters. - int use_scaling; - int scaled_width, scaled_height; - - // If non NULL, pointer to the alpha data (if present) corresponding to the - // start of the current row (That is: it is pre-offset by mb_y and takes - // cropping into account). - const uint8_t* a; -}; - -// Internal, version-checked, entry point -int VP8InitIoInternal(VP8Io* const, int); - -// Set the custom IO function pointers and user-data. The setter for IO hooks -// should be called before initiating incremental decoding. Returns true if -// WebPIDecoder object is successfully modified, false otherwise. -int WebPISetIOHooks(WebPIDecoder* const idec, - VP8IoPutHook put, - VP8IoSetupHook setup, - VP8IoTeardownHook teardown, - void* user_data); - -// Main decoding object. This is an opaque structure. -typedef struct VP8Decoder VP8Decoder; - -// Create a new decoder object. -VP8Decoder* VP8New(void); - -// Must be called to make sure 'io' is initialized properly. -// Returns false in case of version mismatch. Upon such failure, no other -// decoding function should be called (VP8Decode, VP8GetHeaders, ...) -static WEBP_INLINE int VP8InitIo(VP8Io* const io) { - return VP8InitIoInternal(io, WEBP_DECODER_ABI_VERSION); -} - -// Decode the VP8 frame header. Returns true if ok. -// Note: 'io->data' must be pointing to the start of the VP8 frame header. -int VP8GetHeaders(VP8Decoder* const dec, VP8Io* const io); - -// Decode a picture. Will call VP8GetHeaders() if it wasn't done already. -// Returns false in case of error. -int VP8Decode(VP8Decoder* const dec, VP8Io* const io); - -// Return current status of the decoder: -VP8StatusCode VP8Status(VP8Decoder* const dec); - -// return readable string corresponding to the last status. -const char* VP8StatusMessage(VP8Decoder* const dec); - -// Resets the decoder in its initial state, reclaiming memory. -// Not a mandatory call between calls to VP8Decode(). -void VP8Clear(VP8Decoder* const dec); - -// Destroy the decoder object. -void VP8Delete(VP8Decoder* const dec); - -//------------------------------------------------------------------------------ -// Miscellaneous VP8/VP8L bitstream probing functions. - -// Returns true if the next 3 bytes in data contain the VP8 signature. -WEBP_EXTERN(int) VP8CheckSignature(const uint8_t* const data, size_t data_size); - -// Validates the VP8 data-header and retrieves basic header information viz -// width and height. Returns 0 in case of formatting error. *width/*height -// can be passed NULL. -WEBP_EXTERN(int) VP8GetInfo( - const uint8_t* data, - size_t data_size, // data available so far - size_t chunk_size, // total data size expected in the chunk - int* const width, int* const height); - -// Returns true if the next byte(s) in data is a VP8L signature. -WEBP_EXTERN(int) VP8LCheckSignature(const uint8_t* const data, size_t size); - -// Validates the VP8L data-header and retrieves basic header information viz -// width, height and alpha. Returns 0 in case of formatting error. -// width/height/has_alpha can be passed NULL. -WEBP_EXTERN(int) VP8LGetInfo( - const uint8_t* data, size_t data_size, // data available so far - int* const width, int* const height, int* const has_alpha); - -#ifdef __cplusplus -} // extern "C" -#endif - -#endif /* WEBP_WEBP_DECODE_VP8_H_ */ diff --git a/thirdparty/libwebp/dec/frame.c b/thirdparty/libwebp/dec/frame.c deleted file mode 100644 index 22d291d2cd..0000000000 --- a/thirdparty/libwebp/dec/frame.c +++ /dev/null @@ -1,812 +0,0 @@ -// Copyright 2010 Google Inc. All Rights Reserved. -// -// Use of this source code is governed by a BSD-style license -// that can be found in the COPYING file in the root of the source -// tree. An additional intellectual property rights grant can be found -// in the file PATENTS. All contributing project authors may -// be found in the AUTHORS file in the root of the source tree. -// ----------------------------------------------------------------------------- -// -// Frame-reconstruction function. Memory allocation. -// -// Author: Skal (pascal.massimino@gmail.com) - -#include -#include "./vp8i.h" -#include "../utils/utils.h" - -//------------------------------------------------------------------------------ -// Main reconstruction function. - -static const int kScan[16] = { - 0 + 0 * BPS, 4 + 0 * BPS, 8 + 0 * BPS, 12 + 0 * BPS, - 0 + 4 * BPS, 4 + 4 * BPS, 8 + 4 * BPS, 12 + 4 * BPS, - 0 + 8 * BPS, 4 + 8 * BPS, 8 + 8 * BPS, 12 + 8 * BPS, - 0 + 12 * BPS, 4 + 12 * BPS, 8 + 12 * BPS, 12 + 12 * BPS -}; - -static int CheckMode(int mb_x, int mb_y, int mode) { - if (mode == B_DC_PRED) { - if (mb_x == 0) { - return (mb_y == 0) ? B_DC_PRED_NOTOPLEFT : B_DC_PRED_NOLEFT; - } else { - return (mb_y == 0) ? B_DC_PRED_NOTOP : B_DC_PRED; - } - } - return mode; -} - -static void Copy32b(uint8_t* const dst, const uint8_t* const src) { - memcpy(dst, src, 4); -} - -static WEBP_INLINE void DoTransform(uint32_t bits, const int16_t* const src, - uint8_t* const dst) { - switch (bits >> 30) { - case 3: - VP8Transform(src, dst, 0); - break; - case 2: - VP8TransformAC3(src, dst); - break; - case 1: - VP8TransformDC(src, dst); - break; - default: - break; - } -} - -static void DoUVTransform(uint32_t bits, const int16_t* const src, - uint8_t* const dst) { - if (bits & 0xff) { // any non-zero coeff at all? - if (bits & 0xaa) { // any non-zero AC coefficient? - VP8TransformUV(src, dst); // note we don't use the AC3 variant for U/V - } else { - VP8TransformDCUV(src, dst); - } - } -} - -static void ReconstructRow(const VP8Decoder* const dec, - const VP8ThreadContext* ctx) { - int j; - int mb_x; - const int mb_y = ctx->mb_y_; - const int cache_id = ctx->id_; - uint8_t* const y_dst = dec->yuv_b_ + Y_OFF; - uint8_t* const u_dst = dec->yuv_b_ + U_OFF; - uint8_t* const v_dst = dec->yuv_b_ + V_OFF; - - // Initialize left-most block. - for (j = 0; j < 16; ++j) { - y_dst[j * BPS - 1] = 129; - } - for (j = 0; j < 8; ++j) { - u_dst[j * BPS - 1] = 129; - v_dst[j * BPS - 1] = 129; - } - - // Init top-left sample on left column too. - if (mb_y > 0) { - y_dst[-1 - BPS] = u_dst[-1 - BPS] = v_dst[-1 - BPS] = 129; - } else { - // we only need to do this init once at block (0,0). - // Afterward, it remains valid for the whole topmost row. - memset(y_dst - BPS - 1, 127, 16 + 4 + 1); - memset(u_dst - BPS - 1, 127, 8 + 1); - memset(v_dst - BPS - 1, 127, 8 + 1); - } - - // Reconstruct one row. - for (mb_x = 0; mb_x < dec->mb_w_; ++mb_x) { - const VP8MBData* const block = ctx->mb_data_ + mb_x; - - // Rotate in the left samples from previously decoded block. We move four - // pixels at a time for alignment reason, and because of in-loop filter. - if (mb_x > 0) { - for (j = -1; j < 16; ++j) { - Copy32b(&y_dst[j * BPS - 4], &y_dst[j * BPS + 12]); - } - for (j = -1; j < 8; ++j) { - Copy32b(&u_dst[j * BPS - 4], &u_dst[j * BPS + 4]); - Copy32b(&v_dst[j * BPS - 4], &v_dst[j * BPS + 4]); - } - } - { - // bring top samples into the cache - VP8TopSamples* const top_yuv = dec->yuv_t_ + mb_x; - const int16_t* const coeffs = block->coeffs_; - uint32_t bits = block->non_zero_y_; - int n; - - if (mb_y > 0) { - memcpy(y_dst - BPS, top_yuv[0].y, 16); - memcpy(u_dst - BPS, top_yuv[0].u, 8); - memcpy(v_dst - BPS, top_yuv[0].v, 8); - } - - // predict and add residuals - if (block->is_i4x4_) { // 4x4 - uint32_t* const top_right = (uint32_t*)(y_dst - BPS + 16); - - if (mb_y > 0) { - if (mb_x >= dec->mb_w_ - 1) { // on rightmost border - memset(top_right, top_yuv[0].y[15], sizeof(*top_right)); - } else { - memcpy(top_right, top_yuv[1].y, sizeof(*top_right)); - } - } - // replicate the top-right pixels below - top_right[BPS] = top_right[2 * BPS] = top_right[3 * BPS] = top_right[0]; - - // predict and add residuals for all 4x4 blocks in turn. - for (n = 0; n < 16; ++n, bits <<= 2) { - uint8_t* const dst = y_dst + kScan[n]; - VP8PredLuma4[block->imodes_[n]](dst); - DoTransform(bits, coeffs + n * 16, dst); - } - } else { // 16x16 - const int pred_func = CheckMode(mb_x, mb_y, block->imodes_[0]); - VP8PredLuma16[pred_func](y_dst); - if (bits != 0) { - for (n = 0; n < 16; ++n, bits <<= 2) { - DoTransform(bits, coeffs + n * 16, y_dst + kScan[n]); - } - } - } - { - // Chroma - const uint32_t bits_uv = block->non_zero_uv_; - const int pred_func = CheckMode(mb_x, mb_y, block->uvmode_); - VP8PredChroma8[pred_func](u_dst); - VP8PredChroma8[pred_func](v_dst); - DoUVTransform(bits_uv >> 0, coeffs + 16 * 16, u_dst); - DoUVTransform(bits_uv >> 8, coeffs + 20 * 16, v_dst); - } - - // stash away top samples for next block - if (mb_y < dec->mb_h_ - 1) { - memcpy(top_yuv[0].y, y_dst + 15 * BPS, 16); - memcpy(top_yuv[0].u, u_dst + 7 * BPS, 8); - memcpy(top_yuv[0].v, v_dst + 7 * BPS, 8); - } - } - // Transfer reconstructed samples from yuv_b_ cache to final destination. - { - const int y_offset = cache_id * 16 * dec->cache_y_stride_; - const int uv_offset = cache_id * 8 * dec->cache_uv_stride_; - uint8_t* const y_out = dec->cache_y_ + mb_x * 16 + y_offset; - uint8_t* const u_out = dec->cache_u_ + mb_x * 8 + uv_offset; - uint8_t* const v_out = dec->cache_v_ + mb_x * 8 + uv_offset; - for (j = 0; j < 16; ++j) { - memcpy(y_out + j * dec->cache_y_stride_, y_dst + j * BPS, 16); - } - for (j = 0; j < 8; ++j) { - memcpy(u_out + j * dec->cache_uv_stride_, u_dst + j * BPS, 8); - memcpy(v_out + j * dec->cache_uv_stride_, v_dst + j * BPS, 8); - } - } - } -} - -//------------------------------------------------------------------------------ -// Filtering - -// kFilterExtraRows[] = How many extra lines are needed on the MB boundary -// for caching, given a filtering level. -// Simple filter: up to 2 luma samples are read and 1 is written. -// Complex filter: up to 4 luma samples are read and 3 are written. Same for -// U/V, so it's 8 samples total (because of the 2x upsampling). -static const uint8_t kFilterExtraRows[3] = { 0, 2, 8 }; - -static void DoFilter(const VP8Decoder* const dec, int mb_x, int mb_y) { - const VP8ThreadContext* const ctx = &dec->thread_ctx_; - const int cache_id = ctx->id_; - const int y_bps = dec->cache_y_stride_; - const VP8FInfo* const f_info = ctx->f_info_ + mb_x; - uint8_t* const y_dst = dec->cache_y_ + cache_id * 16 * y_bps + mb_x * 16; - const int ilevel = f_info->f_ilevel_; - const int limit = f_info->f_limit_; - if (limit == 0) { - return; - } - assert(limit >= 3); - if (dec->filter_type_ == 1) { // simple - if (mb_x > 0) { - VP8SimpleHFilter16(y_dst, y_bps, limit + 4); - } - if (f_info->f_inner_) { - VP8SimpleHFilter16i(y_dst, y_bps, limit); - } - if (mb_y > 0) { - VP8SimpleVFilter16(y_dst, y_bps, limit + 4); - } - if (f_info->f_inner_) { - VP8SimpleVFilter16i(y_dst, y_bps, limit); - } - } else { // complex - const int uv_bps = dec->cache_uv_stride_; - uint8_t* const u_dst = dec->cache_u_ + cache_id * 8 * uv_bps + mb_x * 8; - uint8_t* const v_dst = dec->cache_v_ + cache_id * 8 * uv_bps + mb_x * 8; - const int hev_thresh = f_info->hev_thresh_; - if (mb_x > 0) { - VP8HFilter16(y_dst, y_bps, limit + 4, ilevel, hev_thresh); - VP8HFilter8(u_dst, v_dst, uv_bps, limit + 4, ilevel, hev_thresh); - } - if (f_info->f_inner_) { - VP8HFilter16i(y_dst, y_bps, limit, ilevel, hev_thresh); - VP8HFilter8i(u_dst, v_dst, uv_bps, limit, ilevel, hev_thresh); - } - if (mb_y > 0) { - VP8VFilter16(y_dst, y_bps, limit + 4, ilevel, hev_thresh); - VP8VFilter8(u_dst, v_dst, uv_bps, limit + 4, ilevel, hev_thresh); - } - if (f_info->f_inner_) { - VP8VFilter16i(y_dst, y_bps, limit, ilevel, hev_thresh); - VP8VFilter8i(u_dst, v_dst, uv_bps, limit, ilevel, hev_thresh); - } - } -} - -// Filter the decoded macroblock row (if needed) -static void FilterRow(const VP8Decoder* const dec) { - int mb_x; - const int mb_y = dec->thread_ctx_.mb_y_; - assert(dec->thread_ctx_.filter_row_); - for (mb_x = dec->tl_mb_x_; mb_x < dec->br_mb_x_; ++mb_x) { - DoFilter(dec, mb_x, mb_y); - } -} - -//------------------------------------------------------------------------------ -// Precompute the filtering strength for each segment and each i4x4/i16x16 mode. - -static void PrecomputeFilterStrengths(VP8Decoder* const dec) { - if (dec->filter_type_ > 0) { - int s; - const VP8FilterHeader* const hdr = &dec->filter_hdr_; - for (s = 0; s < NUM_MB_SEGMENTS; ++s) { - int i4x4; - // First, compute the initial level - int base_level; - if (dec->segment_hdr_.use_segment_) { - base_level = dec->segment_hdr_.filter_strength_[s]; - if (!dec->segment_hdr_.absolute_delta_) { - base_level += hdr->level_; - } - } else { - base_level = hdr->level_; - } - for (i4x4 = 0; i4x4 <= 1; ++i4x4) { - VP8FInfo* const info = &dec->fstrengths_[s][i4x4]; - int level = base_level; - if (hdr->use_lf_delta_) { - level += hdr->ref_lf_delta_[0]; - if (i4x4) { - level += hdr->mode_lf_delta_[0]; - } - } - level = (level < 0) ? 0 : (level > 63) ? 63 : level; - if (level > 0) { - int ilevel = level; - if (hdr->sharpness_ > 0) { - if (hdr->sharpness_ > 4) { - ilevel >>= 2; - } else { - ilevel >>= 1; - } - if (ilevel > 9 - hdr->sharpness_) { - ilevel = 9 - hdr->sharpness_; - } - } - if (ilevel < 1) ilevel = 1; - info->f_ilevel_ = ilevel; - info->f_limit_ = 2 * level + ilevel; - info->hev_thresh_ = (level >= 40) ? 2 : (level >= 15) ? 1 : 0; - } else { - info->f_limit_ = 0; // no filtering - } - info->f_inner_ = i4x4; - } - } - } -} - -//------------------------------------------------------------------------------ -// Dithering - -// minimal amp that will provide a non-zero dithering effect -#define MIN_DITHER_AMP 4 - -#define DITHER_AMP_TAB_SIZE 12 -static const int kQuantToDitherAmp[DITHER_AMP_TAB_SIZE] = { - // roughly, it's dqm->uv_mat_[1] - 8, 7, 6, 4, 4, 2, 2, 2, 1, 1, 1, 1 -}; - -void VP8InitDithering(const WebPDecoderOptions* const options, - VP8Decoder* const dec) { - assert(dec != NULL); - if (options != NULL) { - const int d = options->dithering_strength; - const int max_amp = (1 << VP8_RANDOM_DITHER_FIX) - 1; - const int f = (d < 0) ? 0 : (d > 100) ? max_amp : (d * max_amp / 100); - if (f > 0) { - int s; - int all_amp = 0; - for (s = 0; s < NUM_MB_SEGMENTS; ++s) { - VP8QuantMatrix* const dqm = &dec->dqm_[s]; - if (dqm->uv_quant_ < DITHER_AMP_TAB_SIZE) { - // TODO(skal): should we specially dither more for uv_quant_ < 0? - const int idx = (dqm->uv_quant_ < 0) ? 0 : dqm->uv_quant_; - dqm->dither_ = (f * kQuantToDitherAmp[idx]) >> 3; - } - all_amp |= dqm->dither_; - } - if (all_amp != 0) { - VP8InitRandom(&dec->dithering_rg_, 1.0f); - dec->dither_ = 1; - } - } - // potentially allow alpha dithering - dec->alpha_dithering_ = options->alpha_dithering_strength; - if (dec->alpha_dithering_ > 100) { - dec->alpha_dithering_ = 100; - } else if (dec->alpha_dithering_ < 0) { - dec->alpha_dithering_ = 0; - } - } -} - -// Convert to range: [-2,2] for dither=50, [-4,4] for dither=100 -static void Dither8x8(VP8Random* const rg, uint8_t* dst, int bps, int amp) { - uint8_t dither[64]; - int i; - for (i = 0; i < 8 * 8; ++i) { - dither[i] = VP8RandomBits2(rg, VP8_DITHER_AMP_BITS + 1, amp); - } - VP8DitherCombine8x8(dither, dst, bps); -} - -static void DitherRow(VP8Decoder* const dec) { - int mb_x; - assert(dec->dither_); - for (mb_x = dec->tl_mb_x_; mb_x < dec->br_mb_x_; ++mb_x) { - const VP8ThreadContext* const ctx = &dec->thread_ctx_; - const VP8MBData* const data = ctx->mb_data_ + mb_x; - const int cache_id = ctx->id_; - const int uv_bps = dec->cache_uv_stride_; - if (data->dither_ >= MIN_DITHER_AMP) { - uint8_t* const u_dst = dec->cache_u_ + cache_id * 8 * uv_bps + mb_x * 8; - uint8_t* const v_dst = dec->cache_v_ + cache_id * 8 * uv_bps + mb_x * 8; - Dither8x8(&dec->dithering_rg_, u_dst, uv_bps, data->dither_); - Dither8x8(&dec->dithering_rg_, v_dst, uv_bps, data->dither_); - } - } -} - -//------------------------------------------------------------------------------ -// This function is called after a row of macroblocks is finished decoding. -// It also takes into account the following restrictions: -// * In case of in-loop filtering, we must hold off sending some of the bottom -// pixels as they are yet unfiltered. They will be when the next macroblock -// row is decoded. Meanwhile, we must preserve them by rotating them in the -// cache area. This doesn't hold for the very bottom row of the uncropped -// picture of course. -// * we must clip the remaining pixels against the cropping area. The VP8Io -// struct must have the following fields set correctly before calling put(): - -#define MACROBLOCK_VPOS(mb_y) ((mb_y) * 16) // vertical position of a MB - -// Finalize and transmit a complete row. Return false in case of user-abort. -static int FinishRow(VP8Decoder* const dec, VP8Io* const io) { - int ok = 1; - const VP8ThreadContext* const ctx = &dec->thread_ctx_; - const int cache_id = ctx->id_; - const int extra_y_rows = kFilterExtraRows[dec->filter_type_]; - const int ysize = extra_y_rows * dec->cache_y_stride_; - const int uvsize = (extra_y_rows / 2) * dec->cache_uv_stride_; - const int y_offset = cache_id * 16 * dec->cache_y_stride_; - const int uv_offset = cache_id * 8 * dec->cache_uv_stride_; - uint8_t* const ydst = dec->cache_y_ - ysize + y_offset; - uint8_t* const udst = dec->cache_u_ - uvsize + uv_offset; - uint8_t* const vdst = dec->cache_v_ - uvsize + uv_offset; - const int mb_y = ctx->mb_y_; - const int is_first_row = (mb_y == 0); - const int is_last_row = (mb_y >= dec->br_mb_y_ - 1); - - if (dec->mt_method_ == 2) { - ReconstructRow(dec, ctx); - } - - if (ctx->filter_row_) { - FilterRow(dec); - } - - if (dec->dither_) { - DitherRow(dec); - } - - if (io->put != NULL) { - int y_start = MACROBLOCK_VPOS(mb_y); - int y_end = MACROBLOCK_VPOS(mb_y + 1); - if (!is_first_row) { - y_start -= extra_y_rows; - io->y = ydst; - io->u = udst; - io->v = vdst; - } else { - io->y = dec->cache_y_ + y_offset; - io->u = dec->cache_u_ + uv_offset; - io->v = dec->cache_v_ + uv_offset; - } - - if (!is_last_row) { - y_end -= extra_y_rows; - } - if (y_end > io->crop_bottom) { - y_end = io->crop_bottom; // make sure we don't overflow on last row. - } - io->a = NULL; - if (dec->alpha_data_ != NULL && y_start < y_end) { - // TODO(skal): testing presence of alpha with dec->alpha_data_ is not a - // good idea. - io->a = VP8DecompressAlphaRows(dec, io, y_start, y_end - y_start); - if (io->a == NULL) { - return VP8SetError(dec, VP8_STATUS_BITSTREAM_ERROR, - "Could not decode alpha data."); - } - } - if (y_start < io->crop_top) { - const int delta_y = io->crop_top - y_start; - y_start = io->crop_top; - assert(!(delta_y & 1)); - io->y += dec->cache_y_stride_ * delta_y; - io->u += dec->cache_uv_stride_ * (delta_y >> 1); - io->v += dec->cache_uv_stride_ * (delta_y >> 1); - if (io->a != NULL) { - io->a += io->width * delta_y; - } - } - if (y_start < y_end) { - io->y += io->crop_left; - io->u += io->crop_left >> 1; - io->v += io->crop_left >> 1; - if (io->a != NULL) { - io->a += io->crop_left; - } - io->mb_y = y_start - io->crop_top; - io->mb_w = io->crop_right - io->crop_left; - io->mb_h = y_end - y_start; - ok = io->put(io); - } - } - // rotate top samples if needed - if (cache_id + 1 == dec->num_caches_) { - if (!is_last_row) { - memcpy(dec->cache_y_ - ysize, ydst + 16 * dec->cache_y_stride_, ysize); - memcpy(dec->cache_u_ - uvsize, udst + 8 * dec->cache_uv_stride_, uvsize); - memcpy(dec->cache_v_ - uvsize, vdst + 8 * dec->cache_uv_stride_, uvsize); - } - } - - return ok; -} - -#undef MACROBLOCK_VPOS - -//------------------------------------------------------------------------------ - -int VP8ProcessRow(VP8Decoder* const dec, VP8Io* const io) { - int ok = 1; - VP8ThreadContext* const ctx = &dec->thread_ctx_; - const int filter_row = - (dec->filter_type_ > 0) && - (dec->mb_y_ >= dec->tl_mb_y_) && (dec->mb_y_ <= dec->br_mb_y_); - if (dec->mt_method_ == 0) { - // ctx->id_ and ctx->f_info_ are already set - ctx->mb_y_ = dec->mb_y_; - ctx->filter_row_ = filter_row; - ReconstructRow(dec, ctx); - ok = FinishRow(dec, io); - } else { - WebPWorker* const worker = &dec->worker_; - // Finish previous job *before* updating context - ok &= WebPGetWorkerInterface()->Sync(worker); - assert(worker->status_ == OK); - if (ok) { // spawn a new deblocking/output job - ctx->io_ = *io; - ctx->id_ = dec->cache_id_; - ctx->mb_y_ = dec->mb_y_; - ctx->filter_row_ = filter_row; - if (dec->mt_method_ == 2) { // swap macroblock data - VP8MBData* const tmp = ctx->mb_data_; - ctx->mb_data_ = dec->mb_data_; - dec->mb_data_ = tmp; - } else { - // perform reconstruction directly in main thread - ReconstructRow(dec, ctx); - } - if (filter_row) { // swap filter info - VP8FInfo* const tmp = ctx->f_info_; - ctx->f_info_ = dec->f_info_; - dec->f_info_ = tmp; - } - // (reconstruct)+filter in parallel - WebPGetWorkerInterface()->Launch(worker); - if (++dec->cache_id_ == dec->num_caches_) { - dec->cache_id_ = 0; - } - } - } - return ok; -} - -//------------------------------------------------------------------------------ -// Finish setting up the decoding parameter once user's setup() is called. - -VP8StatusCode VP8EnterCritical(VP8Decoder* const dec, VP8Io* const io) { - // Call setup() first. This may trigger additional decoding features on 'io'. - // Note: Afterward, we must call teardown() no matter what. - if (io->setup != NULL && !io->setup(io)) { - VP8SetError(dec, VP8_STATUS_USER_ABORT, "Frame setup failed"); - return dec->status_; - } - - // Disable filtering per user request - if (io->bypass_filtering) { - dec->filter_type_ = 0; - } - // TODO(skal): filter type / strength / sharpness forcing - - // Define the area where we can skip in-loop filtering, in case of cropping. - // - // 'Simple' filter reads two luma samples outside of the macroblock - // and filters one. It doesn't filter the chroma samples. Hence, we can - // avoid doing the in-loop filtering before crop_top/crop_left position. - // For the 'Complex' filter, 3 samples are read and up to 3 are filtered. - // Means: there's a dependency chain that goes all the way up to the - // top-left corner of the picture (MB #0). We must filter all the previous - // macroblocks. - // TODO(skal): add an 'approximate_decoding' option, that won't produce - // a 1:1 bit-exactness for complex filtering? - { - const int extra_pixels = kFilterExtraRows[dec->filter_type_]; - if (dec->filter_type_ == 2) { - // For complex filter, we need to preserve the dependency chain. - dec->tl_mb_x_ = 0; - dec->tl_mb_y_ = 0; - } else { - // For simple filter, we can filter only the cropped region. - // We include 'extra_pixels' on the other side of the boundary, since - // vertical or horizontal filtering of the previous macroblock can - // modify some abutting pixels. - dec->tl_mb_x_ = (io->crop_left - extra_pixels) >> 4; - dec->tl_mb_y_ = (io->crop_top - extra_pixels) >> 4; - if (dec->tl_mb_x_ < 0) dec->tl_mb_x_ = 0; - if (dec->tl_mb_y_ < 0) dec->tl_mb_y_ = 0; - } - // We need some 'extra' pixels on the right/bottom. - dec->br_mb_y_ = (io->crop_bottom + 15 + extra_pixels) >> 4; - dec->br_mb_x_ = (io->crop_right + 15 + extra_pixels) >> 4; - if (dec->br_mb_x_ > dec->mb_w_) { - dec->br_mb_x_ = dec->mb_w_; - } - if (dec->br_mb_y_ > dec->mb_h_) { - dec->br_mb_y_ = dec->mb_h_; - } - } - PrecomputeFilterStrengths(dec); - return VP8_STATUS_OK; -} - -int VP8ExitCritical(VP8Decoder* const dec, VP8Io* const io) { - int ok = 1; - if (dec->mt_method_ > 0) { - ok = WebPGetWorkerInterface()->Sync(&dec->worker_); - } - - if (io->teardown != NULL) { - io->teardown(io); - } - return ok; -} - -//------------------------------------------------------------------------------ -// For multi-threaded decoding we need to use 3 rows of 16 pixels as delay line. -// -// Reason is: the deblocking filter cannot deblock the bottom horizontal edges -// immediately, and needs to wait for first few rows of the next macroblock to -// be decoded. Hence, deblocking is lagging behind by 4 or 8 pixels (depending -// on strength). -// With two threads, the vertical positions of the rows being decoded are: -// Decode: [ 0..15][16..31][32..47][48..63][64..79][... -// Deblock: [ 0..11][12..27][28..43][44..59][... -// If we use two threads and two caches of 16 pixels, the sequence would be: -// Decode: [ 0..15][16..31][ 0..15!!][16..31][ 0..15][... -// Deblock: [ 0..11][12..27!!][-4..11][12..27][... -// The problem occurs during row [12..15!!] that both the decoding and -// deblocking threads are writing simultaneously. -// With 3 cache lines, one get a safe write pattern: -// Decode: [ 0..15][16..31][32..47][ 0..15][16..31][32..47][0.. -// Deblock: [ 0..11][12..27][28..43][-4..11][12..27][28... -// Note that multi-threaded output _without_ deblocking can make use of two -// cache lines of 16 pixels only, since there's no lagging behind. The decoding -// and output process have non-concurrent writing: -// Decode: [ 0..15][16..31][ 0..15][16..31][... -// io->put: [ 0..15][16..31][ 0..15][... - -#define MT_CACHE_LINES 3 -#define ST_CACHE_LINES 1 // 1 cache row only for single-threaded case - -// Initialize multi/single-thread worker -static int InitThreadContext(VP8Decoder* const dec) { - dec->cache_id_ = 0; - if (dec->mt_method_ > 0) { - WebPWorker* const worker = &dec->worker_; - if (!WebPGetWorkerInterface()->Reset(worker)) { - return VP8SetError(dec, VP8_STATUS_OUT_OF_MEMORY, - "thread initialization failed."); - } - worker->data1 = dec; - worker->data2 = (void*)&dec->thread_ctx_.io_; - worker->hook = (WebPWorkerHook)FinishRow; - dec->num_caches_ = - (dec->filter_type_ > 0) ? MT_CACHE_LINES : MT_CACHE_LINES - 1; - } else { - dec->num_caches_ = ST_CACHE_LINES; - } - return 1; -} - -int VP8GetThreadMethod(const WebPDecoderOptions* const options, - const WebPHeaderStructure* const headers, - int width, int height) { - if (options == NULL || options->use_threads == 0) { - return 0; - } - (void)headers; - (void)width; - (void)height; - assert(headers == NULL || !headers->is_lossless); -#if defined(WEBP_USE_THREAD) - if (width < MIN_WIDTH_FOR_THREADS) return 0; - // TODO(skal): tune the heuristic further -#if 0 - if (height < 2 * width) return 2; -#endif - return 2; -#else // !WEBP_USE_THREAD - return 0; -#endif -} - -#undef MT_CACHE_LINES -#undef ST_CACHE_LINES - -//------------------------------------------------------------------------------ -// Memory setup - -static int AllocateMemory(VP8Decoder* const dec) { - const int num_caches = dec->num_caches_; - const int mb_w = dec->mb_w_; - // Note: we use 'size_t' when there's no overflow risk, uint64_t otherwise. - const size_t intra_pred_mode_size = 4 * mb_w * sizeof(uint8_t); - const size_t top_size = sizeof(VP8TopSamples) * mb_w; - const size_t mb_info_size = (mb_w + 1) * sizeof(VP8MB); - const size_t f_info_size = - (dec->filter_type_ > 0) ? - mb_w * (dec->mt_method_ > 0 ? 2 : 1) * sizeof(VP8FInfo) - : 0; - const size_t yuv_size = YUV_SIZE * sizeof(*dec->yuv_b_); - const size_t mb_data_size = - (dec->mt_method_ == 2 ? 2 : 1) * mb_w * sizeof(*dec->mb_data_); - const size_t cache_height = (16 * num_caches - + kFilterExtraRows[dec->filter_type_]) * 3 / 2; - const size_t cache_size = top_size * cache_height; - // alpha_size is the only one that scales as width x height. - const uint64_t alpha_size = (dec->alpha_data_ != NULL) ? - (uint64_t)dec->pic_hdr_.width_ * dec->pic_hdr_.height_ : 0ULL; - const uint64_t needed = (uint64_t)intra_pred_mode_size - + top_size + mb_info_size + f_info_size - + yuv_size + mb_data_size - + cache_size + alpha_size + WEBP_ALIGN_CST; - uint8_t* mem; - - if (needed != (size_t)needed) return 0; // check for overflow - if (needed > dec->mem_size_) { - WebPSafeFree(dec->mem_); - dec->mem_size_ = 0; - dec->mem_ = WebPSafeMalloc(needed, sizeof(uint8_t)); - if (dec->mem_ == NULL) { - return VP8SetError(dec, VP8_STATUS_OUT_OF_MEMORY, - "no memory during frame initialization."); - } - // down-cast is ok, thanks to WebPSafeAlloc() above. - dec->mem_size_ = (size_t)needed; - } - - mem = (uint8_t*)dec->mem_; - dec->intra_t_ = (uint8_t*)mem; - mem += intra_pred_mode_size; - - dec->yuv_t_ = (VP8TopSamples*)mem; - mem += top_size; - - dec->mb_info_ = ((VP8MB*)mem) + 1; - mem += mb_info_size; - - dec->f_info_ = f_info_size ? (VP8FInfo*)mem : NULL; - mem += f_info_size; - dec->thread_ctx_.id_ = 0; - dec->thread_ctx_.f_info_ = dec->f_info_; - if (dec->mt_method_ > 0) { - // secondary cache line. The deblocking process need to make use of the - // filtering strength from previous macroblock row, while the new ones - // are being decoded in parallel. We'll just swap the pointers. - dec->thread_ctx_.f_info_ += mb_w; - } - - mem = (uint8_t*)WEBP_ALIGN(mem); - assert((yuv_size & WEBP_ALIGN_CST) == 0); - dec->yuv_b_ = (uint8_t*)mem; - mem += yuv_size; - - dec->mb_data_ = (VP8MBData*)mem; - dec->thread_ctx_.mb_data_ = (VP8MBData*)mem; - if (dec->mt_method_ == 2) { - dec->thread_ctx_.mb_data_ += mb_w; - } - mem += mb_data_size; - - dec->cache_y_stride_ = 16 * mb_w; - dec->cache_uv_stride_ = 8 * mb_w; - { - const int extra_rows = kFilterExtraRows[dec->filter_type_]; - const int extra_y = extra_rows * dec->cache_y_stride_; - const int extra_uv = (extra_rows / 2) * dec->cache_uv_stride_; - dec->cache_y_ = ((uint8_t*)mem) + extra_y; - dec->cache_u_ = dec->cache_y_ - + 16 * num_caches * dec->cache_y_stride_ + extra_uv; - dec->cache_v_ = dec->cache_u_ - + 8 * num_caches * dec->cache_uv_stride_ + extra_uv; - dec->cache_id_ = 0; - } - mem += cache_size; - - // alpha plane - dec->alpha_plane_ = alpha_size ? (uint8_t*)mem : NULL; - mem += alpha_size; - assert(mem <= (uint8_t*)dec->mem_ + dec->mem_size_); - - // note: left/top-info is initialized once for all. - memset(dec->mb_info_ - 1, 0, mb_info_size); - VP8InitScanline(dec); // initialize left too. - - // initialize top - memset(dec->intra_t_, B_DC_PRED, intra_pred_mode_size); - - return 1; -} - -static void InitIo(VP8Decoder* const dec, VP8Io* io) { - // prepare 'io' - io->mb_y = 0; - io->y = dec->cache_y_; - io->u = dec->cache_u_; - io->v = dec->cache_v_; - io->y_stride = dec->cache_y_stride_; - io->uv_stride = dec->cache_uv_stride_; - io->a = NULL; -} - -int VP8InitFrame(VP8Decoder* const dec, VP8Io* const io) { - if (!InitThreadContext(dec)) return 0; // call first. Sets dec->num_caches_. - if (!AllocateMemory(dec)) return 0; - InitIo(dec, io); - VP8DspInit(); // Init critical function pointers and look-up tables. - return 1; -} - -//------------------------------------------------------------------------------ diff --git a/thirdparty/libwebp/dec/frame_dec.c b/thirdparty/libwebp/dec/frame_dec.c new file mode 100644 index 0000000000..f91e27f7c8 --- /dev/null +++ b/thirdparty/libwebp/dec/frame_dec.c @@ -0,0 +1,812 @@ +// Copyright 2010 Google Inc. All Rights Reserved. +// +// Use of this source code is governed by a BSD-style license +// that can be found in the COPYING file in the root of the source +// tree. An additional intellectual property rights grant can be found +// in the file PATENTS. All contributing project authors may +// be found in the AUTHORS file in the root of the source tree. +// ----------------------------------------------------------------------------- +// +// Frame-reconstruction function. Memory allocation. +// +// Author: Skal (pascal.massimino@gmail.com) + +#include +#include "./vp8i_dec.h" +#include "../utils/utils.h" + +//------------------------------------------------------------------------------ +// Main reconstruction function. + +static const int kScan[16] = { + 0 + 0 * BPS, 4 + 0 * BPS, 8 + 0 * BPS, 12 + 0 * BPS, + 0 + 4 * BPS, 4 + 4 * BPS, 8 + 4 * BPS, 12 + 4 * BPS, + 0 + 8 * BPS, 4 + 8 * BPS, 8 + 8 * BPS, 12 + 8 * BPS, + 0 + 12 * BPS, 4 + 12 * BPS, 8 + 12 * BPS, 12 + 12 * BPS +}; + +static int CheckMode(int mb_x, int mb_y, int mode) { + if (mode == B_DC_PRED) { + if (mb_x == 0) { + return (mb_y == 0) ? B_DC_PRED_NOTOPLEFT : B_DC_PRED_NOLEFT; + } else { + return (mb_y == 0) ? B_DC_PRED_NOTOP : B_DC_PRED; + } + } + return mode; +} + +static void Copy32b(uint8_t* const dst, const uint8_t* const src) { + memcpy(dst, src, 4); +} + +static WEBP_INLINE void DoTransform(uint32_t bits, const int16_t* const src, + uint8_t* const dst) { + switch (bits >> 30) { + case 3: + VP8Transform(src, dst, 0); + break; + case 2: + VP8TransformAC3(src, dst); + break; + case 1: + VP8TransformDC(src, dst); + break; + default: + break; + } +} + +static void DoUVTransform(uint32_t bits, const int16_t* const src, + uint8_t* const dst) { + if (bits & 0xff) { // any non-zero coeff at all? + if (bits & 0xaa) { // any non-zero AC coefficient? + VP8TransformUV(src, dst); // note we don't use the AC3 variant for U/V + } else { + VP8TransformDCUV(src, dst); + } + } +} + +static void ReconstructRow(const VP8Decoder* const dec, + const VP8ThreadContext* ctx) { + int j; + int mb_x; + const int mb_y = ctx->mb_y_; + const int cache_id = ctx->id_; + uint8_t* const y_dst = dec->yuv_b_ + Y_OFF; + uint8_t* const u_dst = dec->yuv_b_ + U_OFF; + uint8_t* const v_dst = dec->yuv_b_ + V_OFF; + + // Initialize left-most block. + for (j = 0; j < 16; ++j) { + y_dst[j * BPS - 1] = 129; + } + for (j = 0; j < 8; ++j) { + u_dst[j * BPS - 1] = 129; + v_dst[j * BPS - 1] = 129; + } + + // Init top-left sample on left column too. + if (mb_y > 0) { + y_dst[-1 - BPS] = u_dst[-1 - BPS] = v_dst[-1 - BPS] = 129; + } else { + // we only need to do this init once at block (0,0). + // Afterward, it remains valid for the whole topmost row. + memset(y_dst - BPS - 1, 127, 16 + 4 + 1); + memset(u_dst - BPS - 1, 127, 8 + 1); + memset(v_dst - BPS - 1, 127, 8 + 1); + } + + // Reconstruct one row. + for (mb_x = 0; mb_x < dec->mb_w_; ++mb_x) { + const VP8MBData* const block = ctx->mb_data_ + mb_x; + + // Rotate in the left samples from previously decoded block. We move four + // pixels at a time for alignment reason, and because of in-loop filter. + if (mb_x > 0) { + for (j = -1; j < 16; ++j) { + Copy32b(&y_dst[j * BPS - 4], &y_dst[j * BPS + 12]); + } + for (j = -1; j < 8; ++j) { + Copy32b(&u_dst[j * BPS - 4], &u_dst[j * BPS + 4]); + Copy32b(&v_dst[j * BPS - 4], &v_dst[j * BPS + 4]); + } + } + { + // bring top samples into the cache + VP8TopSamples* const top_yuv = dec->yuv_t_ + mb_x; + const int16_t* const coeffs = block->coeffs_; + uint32_t bits = block->non_zero_y_; + int n; + + if (mb_y > 0) { + memcpy(y_dst - BPS, top_yuv[0].y, 16); + memcpy(u_dst - BPS, top_yuv[0].u, 8); + memcpy(v_dst - BPS, top_yuv[0].v, 8); + } + + // predict and add residuals + if (block->is_i4x4_) { // 4x4 + uint32_t* const top_right = (uint32_t*)(y_dst - BPS + 16); + + if (mb_y > 0) { + if (mb_x >= dec->mb_w_ - 1) { // on rightmost border + memset(top_right, top_yuv[0].y[15], sizeof(*top_right)); + } else { + memcpy(top_right, top_yuv[1].y, sizeof(*top_right)); + } + } + // replicate the top-right pixels below + top_right[BPS] = top_right[2 * BPS] = top_right[3 * BPS] = top_right[0]; + + // predict and add residuals for all 4x4 blocks in turn. + for (n = 0; n < 16; ++n, bits <<= 2) { + uint8_t* const dst = y_dst + kScan[n]; + VP8PredLuma4[block->imodes_[n]](dst); + DoTransform(bits, coeffs + n * 16, dst); + } + } else { // 16x16 + const int pred_func = CheckMode(mb_x, mb_y, block->imodes_[0]); + VP8PredLuma16[pred_func](y_dst); + if (bits != 0) { + for (n = 0; n < 16; ++n, bits <<= 2) { + DoTransform(bits, coeffs + n * 16, y_dst + kScan[n]); + } + } + } + { + // Chroma + const uint32_t bits_uv = block->non_zero_uv_; + const int pred_func = CheckMode(mb_x, mb_y, block->uvmode_); + VP8PredChroma8[pred_func](u_dst); + VP8PredChroma8[pred_func](v_dst); + DoUVTransform(bits_uv >> 0, coeffs + 16 * 16, u_dst); + DoUVTransform(bits_uv >> 8, coeffs + 20 * 16, v_dst); + } + + // stash away top samples for next block + if (mb_y < dec->mb_h_ - 1) { + memcpy(top_yuv[0].y, y_dst + 15 * BPS, 16); + memcpy(top_yuv[0].u, u_dst + 7 * BPS, 8); + memcpy(top_yuv[0].v, v_dst + 7 * BPS, 8); + } + } + // Transfer reconstructed samples from yuv_b_ cache to final destination. + { + const int y_offset = cache_id * 16 * dec->cache_y_stride_; + const int uv_offset = cache_id * 8 * dec->cache_uv_stride_; + uint8_t* const y_out = dec->cache_y_ + mb_x * 16 + y_offset; + uint8_t* const u_out = dec->cache_u_ + mb_x * 8 + uv_offset; + uint8_t* const v_out = dec->cache_v_ + mb_x * 8 + uv_offset; + for (j = 0; j < 16; ++j) { + memcpy(y_out + j * dec->cache_y_stride_, y_dst + j * BPS, 16); + } + for (j = 0; j < 8; ++j) { + memcpy(u_out + j * dec->cache_uv_stride_, u_dst + j * BPS, 8); + memcpy(v_out + j * dec->cache_uv_stride_, v_dst + j * BPS, 8); + } + } + } +} + +//------------------------------------------------------------------------------ +// Filtering + +// kFilterExtraRows[] = How many extra lines are needed on the MB boundary +// for caching, given a filtering level. +// Simple filter: up to 2 luma samples are read and 1 is written. +// Complex filter: up to 4 luma samples are read and 3 are written. Same for +// U/V, so it's 8 samples total (because of the 2x upsampling). +static const uint8_t kFilterExtraRows[3] = { 0, 2, 8 }; + +static void DoFilter(const VP8Decoder* const dec, int mb_x, int mb_y) { + const VP8ThreadContext* const ctx = &dec->thread_ctx_; + const int cache_id = ctx->id_; + const int y_bps = dec->cache_y_stride_; + const VP8FInfo* const f_info = ctx->f_info_ + mb_x; + uint8_t* const y_dst = dec->cache_y_ + cache_id * 16 * y_bps + mb_x * 16; + const int ilevel = f_info->f_ilevel_; + const int limit = f_info->f_limit_; + if (limit == 0) { + return; + } + assert(limit >= 3); + if (dec->filter_type_ == 1) { // simple + if (mb_x > 0) { + VP8SimpleHFilter16(y_dst, y_bps, limit + 4); + } + if (f_info->f_inner_) { + VP8SimpleHFilter16i(y_dst, y_bps, limit); + } + if (mb_y > 0) { + VP8SimpleVFilter16(y_dst, y_bps, limit + 4); + } + if (f_info->f_inner_) { + VP8SimpleVFilter16i(y_dst, y_bps, limit); + } + } else { // complex + const int uv_bps = dec->cache_uv_stride_; + uint8_t* const u_dst = dec->cache_u_ + cache_id * 8 * uv_bps + mb_x * 8; + uint8_t* const v_dst = dec->cache_v_ + cache_id * 8 * uv_bps + mb_x * 8; + const int hev_thresh = f_info->hev_thresh_; + if (mb_x > 0) { + VP8HFilter16(y_dst, y_bps, limit + 4, ilevel, hev_thresh); + VP8HFilter8(u_dst, v_dst, uv_bps, limit + 4, ilevel, hev_thresh); + } + if (f_info->f_inner_) { + VP8HFilter16i(y_dst, y_bps, limit, ilevel, hev_thresh); + VP8HFilter8i(u_dst, v_dst, uv_bps, limit, ilevel, hev_thresh); + } + if (mb_y > 0) { + VP8VFilter16(y_dst, y_bps, limit + 4, ilevel, hev_thresh); + VP8VFilter8(u_dst, v_dst, uv_bps, limit + 4, ilevel, hev_thresh); + } + if (f_info->f_inner_) { + VP8VFilter16i(y_dst, y_bps, limit, ilevel, hev_thresh); + VP8VFilter8i(u_dst, v_dst, uv_bps, limit, ilevel, hev_thresh); + } + } +} + +// Filter the decoded macroblock row (if needed) +static void FilterRow(const VP8Decoder* const dec) { + int mb_x; + const int mb_y = dec->thread_ctx_.mb_y_; + assert(dec->thread_ctx_.filter_row_); + for (mb_x = dec->tl_mb_x_; mb_x < dec->br_mb_x_; ++mb_x) { + DoFilter(dec, mb_x, mb_y); + } +} + +//------------------------------------------------------------------------------ +// Precompute the filtering strength for each segment and each i4x4/i16x16 mode. + +static void PrecomputeFilterStrengths(VP8Decoder* const dec) { + if (dec->filter_type_ > 0) { + int s; + const VP8FilterHeader* const hdr = &dec->filter_hdr_; + for (s = 0; s < NUM_MB_SEGMENTS; ++s) { + int i4x4; + // First, compute the initial level + int base_level; + if (dec->segment_hdr_.use_segment_) { + base_level = dec->segment_hdr_.filter_strength_[s]; + if (!dec->segment_hdr_.absolute_delta_) { + base_level += hdr->level_; + } + } else { + base_level = hdr->level_; + } + for (i4x4 = 0; i4x4 <= 1; ++i4x4) { + VP8FInfo* const info = &dec->fstrengths_[s][i4x4]; + int level = base_level; + if (hdr->use_lf_delta_) { + level += hdr->ref_lf_delta_[0]; + if (i4x4) { + level += hdr->mode_lf_delta_[0]; + } + } + level = (level < 0) ? 0 : (level > 63) ? 63 : level; + if (level > 0) { + int ilevel = level; + if (hdr->sharpness_ > 0) { + if (hdr->sharpness_ > 4) { + ilevel >>= 2; + } else { + ilevel >>= 1; + } + if (ilevel > 9 - hdr->sharpness_) { + ilevel = 9 - hdr->sharpness_; + } + } + if (ilevel < 1) ilevel = 1; + info->f_ilevel_ = ilevel; + info->f_limit_ = 2 * level + ilevel; + info->hev_thresh_ = (level >= 40) ? 2 : (level >= 15) ? 1 : 0; + } else { + info->f_limit_ = 0; // no filtering + } + info->f_inner_ = i4x4; + } + } + } +} + +//------------------------------------------------------------------------------ +// Dithering + +// minimal amp that will provide a non-zero dithering effect +#define MIN_DITHER_AMP 4 + +#define DITHER_AMP_TAB_SIZE 12 +static const int kQuantToDitherAmp[DITHER_AMP_TAB_SIZE] = { + // roughly, it's dqm->uv_mat_[1] + 8, 7, 6, 4, 4, 2, 2, 2, 1, 1, 1, 1 +}; + +void VP8InitDithering(const WebPDecoderOptions* const options, + VP8Decoder* const dec) { + assert(dec != NULL); + if (options != NULL) { + const int d = options->dithering_strength; + const int max_amp = (1 << VP8_RANDOM_DITHER_FIX) - 1; + const int f = (d < 0) ? 0 : (d > 100) ? max_amp : (d * max_amp / 100); + if (f > 0) { + int s; + int all_amp = 0; + for (s = 0; s < NUM_MB_SEGMENTS; ++s) { + VP8QuantMatrix* const dqm = &dec->dqm_[s]; + if (dqm->uv_quant_ < DITHER_AMP_TAB_SIZE) { + // TODO(skal): should we specially dither more for uv_quant_ < 0? + const int idx = (dqm->uv_quant_ < 0) ? 0 : dqm->uv_quant_; + dqm->dither_ = (f * kQuantToDitherAmp[idx]) >> 3; + } + all_amp |= dqm->dither_; + } + if (all_amp != 0) { + VP8InitRandom(&dec->dithering_rg_, 1.0f); + dec->dither_ = 1; + } + } + // potentially allow alpha dithering + dec->alpha_dithering_ = options->alpha_dithering_strength; + if (dec->alpha_dithering_ > 100) { + dec->alpha_dithering_ = 100; + } else if (dec->alpha_dithering_ < 0) { + dec->alpha_dithering_ = 0; + } + } +} + +// Convert to range: [-2,2] for dither=50, [-4,4] for dither=100 +static void Dither8x8(VP8Random* const rg, uint8_t* dst, int bps, int amp) { + uint8_t dither[64]; + int i; + for (i = 0; i < 8 * 8; ++i) { + dither[i] = VP8RandomBits2(rg, VP8_DITHER_AMP_BITS + 1, amp); + } + VP8DitherCombine8x8(dither, dst, bps); +} + +static void DitherRow(VP8Decoder* const dec) { + int mb_x; + assert(dec->dither_); + for (mb_x = dec->tl_mb_x_; mb_x < dec->br_mb_x_; ++mb_x) { + const VP8ThreadContext* const ctx = &dec->thread_ctx_; + const VP8MBData* const data = ctx->mb_data_ + mb_x; + const int cache_id = ctx->id_; + const int uv_bps = dec->cache_uv_stride_; + if (data->dither_ >= MIN_DITHER_AMP) { + uint8_t* const u_dst = dec->cache_u_ + cache_id * 8 * uv_bps + mb_x * 8; + uint8_t* const v_dst = dec->cache_v_ + cache_id * 8 * uv_bps + mb_x * 8; + Dither8x8(&dec->dithering_rg_, u_dst, uv_bps, data->dither_); + Dither8x8(&dec->dithering_rg_, v_dst, uv_bps, data->dither_); + } + } +} + +//------------------------------------------------------------------------------ +// This function is called after a row of macroblocks is finished decoding. +// It also takes into account the following restrictions: +// * In case of in-loop filtering, we must hold off sending some of the bottom +// pixels as they are yet unfiltered. They will be when the next macroblock +// row is decoded. Meanwhile, we must preserve them by rotating them in the +// cache area. This doesn't hold for the very bottom row of the uncropped +// picture of course. +// * we must clip the remaining pixels against the cropping area. The VP8Io +// struct must have the following fields set correctly before calling put(): + +#define MACROBLOCK_VPOS(mb_y) ((mb_y) * 16) // vertical position of a MB + +// Finalize and transmit a complete row. Return false in case of user-abort. +static int FinishRow(VP8Decoder* const dec, VP8Io* const io) { + int ok = 1; + const VP8ThreadContext* const ctx = &dec->thread_ctx_; + const int cache_id = ctx->id_; + const int extra_y_rows = kFilterExtraRows[dec->filter_type_]; + const int ysize = extra_y_rows * dec->cache_y_stride_; + const int uvsize = (extra_y_rows / 2) * dec->cache_uv_stride_; + const int y_offset = cache_id * 16 * dec->cache_y_stride_; + const int uv_offset = cache_id * 8 * dec->cache_uv_stride_; + uint8_t* const ydst = dec->cache_y_ - ysize + y_offset; + uint8_t* const udst = dec->cache_u_ - uvsize + uv_offset; + uint8_t* const vdst = dec->cache_v_ - uvsize + uv_offset; + const int mb_y = ctx->mb_y_; + const int is_first_row = (mb_y == 0); + const int is_last_row = (mb_y >= dec->br_mb_y_ - 1); + + if (dec->mt_method_ == 2) { + ReconstructRow(dec, ctx); + } + + if (ctx->filter_row_) { + FilterRow(dec); + } + + if (dec->dither_) { + DitherRow(dec); + } + + if (io->put != NULL) { + int y_start = MACROBLOCK_VPOS(mb_y); + int y_end = MACROBLOCK_VPOS(mb_y + 1); + if (!is_first_row) { + y_start -= extra_y_rows; + io->y = ydst; + io->u = udst; + io->v = vdst; + } else { + io->y = dec->cache_y_ + y_offset; + io->u = dec->cache_u_ + uv_offset; + io->v = dec->cache_v_ + uv_offset; + } + + if (!is_last_row) { + y_end -= extra_y_rows; + } + if (y_end > io->crop_bottom) { + y_end = io->crop_bottom; // make sure we don't overflow on last row. + } + io->a = NULL; + if (dec->alpha_data_ != NULL && y_start < y_end) { + // TODO(skal): testing presence of alpha with dec->alpha_data_ is not a + // good idea. + io->a = VP8DecompressAlphaRows(dec, io, y_start, y_end - y_start); + if (io->a == NULL) { + return VP8SetError(dec, VP8_STATUS_BITSTREAM_ERROR, + "Could not decode alpha data."); + } + } + if (y_start < io->crop_top) { + const int delta_y = io->crop_top - y_start; + y_start = io->crop_top; + assert(!(delta_y & 1)); + io->y += dec->cache_y_stride_ * delta_y; + io->u += dec->cache_uv_stride_ * (delta_y >> 1); + io->v += dec->cache_uv_stride_ * (delta_y >> 1); + if (io->a != NULL) { + io->a += io->width * delta_y; + } + } + if (y_start < y_end) { + io->y += io->crop_left; + io->u += io->crop_left >> 1; + io->v += io->crop_left >> 1; + if (io->a != NULL) { + io->a += io->crop_left; + } + io->mb_y = y_start - io->crop_top; + io->mb_w = io->crop_right - io->crop_left; + io->mb_h = y_end - y_start; + ok = io->put(io); + } + } + // rotate top samples if needed + if (cache_id + 1 == dec->num_caches_) { + if (!is_last_row) { + memcpy(dec->cache_y_ - ysize, ydst + 16 * dec->cache_y_stride_, ysize); + memcpy(dec->cache_u_ - uvsize, udst + 8 * dec->cache_uv_stride_, uvsize); + memcpy(dec->cache_v_ - uvsize, vdst + 8 * dec->cache_uv_stride_, uvsize); + } + } + + return ok; +} + +#undef MACROBLOCK_VPOS + +//------------------------------------------------------------------------------ + +int VP8ProcessRow(VP8Decoder* const dec, VP8Io* const io) { + int ok = 1; + VP8ThreadContext* const ctx = &dec->thread_ctx_; + const int filter_row = + (dec->filter_type_ > 0) && + (dec->mb_y_ >= dec->tl_mb_y_) && (dec->mb_y_ <= dec->br_mb_y_); + if (dec->mt_method_ == 0) { + // ctx->id_ and ctx->f_info_ are already set + ctx->mb_y_ = dec->mb_y_; + ctx->filter_row_ = filter_row; + ReconstructRow(dec, ctx); + ok = FinishRow(dec, io); + } else { + WebPWorker* const worker = &dec->worker_; + // Finish previous job *before* updating context + ok &= WebPGetWorkerInterface()->Sync(worker); + assert(worker->status_ == OK); + if (ok) { // spawn a new deblocking/output job + ctx->io_ = *io; + ctx->id_ = dec->cache_id_; + ctx->mb_y_ = dec->mb_y_; + ctx->filter_row_ = filter_row; + if (dec->mt_method_ == 2) { // swap macroblock data + VP8MBData* const tmp = ctx->mb_data_; + ctx->mb_data_ = dec->mb_data_; + dec->mb_data_ = tmp; + } else { + // perform reconstruction directly in main thread + ReconstructRow(dec, ctx); + } + if (filter_row) { // swap filter info + VP8FInfo* const tmp = ctx->f_info_; + ctx->f_info_ = dec->f_info_; + dec->f_info_ = tmp; + } + // (reconstruct)+filter in parallel + WebPGetWorkerInterface()->Launch(worker); + if (++dec->cache_id_ == dec->num_caches_) { + dec->cache_id_ = 0; + } + } + } + return ok; +} + +//------------------------------------------------------------------------------ +// Finish setting up the decoding parameter once user's setup() is called. + +VP8StatusCode VP8EnterCritical(VP8Decoder* const dec, VP8Io* const io) { + // Call setup() first. This may trigger additional decoding features on 'io'. + // Note: Afterward, we must call teardown() no matter what. + if (io->setup != NULL && !io->setup(io)) { + VP8SetError(dec, VP8_STATUS_USER_ABORT, "Frame setup failed"); + return dec->status_; + } + + // Disable filtering per user request + if (io->bypass_filtering) { + dec->filter_type_ = 0; + } + // TODO(skal): filter type / strength / sharpness forcing + + // Define the area where we can skip in-loop filtering, in case of cropping. + // + // 'Simple' filter reads two luma samples outside of the macroblock + // and filters one. It doesn't filter the chroma samples. Hence, we can + // avoid doing the in-loop filtering before crop_top/crop_left position. + // For the 'Complex' filter, 3 samples are read and up to 3 are filtered. + // Means: there's a dependency chain that goes all the way up to the + // top-left corner of the picture (MB #0). We must filter all the previous + // macroblocks. + // TODO(skal): add an 'approximate_decoding' option, that won't produce + // a 1:1 bit-exactness for complex filtering? + { + const int extra_pixels = kFilterExtraRows[dec->filter_type_]; + if (dec->filter_type_ == 2) { + // For complex filter, we need to preserve the dependency chain. + dec->tl_mb_x_ = 0; + dec->tl_mb_y_ = 0; + } else { + // For simple filter, we can filter only the cropped region. + // We include 'extra_pixels' on the other side of the boundary, since + // vertical or horizontal filtering of the previous macroblock can + // modify some abutting pixels. + dec->tl_mb_x_ = (io->crop_left - extra_pixels) >> 4; + dec->tl_mb_y_ = (io->crop_top - extra_pixels) >> 4; + if (dec->tl_mb_x_ < 0) dec->tl_mb_x_ = 0; + if (dec->tl_mb_y_ < 0) dec->tl_mb_y_ = 0; + } + // We need some 'extra' pixels on the right/bottom. + dec->br_mb_y_ = (io->crop_bottom + 15 + extra_pixels) >> 4; + dec->br_mb_x_ = (io->crop_right + 15 + extra_pixels) >> 4; + if (dec->br_mb_x_ > dec->mb_w_) { + dec->br_mb_x_ = dec->mb_w_; + } + if (dec->br_mb_y_ > dec->mb_h_) { + dec->br_mb_y_ = dec->mb_h_; + } + } + PrecomputeFilterStrengths(dec); + return VP8_STATUS_OK; +} + +int VP8ExitCritical(VP8Decoder* const dec, VP8Io* const io) { + int ok = 1; + if (dec->mt_method_ > 0) { + ok = WebPGetWorkerInterface()->Sync(&dec->worker_); + } + + if (io->teardown != NULL) { + io->teardown(io); + } + return ok; +} + +//------------------------------------------------------------------------------ +// For multi-threaded decoding we need to use 3 rows of 16 pixels as delay line. +// +// Reason is: the deblocking filter cannot deblock the bottom horizontal edges +// immediately, and needs to wait for first few rows of the next macroblock to +// be decoded. Hence, deblocking is lagging behind by 4 or 8 pixels (depending +// on strength). +// With two threads, the vertical positions of the rows being decoded are: +// Decode: [ 0..15][16..31][32..47][48..63][64..79][... +// Deblock: [ 0..11][12..27][28..43][44..59][... +// If we use two threads and two caches of 16 pixels, the sequence would be: +// Decode: [ 0..15][16..31][ 0..15!!][16..31][ 0..15][... +// Deblock: [ 0..11][12..27!!][-4..11][12..27][... +// The problem occurs during row [12..15!!] that both the decoding and +// deblocking threads are writing simultaneously. +// With 3 cache lines, one get a safe write pattern: +// Decode: [ 0..15][16..31][32..47][ 0..15][16..31][32..47][0.. +// Deblock: [ 0..11][12..27][28..43][-4..11][12..27][28... +// Note that multi-threaded output _without_ deblocking can make use of two +// cache lines of 16 pixels only, since there's no lagging behind. The decoding +// and output process have non-concurrent writing: +// Decode: [ 0..15][16..31][ 0..15][16..31][... +// io->put: [ 0..15][16..31][ 0..15][... + +#define MT_CACHE_LINES 3 +#define ST_CACHE_LINES 1 // 1 cache row only for single-threaded case + +// Initialize multi/single-thread worker +static int InitThreadContext(VP8Decoder* const dec) { + dec->cache_id_ = 0; + if (dec->mt_method_ > 0) { + WebPWorker* const worker = &dec->worker_; + if (!WebPGetWorkerInterface()->Reset(worker)) { + return VP8SetError(dec, VP8_STATUS_OUT_OF_MEMORY, + "thread initialization failed."); + } + worker->data1 = dec; + worker->data2 = (void*)&dec->thread_ctx_.io_; + worker->hook = (WebPWorkerHook)FinishRow; + dec->num_caches_ = + (dec->filter_type_ > 0) ? MT_CACHE_LINES : MT_CACHE_LINES - 1; + } else { + dec->num_caches_ = ST_CACHE_LINES; + } + return 1; +} + +int VP8GetThreadMethod(const WebPDecoderOptions* const options, + const WebPHeaderStructure* const headers, + int width, int height) { + if (options == NULL || options->use_threads == 0) { + return 0; + } + (void)headers; + (void)width; + (void)height; + assert(headers == NULL || !headers->is_lossless); +#if defined(WEBP_USE_THREAD) + if (width < MIN_WIDTH_FOR_THREADS) return 0; + // TODO(skal): tune the heuristic further +#if 0 + if (height < 2 * width) return 2; +#endif + return 2; +#else // !WEBP_USE_THREAD + return 0; +#endif +} + +#undef MT_CACHE_LINES +#undef ST_CACHE_LINES + +//------------------------------------------------------------------------------ +// Memory setup + +static int AllocateMemory(VP8Decoder* const dec) { + const int num_caches = dec->num_caches_; + const int mb_w = dec->mb_w_; + // Note: we use 'size_t' when there's no overflow risk, uint64_t otherwise. + const size_t intra_pred_mode_size = 4 * mb_w * sizeof(uint8_t); + const size_t top_size = sizeof(VP8TopSamples) * mb_w; + const size_t mb_info_size = (mb_w + 1) * sizeof(VP8MB); + const size_t f_info_size = + (dec->filter_type_ > 0) ? + mb_w * (dec->mt_method_ > 0 ? 2 : 1) * sizeof(VP8FInfo) + : 0; + const size_t yuv_size = YUV_SIZE * sizeof(*dec->yuv_b_); + const size_t mb_data_size = + (dec->mt_method_ == 2 ? 2 : 1) * mb_w * sizeof(*dec->mb_data_); + const size_t cache_height = (16 * num_caches + + kFilterExtraRows[dec->filter_type_]) * 3 / 2; + const size_t cache_size = top_size * cache_height; + // alpha_size is the only one that scales as width x height. + const uint64_t alpha_size = (dec->alpha_data_ != NULL) ? + (uint64_t)dec->pic_hdr_.width_ * dec->pic_hdr_.height_ : 0ULL; + const uint64_t needed = (uint64_t)intra_pred_mode_size + + top_size + mb_info_size + f_info_size + + yuv_size + mb_data_size + + cache_size + alpha_size + WEBP_ALIGN_CST; + uint8_t* mem; + + if (needed != (size_t)needed) return 0; // check for overflow + if (needed > dec->mem_size_) { + WebPSafeFree(dec->mem_); + dec->mem_size_ = 0; + dec->mem_ = WebPSafeMalloc(needed, sizeof(uint8_t)); + if (dec->mem_ == NULL) { + return VP8SetError(dec, VP8_STATUS_OUT_OF_MEMORY, + "no memory during frame initialization."); + } + // down-cast is ok, thanks to WebPSafeMalloc() above. + dec->mem_size_ = (size_t)needed; + } + + mem = (uint8_t*)dec->mem_; + dec->intra_t_ = (uint8_t*)mem; + mem += intra_pred_mode_size; + + dec->yuv_t_ = (VP8TopSamples*)mem; + mem += top_size; + + dec->mb_info_ = ((VP8MB*)mem) + 1; + mem += mb_info_size; + + dec->f_info_ = f_info_size ? (VP8FInfo*)mem : NULL; + mem += f_info_size; + dec->thread_ctx_.id_ = 0; + dec->thread_ctx_.f_info_ = dec->f_info_; + if (dec->mt_method_ > 0) { + // secondary cache line. The deblocking process need to make use of the + // filtering strength from previous macroblock row, while the new ones + // are being decoded in parallel. We'll just swap the pointers. + dec->thread_ctx_.f_info_ += mb_w; + } + + mem = (uint8_t*)WEBP_ALIGN(mem); + assert((yuv_size & WEBP_ALIGN_CST) == 0); + dec->yuv_b_ = (uint8_t*)mem; + mem += yuv_size; + + dec->mb_data_ = (VP8MBData*)mem; + dec->thread_ctx_.mb_data_ = (VP8MBData*)mem; + if (dec->mt_method_ == 2) { + dec->thread_ctx_.mb_data_ += mb_w; + } + mem += mb_data_size; + + dec->cache_y_stride_ = 16 * mb_w; + dec->cache_uv_stride_ = 8 * mb_w; + { + const int extra_rows = kFilterExtraRows[dec->filter_type_]; + const int extra_y = extra_rows * dec->cache_y_stride_; + const int extra_uv = (extra_rows / 2) * dec->cache_uv_stride_; + dec->cache_y_ = ((uint8_t*)mem) + extra_y; + dec->cache_u_ = dec->cache_y_ + + 16 * num_caches * dec->cache_y_stride_ + extra_uv; + dec->cache_v_ = dec->cache_u_ + + 8 * num_caches * dec->cache_uv_stride_ + extra_uv; + dec->cache_id_ = 0; + } + mem += cache_size; + + // alpha plane + dec->alpha_plane_ = alpha_size ? (uint8_t*)mem : NULL; + mem += alpha_size; + assert(mem <= (uint8_t*)dec->mem_ + dec->mem_size_); + + // note: left/top-info is initialized once for all. + memset(dec->mb_info_ - 1, 0, mb_info_size); + VP8InitScanline(dec); // initialize left too. + + // initialize top + memset(dec->intra_t_, B_DC_PRED, intra_pred_mode_size); + + return 1; +} + +static void InitIo(VP8Decoder* const dec, VP8Io* io) { + // prepare 'io' + io->mb_y = 0; + io->y = dec->cache_y_; + io->u = dec->cache_u_; + io->v = dec->cache_v_; + io->y_stride = dec->cache_y_stride_; + io->uv_stride = dec->cache_uv_stride_; + io->a = NULL; +} + +int VP8InitFrame(VP8Decoder* const dec, VP8Io* const io) { + if (!InitThreadContext(dec)) return 0; // call first. Sets dec->num_caches_. + if (!AllocateMemory(dec)) return 0; + InitIo(dec, io); + VP8DspInit(); // Init critical function pointers and look-up tables. + return 1; +} + +//------------------------------------------------------------------------------ diff --git a/thirdparty/libwebp/dec/idec.c b/thirdparty/libwebp/dec/idec.c deleted file mode 100644 index 8de131916e..0000000000 --- a/thirdparty/libwebp/dec/idec.c +++ /dev/null @@ -1,892 +0,0 @@ -// Copyright 2011 Google Inc. All Rights Reserved. -// -// Use of this source code is governed by a BSD-style license -// that can be found in the COPYING file in the root of the source -// tree. An additional intellectual property rights grant can be found -// in the file PATENTS. All contributing project authors may -// be found in the AUTHORS file in the root of the source tree. -// ----------------------------------------------------------------------------- -// -// Incremental decoding -// -// Author: somnath@google.com (Somnath Banerjee) - -#include -#include -#include - -#include "./alphai.h" -#include "./webpi.h" -#include "./vp8i.h" -#include "../utils/utils.h" - -// In append mode, buffer allocations increase as multiples of this value. -// Needs to be a power of 2. -#define CHUNK_SIZE 4096 -#define MAX_MB_SIZE 4096 - -//------------------------------------------------------------------------------ -// Data structures for memory and states - -// Decoding states. State normally flows as: -// WEBP_HEADER->VP8_HEADER->VP8_PARTS0->VP8_DATA->DONE for a lossy image, and -// WEBP_HEADER->VP8L_HEADER->VP8L_DATA->DONE for a lossless image. -// If there is any error the decoder goes into state ERROR. -typedef enum { - STATE_WEBP_HEADER, // All the data before that of the VP8/VP8L chunk. - STATE_VP8_HEADER, // The VP8 Frame header (within the VP8 chunk). - STATE_VP8_PARTS0, - STATE_VP8_DATA, - STATE_VP8L_HEADER, - STATE_VP8L_DATA, - STATE_DONE, - STATE_ERROR -} DecState; - -// Operating state for the MemBuffer -typedef enum { - MEM_MODE_NONE = 0, - MEM_MODE_APPEND, - MEM_MODE_MAP -} MemBufferMode; - -// storage for partition #0 and partial data (in a rolling fashion) -typedef struct { - MemBufferMode mode_; // Operation mode - size_t start_; // start location of the data to be decoded - size_t end_; // end location - size_t buf_size_; // size of the allocated buffer - uint8_t* buf_; // We don't own this buffer in case WebPIUpdate() - - size_t part0_size_; // size of partition #0 - const uint8_t* part0_buf_; // buffer to store partition #0 -} MemBuffer; - -struct WebPIDecoder { - DecState state_; // current decoding state - WebPDecParams params_; // Params to store output info - int is_lossless_; // for down-casting 'dec_'. - void* dec_; // either a VP8Decoder or a VP8LDecoder instance - VP8Io io_; - - MemBuffer mem_; // input memory buffer. - WebPDecBuffer output_; // output buffer (when no external one is supplied, - // or if the external one has slow-memory) - WebPDecBuffer* final_output_; // Slow-memory output to copy to eventually. - size_t chunk_size_; // Compressed VP8/VP8L size extracted from Header. - - int last_mb_y_; // last row reached for intra-mode decoding -}; - -// MB context to restore in case VP8DecodeMB() fails -typedef struct { - VP8MB left_; - VP8MB info_; - VP8BitReader token_br_; -} MBContext; - -//------------------------------------------------------------------------------ -// MemBuffer: incoming data handling - -static WEBP_INLINE size_t MemDataSize(const MemBuffer* mem) { - return (mem->end_ - mem->start_); -} - -// Check if we need to preserve the compressed alpha data, as it may not have -// been decoded yet. -static int NeedCompressedAlpha(const WebPIDecoder* const idec) { - if (idec->state_ == STATE_WEBP_HEADER) { - // We haven't parsed the headers yet, so we don't know whether the image is - // lossy or lossless. This also means that we haven't parsed the ALPH chunk. - return 0; - } - if (idec->is_lossless_) { - return 0; // ALPH chunk is not present for lossless images. - } else { - const VP8Decoder* const dec = (VP8Decoder*)idec->dec_; - assert(dec != NULL); // Must be true as idec->state_ != STATE_WEBP_HEADER. - return (dec->alpha_data_ != NULL) && !dec->is_alpha_decoded_; - } -} - -static void DoRemap(WebPIDecoder* const idec, ptrdiff_t offset) { - MemBuffer* const mem = &idec->mem_; - const uint8_t* const new_base = mem->buf_ + mem->start_; - // note: for VP8, setting up idec->io_ is only really needed at the beginning - // of the decoding, till partition #0 is complete. - idec->io_.data = new_base; - idec->io_.data_size = MemDataSize(mem); - - if (idec->dec_ != NULL) { - if (!idec->is_lossless_) { - VP8Decoder* const dec = (VP8Decoder*)idec->dec_; - const uint32_t last_part = dec->num_parts_minus_one_; - if (offset != 0) { - uint32_t p; - for (p = 0; p <= last_part; ++p) { - VP8RemapBitReader(dec->parts_ + p, offset); - } - // Remap partition #0 data pointer to new offset, but only in MAP - // mode (in APPEND mode, partition #0 is copied into a fixed memory). - if (mem->mode_ == MEM_MODE_MAP) { - VP8RemapBitReader(&dec->br_, offset); - } - } - { - const uint8_t* const last_start = dec->parts_[last_part].buf_; - VP8BitReaderSetBuffer(&dec->parts_[last_part], last_start, - mem->buf_ + mem->end_ - last_start); - } - if (NeedCompressedAlpha(idec)) { - ALPHDecoder* const alph_dec = dec->alph_dec_; - dec->alpha_data_ += offset; - if (alph_dec != NULL) { - if (alph_dec->method_ == ALPHA_LOSSLESS_COMPRESSION) { - VP8LDecoder* const alph_vp8l_dec = alph_dec->vp8l_dec_; - assert(alph_vp8l_dec != NULL); - assert(dec->alpha_data_size_ >= ALPHA_HEADER_LEN); - VP8LBitReaderSetBuffer(&alph_vp8l_dec->br_, - dec->alpha_data_ + ALPHA_HEADER_LEN, - dec->alpha_data_size_ - ALPHA_HEADER_LEN); - } else { // alph_dec->method_ == ALPHA_NO_COMPRESSION - // Nothing special to do in this case. - } - } - } - } else { // Resize lossless bitreader - VP8LDecoder* const dec = (VP8LDecoder*)idec->dec_; - VP8LBitReaderSetBuffer(&dec->br_, new_base, MemDataSize(mem)); - } - } -} - -// Appends data to the end of MemBuffer->buf_. It expands the allocated memory -// size if required and also updates VP8BitReader's if new memory is allocated. -static int AppendToMemBuffer(WebPIDecoder* const idec, - const uint8_t* const data, size_t data_size) { - VP8Decoder* const dec = (VP8Decoder*)idec->dec_; - MemBuffer* const mem = &idec->mem_; - const int need_compressed_alpha = NeedCompressedAlpha(idec); - const uint8_t* const old_start = mem->buf_ + mem->start_; - const uint8_t* const old_base = - need_compressed_alpha ? dec->alpha_data_ : old_start; - assert(mem->mode_ == MEM_MODE_APPEND); - if (data_size > MAX_CHUNK_PAYLOAD) { - // security safeguard: trying to allocate more than what the format - // allows for a chunk should be considered a smoke smell. - return 0; - } - - if (mem->end_ + data_size > mem->buf_size_) { // Need some free memory - const size_t new_mem_start = old_start - old_base; - const size_t current_size = MemDataSize(mem) + new_mem_start; - const uint64_t new_size = (uint64_t)current_size + data_size; - const uint64_t extra_size = (new_size + CHUNK_SIZE - 1) & ~(CHUNK_SIZE - 1); - uint8_t* const new_buf = - (uint8_t*)WebPSafeMalloc(extra_size, sizeof(*new_buf)); - if (new_buf == NULL) return 0; - memcpy(new_buf, old_base, current_size); - WebPSafeFree(mem->buf_); - mem->buf_ = new_buf; - mem->buf_size_ = (size_t)extra_size; - mem->start_ = new_mem_start; - mem->end_ = current_size; - } - - memcpy(mem->buf_ + mem->end_, data, data_size); - mem->end_ += data_size; - assert(mem->end_ <= mem->buf_size_); - - DoRemap(idec, mem->buf_ + mem->start_ - old_start); - return 1; -} - -static int RemapMemBuffer(WebPIDecoder* const idec, - const uint8_t* const data, size_t data_size) { - MemBuffer* const mem = &idec->mem_; - const uint8_t* const old_buf = mem->buf_; - const uint8_t* const old_start = old_buf + mem->start_; - assert(mem->mode_ == MEM_MODE_MAP); - - if (data_size < mem->buf_size_) return 0; // can't remap to a shorter buffer! - - mem->buf_ = (uint8_t*)data; - mem->end_ = mem->buf_size_ = data_size; - - DoRemap(idec, mem->buf_ + mem->start_ - old_start); - return 1; -} - -static void InitMemBuffer(MemBuffer* const mem) { - mem->mode_ = MEM_MODE_NONE; - mem->buf_ = NULL; - mem->buf_size_ = 0; - mem->part0_buf_ = NULL; - mem->part0_size_ = 0; -} - -static void ClearMemBuffer(MemBuffer* const mem) { - assert(mem); - if (mem->mode_ == MEM_MODE_APPEND) { - WebPSafeFree(mem->buf_); - WebPSafeFree((void*)mem->part0_buf_); - } -} - -static int CheckMemBufferMode(MemBuffer* const mem, MemBufferMode expected) { - if (mem->mode_ == MEM_MODE_NONE) { - mem->mode_ = expected; // switch to the expected mode - } else if (mem->mode_ != expected) { - return 0; // we mixed the modes => error - } - assert(mem->mode_ == expected); // mode is ok - return 1; -} - -// To be called last. -static VP8StatusCode FinishDecoding(WebPIDecoder* const idec) { - const WebPDecoderOptions* const options = idec->params_.options; - WebPDecBuffer* const output = idec->params_.output; - - idec->state_ = STATE_DONE; - if (options != NULL && options->flip) { - const VP8StatusCode status = WebPFlipBuffer(output); - if (status != VP8_STATUS_OK) return status; - } - if (idec->final_output_ != NULL) { - WebPCopyDecBufferPixels(output, idec->final_output_); // do the slow-copy - WebPFreeDecBuffer(&idec->output_); - *output = *idec->final_output_; - idec->final_output_ = NULL; - } - return VP8_STATUS_OK; -} - -//------------------------------------------------------------------------------ -// Macroblock-decoding contexts - -static void SaveContext(const VP8Decoder* dec, const VP8BitReader* token_br, - MBContext* const context) { - context->left_ = dec->mb_info_[-1]; - context->info_ = dec->mb_info_[dec->mb_x_]; - context->token_br_ = *token_br; -} - -static void RestoreContext(const MBContext* context, VP8Decoder* const dec, - VP8BitReader* const token_br) { - dec->mb_info_[-1] = context->left_; - dec->mb_info_[dec->mb_x_] = context->info_; - *token_br = context->token_br_; -} - -//------------------------------------------------------------------------------ - -static VP8StatusCode IDecError(WebPIDecoder* const idec, VP8StatusCode error) { - if (idec->state_ == STATE_VP8_DATA) { - VP8Io* const io = &idec->io_; - if (io->teardown != NULL) { - io->teardown(io); - } - } - idec->state_ = STATE_ERROR; - return error; -} - -static void ChangeState(WebPIDecoder* const idec, DecState new_state, - size_t consumed_bytes) { - MemBuffer* const mem = &idec->mem_; - idec->state_ = new_state; - mem->start_ += consumed_bytes; - assert(mem->start_ <= mem->end_); - idec->io_.data = mem->buf_ + mem->start_; - idec->io_.data_size = MemDataSize(mem); -} - -// Headers -static VP8StatusCode DecodeWebPHeaders(WebPIDecoder* const idec) { - MemBuffer* const mem = &idec->mem_; - const uint8_t* data = mem->buf_ + mem->start_; - size_t curr_size = MemDataSize(mem); - VP8StatusCode status; - WebPHeaderStructure headers; - - headers.data = data; - headers.data_size = curr_size; - headers.have_all_data = 0; - status = WebPParseHeaders(&headers); - if (status == VP8_STATUS_NOT_ENOUGH_DATA) { - return VP8_STATUS_SUSPENDED; // We haven't found a VP8 chunk yet. - } else if (status != VP8_STATUS_OK) { - return IDecError(idec, status); - } - - idec->chunk_size_ = headers.compressed_size; - idec->is_lossless_ = headers.is_lossless; - if (!idec->is_lossless_) { - VP8Decoder* const dec = VP8New(); - if (dec == NULL) { - return VP8_STATUS_OUT_OF_MEMORY; - } - idec->dec_ = dec; - dec->alpha_data_ = headers.alpha_data; - dec->alpha_data_size_ = headers.alpha_data_size; - ChangeState(idec, STATE_VP8_HEADER, headers.offset); - } else { - VP8LDecoder* const dec = VP8LNew(); - if (dec == NULL) { - return VP8_STATUS_OUT_OF_MEMORY; - } - idec->dec_ = dec; - ChangeState(idec, STATE_VP8L_HEADER, headers.offset); - } - return VP8_STATUS_OK; -} - -static VP8StatusCode DecodeVP8FrameHeader(WebPIDecoder* const idec) { - const uint8_t* data = idec->mem_.buf_ + idec->mem_.start_; - const size_t curr_size = MemDataSize(&idec->mem_); - int width, height; - uint32_t bits; - - if (curr_size < VP8_FRAME_HEADER_SIZE) { - // Not enough data bytes to extract VP8 Frame Header. - return VP8_STATUS_SUSPENDED; - } - if (!VP8GetInfo(data, curr_size, idec->chunk_size_, &width, &height)) { - return IDecError(idec, VP8_STATUS_BITSTREAM_ERROR); - } - - bits = data[0] | (data[1] << 8) | (data[2] << 16); - idec->mem_.part0_size_ = (bits >> 5) + VP8_FRAME_HEADER_SIZE; - - idec->io_.data = data; - idec->io_.data_size = curr_size; - idec->state_ = STATE_VP8_PARTS0; - return VP8_STATUS_OK; -} - -// Partition #0 -static VP8StatusCode CopyParts0Data(WebPIDecoder* const idec) { - VP8Decoder* const dec = (VP8Decoder*)idec->dec_; - VP8BitReader* const br = &dec->br_; - const size_t part_size = br->buf_end_ - br->buf_; - MemBuffer* const mem = &idec->mem_; - assert(!idec->is_lossless_); - assert(mem->part0_buf_ == NULL); - // the following is a format limitation, no need for runtime check: - assert(part_size <= mem->part0_size_); - if (part_size == 0) { // can't have zero-size partition #0 - return VP8_STATUS_BITSTREAM_ERROR; - } - if (mem->mode_ == MEM_MODE_APPEND) { - // We copy and grab ownership of the partition #0 data. - uint8_t* const part0_buf = (uint8_t*)WebPSafeMalloc(1ULL, part_size); - if (part0_buf == NULL) { - return VP8_STATUS_OUT_OF_MEMORY; - } - memcpy(part0_buf, br->buf_, part_size); - mem->part0_buf_ = part0_buf; - VP8BitReaderSetBuffer(br, part0_buf, part_size); - } else { - // Else: just keep pointers to the partition #0's data in dec_->br_. - } - mem->start_ += part_size; - return VP8_STATUS_OK; -} - -static VP8StatusCode DecodePartition0(WebPIDecoder* const idec) { - VP8Decoder* const dec = (VP8Decoder*)idec->dec_; - VP8Io* const io = &idec->io_; - const WebPDecParams* const params = &idec->params_; - WebPDecBuffer* const output = params->output; - - // Wait till we have enough data for the whole partition #0 - if (MemDataSize(&idec->mem_) < idec->mem_.part0_size_) { - return VP8_STATUS_SUSPENDED; - } - - if (!VP8GetHeaders(dec, io)) { - const VP8StatusCode status = dec->status_; - if (status == VP8_STATUS_SUSPENDED || - status == VP8_STATUS_NOT_ENOUGH_DATA) { - // treating NOT_ENOUGH_DATA as SUSPENDED state - return VP8_STATUS_SUSPENDED; - } - return IDecError(idec, status); - } - - // Allocate/Verify output buffer now - dec->status_ = WebPAllocateDecBuffer(io->width, io->height, params->options, - output); - if (dec->status_ != VP8_STATUS_OK) { - return IDecError(idec, dec->status_); - } - // This change must be done before calling VP8InitFrame() - dec->mt_method_ = VP8GetThreadMethod(params->options, NULL, - io->width, io->height); - VP8InitDithering(params->options, dec); - - dec->status_ = CopyParts0Data(idec); - if (dec->status_ != VP8_STATUS_OK) { - return IDecError(idec, dec->status_); - } - - // Finish setting up the decoding parameters. Will call io->setup(). - if (VP8EnterCritical(dec, io) != VP8_STATUS_OK) { - return IDecError(idec, dec->status_); - } - - // Note: past this point, teardown() must always be called - // in case of error. - idec->state_ = STATE_VP8_DATA; - // Allocate memory and prepare everything. - if (!VP8InitFrame(dec, io)) { - return IDecError(idec, dec->status_); - } - return VP8_STATUS_OK; -} - -// Remaining partitions -static VP8StatusCode DecodeRemaining(WebPIDecoder* const idec) { - VP8Decoder* const dec = (VP8Decoder*)idec->dec_; - VP8Io* const io = &idec->io_; - - assert(dec->ready_); - for (; dec->mb_y_ < dec->mb_h_; ++dec->mb_y_) { - if (idec->last_mb_y_ != dec->mb_y_) { - if (!VP8ParseIntraModeRow(&dec->br_, dec)) { - // note: normally, error shouldn't occur since we already have the whole - // partition0 available here in DecodeRemaining(). Reaching EOF while - // reading intra modes really means a BITSTREAM_ERROR. - return IDecError(idec, VP8_STATUS_BITSTREAM_ERROR); - } - idec->last_mb_y_ = dec->mb_y_; - } - for (; dec->mb_x_ < dec->mb_w_; ++dec->mb_x_) { - VP8BitReader* const token_br = - &dec->parts_[dec->mb_y_ & dec->num_parts_minus_one_]; - MBContext context; - SaveContext(dec, token_br, &context); - if (!VP8DecodeMB(dec, token_br)) { - // We shouldn't fail when MAX_MB data was available - if (dec->num_parts_minus_one_ == 0 && - MemDataSize(&idec->mem_) > MAX_MB_SIZE) { - return IDecError(idec, VP8_STATUS_BITSTREAM_ERROR); - } - RestoreContext(&context, dec, token_br); - return VP8_STATUS_SUSPENDED; - } - // Release buffer only if there is only one partition - if (dec->num_parts_minus_one_ == 0) { - idec->mem_.start_ = token_br->buf_ - idec->mem_.buf_; - assert(idec->mem_.start_ <= idec->mem_.end_); - } - } - VP8InitScanline(dec); // Prepare for next scanline - - // Reconstruct, filter and emit the row. - if (!VP8ProcessRow(dec, io)) { - return IDecError(idec, VP8_STATUS_USER_ABORT); - } - } - // Synchronize the thread and check for errors. - if (!VP8ExitCritical(dec, io)) { - return IDecError(idec, VP8_STATUS_USER_ABORT); - } - dec->ready_ = 0; - return FinishDecoding(idec); -} - -static VP8StatusCode ErrorStatusLossless(WebPIDecoder* const idec, - VP8StatusCode status) { - if (status == VP8_STATUS_SUSPENDED || status == VP8_STATUS_NOT_ENOUGH_DATA) { - return VP8_STATUS_SUSPENDED; - } - return IDecError(idec, status); -} - -static VP8StatusCode DecodeVP8LHeader(WebPIDecoder* const idec) { - VP8Io* const io = &idec->io_; - VP8LDecoder* const dec = (VP8LDecoder*)idec->dec_; - const WebPDecParams* const params = &idec->params_; - WebPDecBuffer* const output = params->output; - size_t curr_size = MemDataSize(&idec->mem_); - assert(idec->is_lossless_); - - // Wait until there's enough data for decoding header. - if (curr_size < (idec->chunk_size_ >> 3)) { - dec->status_ = VP8_STATUS_SUSPENDED; - return ErrorStatusLossless(idec, dec->status_); - } - - if (!VP8LDecodeHeader(dec, io)) { - if (dec->status_ == VP8_STATUS_BITSTREAM_ERROR && - curr_size < idec->chunk_size_) { - dec->status_ = VP8_STATUS_SUSPENDED; - } - return ErrorStatusLossless(idec, dec->status_); - } - // Allocate/verify output buffer now. - dec->status_ = WebPAllocateDecBuffer(io->width, io->height, params->options, - output); - if (dec->status_ != VP8_STATUS_OK) { - return IDecError(idec, dec->status_); - } - - idec->state_ = STATE_VP8L_DATA; - return VP8_STATUS_OK; -} - -static VP8StatusCode DecodeVP8LData(WebPIDecoder* const idec) { - VP8LDecoder* const dec = (VP8LDecoder*)idec->dec_; - const size_t curr_size = MemDataSize(&idec->mem_); - assert(idec->is_lossless_); - - // Switch to incremental decoding if we don't have all the bytes available. - dec->incremental_ = (curr_size < idec->chunk_size_); - - if (!VP8LDecodeImage(dec)) { - return ErrorStatusLossless(idec, dec->status_); - } - assert(dec->status_ == VP8_STATUS_OK || dec->status_ == VP8_STATUS_SUSPENDED); - return (dec->status_ == VP8_STATUS_SUSPENDED) ? dec->status_ - : FinishDecoding(idec); -} - - // Main decoding loop -static VP8StatusCode IDecode(WebPIDecoder* idec) { - VP8StatusCode status = VP8_STATUS_SUSPENDED; - - if (idec->state_ == STATE_WEBP_HEADER) { - status = DecodeWebPHeaders(idec); - } else { - if (idec->dec_ == NULL) { - return VP8_STATUS_SUSPENDED; // can't continue if we have no decoder. - } - } - if (idec->state_ == STATE_VP8_HEADER) { - status = DecodeVP8FrameHeader(idec); - } - if (idec->state_ == STATE_VP8_PARTS0) { - status = DecodePartition0(idec); - } - if (idec->state_ == STATE_VP8_DATA) { - status = DecodeRemaining(idec); - } - if (idec->state_ == STATE_VP8L_HEADER) { - status = DecodeVP8LHeader(idec); - } - if (idec->state_ == STATE_VP8L_DATA) { - status = DecodeVP8LData(idec); - } - return status; -} - -//------------------------------------------------------------------------------ -// Internal constructor - -static WebPIDecoder* NewDecoder(WebPDecBuffer* const output_buffer, - const WebPBitstreamFeatures* const features) { - WebPIDecoder* idec = (WebPIDecoder*)WebPSafeCalloc(1ULL, sizeof(*idec)); - if (idec == NULL) { - return NULL; - } - - idec->state_ = STATE_WEBP_HEADER; - idec->chunk_size_ = 0; - - idec->last_mb_y_ = -1; - - InitMemBuffer(&idec->mem_); - WebPInitDecBuffer(&idec->output_); - VP8InitIo(&idec->io_); - - WebPResetDecParams(&idec->params_); - if (output_buffer == NULL || WebPAvoidSlowMemory(output_buffer, features)) { - idec->params_.output = &idec->output_; - idec->final_output_ = output_buffer; - if (output_buffer != NULL) { - idec->params_.output->colorspace = output_buffer->colorspace; - } - } else { - idec->params_.output = output_buffer; - idec->final_output_ = NULL; - } - WebPInitCustomIo(&idec->params_, &idec->io_); // Plug the I/O functions. - - return idec; -} - -//------------------------------------------------------------------------------ -// Public functions - -WebPIDecoder* WebPINewDecoder(WebPDecBuffer* output_buffer) { - return NewDecoder(output_buffer, NULL); -} - -WebPIDecoder* WebPIDecode(const uint8_t* data, size_t data_size, - WebPDecoderConfig* config) { - WebPIDecoder* idec; - WebPBitstreamFeatures tmp_features; - WebPBitstreamFeatures* const features = - (config == NULL) ? &tmp_features : &config->input; - memset(&tmp_features, 0, sizeof(tmp_features)); - - // Parse the bitstream's features, if requested: - if (data != NULL && data_size > 0) { - if (WebPGetFeatures(data, data_size, features) != VP8_STATUS_OK) { - return NULL; - } - } - - // Create an instance of the incremental decoder - idec = (config != NULL) ? NewDecoder(&config->output, features) - : NewDecoder(NULL, features); - if (idec == NULL) { - return NULL; - } - // Finish initialization - if (config != NULL) { - idec->params_.options = &config->options; - } - return idec; -} - -void WebPIDelete(WebPIDecoder* idec) { - if (idec == NULL) return; - if (idec->dec_ != NULL) { - if (!idec->is_lossless_) { - if (idec->state_ == STATE_VP8_DATA) { - // Synchronize the thread, clean-up and check for errors. - VP8ExitCritical((VP8Decoder*)idec->dec_, &idec->io_); - } - VP8Delete((VP8Decoder*)idec->dec_); - } else { - VP8LDelete((VP8LDecoder*)idec->dec_); - } - } - ClearMemBuffer(&idec->mem_); - WebPFreeDecBuffer(&idec->output_); - WebPSafeFree(idec); -} - -//------------------------------------------------------------------------------ -// Wrapper toward WebPINewDecoder - -WebPIDecoder* WebPINewRGB(WEBP_CSP_MODE mode, uint8_t* output_buffer, - size_t output_buffer_size, int output_stride) { - const int is_external_memory = (output_buffer != NULL) ? 1 : 0; - WebPIDecoder* idec; - - if (mode >= MODE_YUV) return NULL; - if (is_external_memory == 0) { // Overwrite parameters to sane values. - output_buffer_size = 0; - output_stride = 0; - } else { // A buffer was passed. Validate the other params. - if (output_stride == 0 || output_buffer_size == 0) { - return NULL; // invalid parameter. - } - } - idec = WebPINewDecoder(NULL); - if (idec == NULL) return NULL; - idec->output_.colorspace = mode; - idec->output_.is_external_memory = is_external_memory; - idec->output_.u.RGBA.rgba = output_buffer; - idec->output_.u.RGBA.stride = output_stride; - idec->output_.u.RGBA.size = output_buffer_size; - return idec; -} - -WebPIDecoder* WebPINewYUVA(uint8_t* luma, size_t luma_size, int luma_stride, - uint8_t* u, size_t u_size, int u_stride, - uint8_t* v, size_t v_size, int v_stride, - uint8_t* a, size_t a_size, int a_stride) { - const int is_external_memory = (luma != NULL) ? 1 : 0; - WebPIDecoder* idec; - WEBP_CSP_MODE colorspace; - - if (is_external_memory == 0) { // Overwrite parameters to sane values. - luma_size = u_size = v_size = a_size = 0; - luma_stride = u_stride = v_stride = a_stride = 0; - u = v = a = NULL; - colorspace = MODE_YUVA; - } else { // A luma buffer was passed. Validate the other parameters. - if (u == NULL || v == NULL) return NULL; - if (luma_size == 0 || u_size == 0 || v_size == 0) return NULL; - if (luma_stride == 0 || u_stride == 0 || v_stride == 0) return NULL; - if (a != NULL) { - if (a_size == 0 || a_stride == 0) return NULL; - } - colorspace = (a == NULL) ? MODE_YUV : MODE_YUVA; - } - - idec = WebPINewDecoder(NULL); - if (idec == NULL) return NULL; - - idec->output_.colorspace = colorspace; - idec->output_.is_external_memory = is_external_memory; - idec->output_.u.YUVA.y = luma; - idec->output_.u.YUVA.y_stride = luma_stride; - idec->output_.u.YUVA.y_size = luma_size; - idec->output_.u.YUVA.u = u; - idec->output_.u.YUVA.u_stride = u_stride; - idec->output_.u.YUVA.u_size = u_size; - idec->output_.u.YUVA.v = v; - idec->output_.u.YUVA.v_stride = v_stride; - idec->output_.u.YUVA.v_size = v_size; - idec->output_.u.YUVA.a = a; - idec->output_.u.YUVA.a_stride = a_stride; - idec->output_.u.YUVA.a_size = a_size; - return idec; -} - -WebPIDecoder* WebPINewYUV(uint8_t* luma, size_t luma_size, int luma_stride, - uint8_t* u, size_t u_size, int u_stride, - uint8_t* v, size_t v_size, int v_stride) { - return WebPINewYUVA(luma, luma_size, luma_stride, - u, u_size, u_stride, - v, v_size, v_stride, - NULL, 0, 0); -} - -//------------------------------------------------------------------------------ - -static VP8StatusCode IDecCheckStatus(const WebPIDecoder* const idec) { - assert(idec); - if (idec->state_ == STATE_ERROR) { - return VP8_STATUS_BITSTREAM_ERROR; - } - if (idec->state_ == STATE_DONE) { - return VP8_STATUS_OK; - } - return VP8_STATUS_SUSPENDED; -} - -VP8StatusCode WebPIAppend(WebPIDecoder* idec, - const uint8_t* data, size_t data_size) { - VP8StatusCode status; - if (idec == NULL || data == NULL) { - return VP8_STATUS_INVALID_PARAM; - } - status = IDecCheckStatus(idec); - if (status != VP8_STATUS_SUSPENDED) { - return status; - } - // Check mixed calls between RemapMemBuffer and AppendToMemBuffer. - if (!CheckMemBufferMode(&idec->mem_, MEM_MODE_APPEND)) { - return VP8_STATUS_INVALID_PARAM; - } - // Append data to memory buffer - if (!AppendToMemBuffer(idec, data, data_size)) { - return VP8_STATUS_OUT_OF_MEMORY; - } - return IDecode(idec); -} - -VP8StatusCode WebPIUpdate(WebPIDecoder* idec, - const uint8_t* data, size_t data_size) { - VP8StatusCode status; - if (idec == NULL || data == NULL) { - return VP8_STATUS_INVALID_PARAM; - } - status = IDecCheckStatus(idec); - if (status != VP8_STATUS_SUSPENDED) { - return status; - } - // Check mixed calls between RemapMemBuffer and AppendToMemBuffer. - if (!CheckMemBufferMode(&idec->mem_, MEM_MODE_MAP)) { - return VP8_STATUS_INVALID_PARAM; - } - // Make the memory buffer point to the new buffer - if (!RemapMemBuffer(idec, data, data_size)) { - return VP8_STATUS_INVALID_PARAM; - } - return IDecode(idec); -} - -//------------------------------------------------------------------------------ - -static const WebPDecBuffer* GetOutputBuffer(const WebPIDecoder* const idec) { - if (idec == NULL || idec->dec_ == NULL) { - return NULL; - } - if (idec->state_ <= STATE_VP8_PARTS0) { - return NULL; - } - if (idec->final_output_ != NULL) { - return NULL; // not yet slow-copied - } - return idec->params_.output; -} - -const WebPDecBuffer* WebPIDecodedArea(const WebPIDecoder* idec, - int* left, int* top, - int* width, int* height) { - const WebPDecBuffer* const src = GetOutputBuffer(idec); - if (left != NULL) *left = 0; - if (top != NULL) *top = 0; - if (src != NULL) { - if (width != NULL) *width = src->width; - if (height != NULL) *height = idec->params_.last_y; - } else { - if (width != NULL) *width = 0; - if (height != NULL) *height = 0; - } - return src; -} - -uint8_t* WebPIDecGetRGB(const WebPIDecoder* idec, int* last_y, - int* width, int* height, int* stride) { - const WebPDecBuffer* const src = GetOutputBuffer(idec); - if (src == NULL) return NULL; - if (src->colorspace >= MODE_YUV) { - return NULL; - } - - if (last_y != NULL) *last_y = idec->params_.last_y; - if (width != NULL) *width = src->width; - if (height != NULL) *height = src->height; - if (stride != NULL) *stride = src->u.RGBA.stride; - - return src->u.RGBA.rgba; -} - -uint8_t* WebPIDecGetYUVA(const WebPIDecoder* idec, int* last_y, - uint8_t** u, uint8_t** v, uint8_t** a, - int* width, int* height, - int* stride, int* uv_stride, int* a_stride) { - const WebPDecBuffer* const src = GetOutputBuffer(idec); - if (src == NULL) return NULL; - if (src->colorspace < MODE_YUV) { - return NULL; - } - - if (last_y != NULL) *last_y = idec->params_.last_y; - if (u != NULL) *u = src->u.YUVA.u; - if (v != NULL) *v = src->u.YUVA.v; - if (a != NULL) *a = src->u.YUVA.a; - if (width != NULL) *width = src->width; - if (height != NULL) *height = src->height; - if (stride != NULL) *stride = src->u.YUVA.y_stride; - if (uv_stride != NULL) *uv_stride = src->u.YUVA.u_stride; - if (a_stride != NULL) *a_stride = src->u.YUVA.a_stride; - - return src->u.YUVA.y; -} - -int WebPISetIOHooks(WebPIDecoder* const idec, - VP8IoPutHook put, - VP8IoSetupHook setup, - VP8IoTeardownHook teardown, - void* user_data) { - if (idec == NULL || idec->state_ > STATE_WEBP_HEADER) { - return 0; - } - - idec->io_.put = put; - idec->io_.setup = setup; - idec->io_.teardown = teardown; - idec->io_.opaque = user_data; - - return 1; -} diff --git a/thirdparty/libwebp/dec/idec_dec.c b/thirdparty/libwebp/dec/idec_dec.c new file mode 100644 index 0000000000..78fb2e7186 --- /dev/null +++ b/thirdparty/libwebp/dec/idec_dec.c @@ -0,0 +1,892 @@ +// Copyright 2011 Google Inc. All Rights Reserved. +// +// Use of this source code is governed by a BSD-style license +// that can be found in the COPYING file in the root of the source +// tree. An additional intellectual property rights grant can be found +// in the file PATENTS. All contributing project authors may +// be found in the AUTHORS file in the root of the source tree. +// ----------------------------------------------------------------------------- +// +// Incremental decoding +// +// Author: somnath@google.com (Somnath Banerjee) + +#include +#include +#include + +#include "./alphai_dec.h" +#include "./webpi_dec.h" +#include "./vp8i_dec.h" +#include "../utils/utils.h" + +// In append mode, buffer allocations increase as multiples of this value. +// Needs to be a power of 2. +#define CHUNK_SIZE 4096 +#define MAX_MB_SIZE 4096 + +//------------------------------------------------------------------------------ +// Data structures for memory and states + +// Decoding states. State normally flows as: +// WEBP_HEADER->VP8_HEADER->VP8_PARTS0->VP8_DATA->DONE for a lossy image, and +// WEBP_HEADER->VP8L_HEADER->VP8L_DATA->DONE for a lossless image. +// If there is any error the decoder goes into state ERROR. +typedef enum { + STATE_WEBP_HEADER, // All the data before that of the VP8/VP8L chunk. + STATE_VP8_HEADER, // The VP8 Frame header (within the VP8 chunk). + STATE_VP8_PARTS0, + STATE_VP8_DATA, + STATE_VP8L_HEADER, + STATE_VP8L_DATA, + STATE_DONE, + STATE_ERROR +} DecState; + +// Operating state for the MemBuffer +typedef enum { + MEM_MODE_NONE = 0, + MEM_MODE_APPEND, + MEM_MODE_MAP +} MemBufferMode; + +// storage for partition #0 and partial data (in a rolling fashion) +typedef struct { + MemBufferMode mode_; // Operation mode + size_t start_; // start location of the data to be decoded + size_t end_; // end location + size_t buf_size_; // size of the allocated buffer + uint8_t* buf_; // We don't own this buffer in case WebPIUpdate() + + size_t part0_size_; // size of partition #0 + const uint8_t* part0_buf_; // buffer to store partition #0 +} MemBuffer; + +struct WebPIDecoder { + DecState state_; // current decoding state + WebPDecParams params_; // Params to store output info + int is_lossless_; // for down-casting 'dec_'. + void* dec_; // either a VP8Decoder or a VP8LDecoder instance + VP8Io io_; + + MemBuffer mem_; // input memory buffer. + WebPDecBuffer output_; // output buffer (when no external one is supplied, + // or if the external one has slow-memory) + WebPDecBuffer* final_output_; // Slow-memory output to copy to eventually. + size_t chunk_size_; // Compressed VP8/VP8L size extracted from Header. + + int last_mb_y_; // last row reached for intra-mode decoding +}; + +// MB context to restore in case VP8DecodeMB() fails +typedef struct { + VP8MB left_; + VP8MB info_; + VP8BitReader token_br_; +} MBContext; + +//------------------------------------------------------------------------------ +// MemBuffer: incoming data handling + +static WEBP_INLINE size_t MemDataSize(const MemBuffer* mem) { + return (mem->end_ - mem->start_); +} + +// Check if we need to preserve the compressed alpha data, as it may not have +// been decoded yet. +static int NeedCompressedAlpha(const WebPIDecoder* const idec) { + if (idec->state_ == STATE_WEBP_HEADER) { + // We haven't parsed the headers yet, so we don't know whether the image is + // lossy or lossless. This also means that we haven't parsed the ALPH chunk. + return 0; + } + if (idec->is_lossless_) { + return 0; // ALPH chunk is not present for lossless images. + } else { + const VP8Decoder* const dec = (VP8Decoder*)idec->dec_; + assert(dec != NULL); // Must be true as idec->state_ != STATE_WEBP_HEADER. + return (dec->alpha_data_ != NULL) && !dec->is_alpha_decoded_; + } +} + +static void DoRemap(WebPIDecoder* const idec, ptrdiff_t offset) { + MemBuffer* const mem = &idec->mem_; + const uint8_t* const new_base = mem->buf_ + mem->start_; + // note: for VP8, setting up idec->io_ is only really needed at the beginning + // of the decoding, till partition #0 is complete. + idec->io_.data = new_base; + idec->io_.data_size = MemDataSize(mem); + + if (idec->dec_ != NULL) { + if (!idec->is_lossless_) { + VP8Decoder* const dec = (VP8Decoder*)idec->dec_; + const uint32_t last_part = dec->num_parts_minus_one_; + if (offset != 0) { + uint32_t p; + for (p = 0; p <= last_part; ++p) { + VP8RemapBitReader(dec->parts_ + p, offset); + } + // Remap partition #0 data pointer to new offset, but only in MAP + // mode (in APPEND mode, partition #0 is copied into a fixed memory). + if (mem->mode_ == MEM_MODE_MAP) { + VP8RemapBitReader(&dec->br_, offset); + } + } + { + const uint8_t* const last_start = dec->parts_[last_part].buf_; + VP8BitReaderSetBuffer(&dec->parts_[last_part], last_start, + mem->buf_ + mem->end_ - last_start); + } + if (NeedCompressedAlpha(idec)) { + ALPHDecoder* const alph_dec = dec->alph_dec_; + dec->alpha_data_ += offset; + if (alph_dec != NULL) { + if (alph_dec->method_ == ALPHA_LOSSLESS_COMPRESSION) { + VP8LDecoder* const alph_vp8l_dec = alph_dec->vp8l_dec_; + assert(alph_vp8l_dec != NULL); + assert(dec->alpha_data_size_ >= ALPHA_HEADER_LEN); + VP8LBitReaderSetBuffer(&alph_vp8l_dec->br_, + dec->alpha_data_ + ALPHA_HEADER_LEN, + dec->alpha_data_size_ - ALPHA_HEADER_LEN); + } else { // alph_dec->method_ == ALPHA_NO_COMPRESSION + // Nothing special to do in this case. + } + } + } + } else { // Resize lossless bitreader + VP8LDecoder* const dec = (VP8LDecoder*)idec->dec_; + VP8LBitReaderSetBuffer(&dec->br_, new_base, MemDataSize(mem)); + } + } +} + +// Appends data to the end of MemBuffer->buf_. It expands the allocated memory +// size if required and also updates VP8BitReader's if new memory is allocated. +static int AppendToMemBuffer(WebPIDecoder* const idec, + const uint8_t* const data, size_t data_size) { + VP8Decoder* const dec = (VP8Decoder*)idec->dec_; + MemBuffer* const mem = &idec->mem_; + const int need_compressed_alpha = NeedCompressedAlpha(idec); + const uint8_t* const old_start = mem->buf_ + mem->start_; + const uint8_t* const old_base = + need_compressed_alpha ? dec->alpha_data_ : old_start; + assert(mem->mode_ == MEM_MODE_APPEND); + if (data_size > MAX_CHUNK_PAYLOAD) { + // security safeguard: trying to allocate more than what the format + // allows for a chunk should be considered a smoke smell. + return 0; + } + + if (mem->end_ + data_size > mem->buf_size_) { // Need some free memory + const size_t new_mem_start = old_start - old_base; + const size_t current_size = MemDataSize(mem) + new_mem_start; + const uint64_t new_size = (uint64_t)current_size + data_size; + const uint64_t extra_size = (new_size + CHUNK_SIZE - 1) & ~(CHUNK_SIZE - 1); + uint8_t* const new_buf = + (uint8_t*)WebPSafeMalloc(extra_size, sizeof(*new_buf)); + if (new_buf == NULL) return 0; + memcpy(new_buf, old_base, current_size); + WebPSafeFree(mem->buf_); + mem->buf_ = new_buf; + mem->buf_size_ = (size_t)extra_size; + mem->start_ = new_mem_start; + mem->end_ = current_size; + } + + memcpy(mem->buf_ + mem->end_, data, data_size); + mem->end_ += data_size; + assert(mem->end_ <= mem->buf_size_); + + DoRemap(idec, mem->buf_ + mem->start_ - old_start); + return 1; +} + +static int RemapMemBuffer(WebPIDecoder* const idec, + const uint8_t* const data, size_t data_size) { + MemBuffer* const mem = &idec->mem_; + const uint8_t* const old_buf = mem->buf_; + const uint8_t* const old_start = old_buf + mem->start_; + assert(mem->mode_ == MEM_MODE_MAP); + + if (data_size < mem->buf_size_) return 0; // can't remap to a shorter buffer! + + mem->buf_ = (uint8_t*)data; + mem->end_ = mem->buf_size_ = data_size; + + DoRemap(idec, mem->buf_ + mem->start_ - old_start); + return 1; +} + +static void InitMemBuffer(MemBuffer* const mem) { + mem->mode_ = MEM_MODE_NONE; + mem->buf_ = NULL; + mem->buf_size_ = 0; + mem->part0_buf_ = NULL; + mem->part0_size_ = 0; +} + +static void ClearMemBuffer(MemBuffer* const mem) { + assert(mem); + if (mem->mode_ == MEM_MODE_APPEND) { + WebPSafeFree(mem->buf_); + WebPSafeFree((void*)mem->part0_buf_); + } +} + +static int CheckMemBufferMode(MemBuffer* const mem, MemBufferMode expected) { + if (mem->mode_ == MEM_MODE_NONE) { + mem->mode_ = expected; // switch to the expected mode + } else if (mem->mode_ != expected) { + return 0; // we mixed the modes => error + } + assert(mem->mode_ == expected); // mode is ok + return 1; +} + +// To be called last. +static VP8StatusCode FinishDecoding(WebPIDecoder* const idec) { + const WebPDecoderOptions* const options = idec->params_.options; + WebPDecBuffer* const output = idec->params_.output; + + idec->state_ = STATE_DONE; + if (options != NULL && options->flip) { + const VP8StatusCode status = WebPFlipBuffer(output); + if (status != VP8_STATUS_OK) return status; + } + if (idec->final_output_ != NULL) { + WebPCopyDecBufferPixels(output, idec->final_output_); // do the slow-copy + WebPFreeDecBuffer(&idec->output_); + *output = *idec->final_output_; + idec->final_output_ = NULL; + } + return VP8_STATUS_OK; +} + +//------------------------------------------------------------------------------ +// Macroblock-decoding contexts + +static void SaveContext(const VP8Decoder* dec, const VP8BitReader* token_br, + MBContext* const context) { + context->left_ = dec->mb_info_[-1]; + context->info_ = dec->mb_info_[dec->mb_x_]; + context->token_br_ = *token_br; +} + +static void RestoreContext(const MBContext* context, VP8Decoder* const dec, + VP8BitReader* const token_br) { + dec->mb_info_[-1] = context->left_; + dec->mb_info_[dec->mb_x_] = context->info_; + *token_br = context->token_br_; +} + +//------------------------------------------------------------------------------ + +static VP8StatusCode IDecError(WebPIDecoder* const idec, VP8StatusCode error) { + if (idec->state_ == STATE_VP8_DATA) { + VP8Io* const io = &idec->io_; + if (io->teardown != NULL) { + io->teardown(io); + } + } + idec->state_ = STATE_ERROR; + return error; +} + +static void ChangeState(WebPIDecoder* const idec, DecState new_state, + size_t consumed_bytes) { + MemBuffer* const mem = &idec->mem_; + idec->state_ = new_state; + mem->start_ += consumed_bytes; + assert(mem->start_ <= mem->end_); + idec->io_.data = mem->buf_ + mem->start_; + idec->io_.data_size = MemDataSize(mem); +} + +// Headers +static VP8StatusCode DecodeWebPHeaders(WebPIDecoder* const idec) { + MemBuffer* const mem = &idec->mem_; + const uint8_t* data = mem->buf_ + mem->start_; + size_t curr_size = MemDataSize(mem); + VP8StatusCode status; + WebPHeaderStructure headers; + + headers.data = data; + headers.data_size = curr_size; + headers.have_all_data = 0; + status = WebPParseHeaders(&headers); + if (status == VP8_STATUS_NOT_ENOUGH_DATA) { + return VP8_STATUS_SUSPENDED; // We haven't found a VP8 chunk yet. + } else if (status != VP8_STATUS_OK) { + return IDecError(idec, status); + } + + idec->chunk_size_ = headers.compressed_size; + idec->is_lossless_ = headers.is_lossless; + if (!idec->is_lossless_) { + VP8Decoder* const dec = VP8New(); + if (dec == NULL) { + return VP8_STATUS_OUT_OF_MEMORY; + } + idec->dec_ = dec; + dec->alpha_data_ = headers.alpha_data; + dec->alpha_data_size_ = headers.alpha_data_size; + ChangeState(idec, STATE_VP8_HEADER, headers.offset); + } else { + VP8LDecoder* const dec = VP8LNew(); + if (dec == NULL) { + return VP8_STATUS_OUT_OF_MEMORY; + } + idec->dec_ = dec; + ChangeState(idec, STATE_VP8L_HEADER, headers.offset); + } + return VP8_STATUS_OK; +} + +static VP8StatusCode DecodeVP8FrameHeader(WebPIDecoder* const idec) { + const uint8_t* data = idec->mem_.buf_ + idec->mem_.start_; + const size_t curr_size = MemDataSize(&idec->mem_); + int width, height; + uint32_t bits; + + if (curr_size < VP8_FRAME_HEADER_SIZE) { + // Not enough data bytes to extract VP8 Frame Header. + return VP8_STATUS_SUSPENDED; + } + if (!VP8GetInfo(data, curr_size, idec->chunk_size_, &width, &height)) { + return IDecError(idec, VP8_STATUS_BITSTREAM_ERROR); + } + + bits = data[0] | (data[1] << 8) | (data[2] << 16); + idec->mem_.part0_size_ = (bits >> 5) + VP8_FRAME_HEADER_SIZE; + + idec->io_.data = data; + idec->io_.data_size = curr_size; + idec->state_ = STATE_VP8_PARTS0; + return VP8_STATUS_OK; +} + +// Partition #0 +static VP8StatusCode CopyParts0Data(WebPIDecoder* const idec) { + VP8Decoder* const dec = (VP8Decoder*)idec->dec_; + VP8BitReader* const br = &dec->br_; + const size_t part_size = br->buf_end_ - br->buf_; + MemBuffer* const mem = &idec->mem_; + assert(!idec->is_lossless_); + assert(mem->part0_buf_ == NULL); + // the following is a format limitation, no need for runtime check: + assert(part_size <= mem->part0_size_); + if (part_size == 0) { // can't have zero-size partition #0 + return VP8_STATUS_BITSTREAM_ERROR; + } + if (mem->mode_ == MEM_MODE_APPEND) { + // We copy and grab ownership of the partition #0 data. + uint8_t* const part0_buf = (uint8_t*)WebPSafeMalloc(1ULL, part_size); + if (part0_buf == NULL) { + return VP8_STATUS_OUT_OF_MEMORY; + } + memcpy(part0_buf, br->buf_, part_size); + mem->part0_buf_ = part0_buf; + VP8BitReaderSetBuffer(br, part0_buf, part_size); + } else { + // Else: just keep pointers to the partition #0's data in dec_->br_. + } + mem->start_ += part_size; + return VP8_STATUS_OK; +} + +static VP8StatusCode DecodePartition0(WebPIDecoder* const idec) { + VP8Decoder* const dec = (VP8Decoder*)idec->dec_; + VP8Io* const io = &idec->io_; + const WebPDecParams* const params = &idec->params_; + WebPDecBuffer* const output = params->output; + + // Wait till we have enough data for the whole partition #0 + if (MemDataSize(&idec->mem_) < idec->mem_.part0_size_) { + return VP8_STATUS_SUSPENDED; + } + + if (!VP8GetHeaders(dec, io)) { + const VP8StatusCode status = dec->status_; + if (status == VP8_STATUS_SUSPENDED || + status == VP8_STATUS_NOT_ENOUGH_DATA) { + // treating NOT_ENOUGH_DATA as SUSPENDED state + return VP8_STATUS_SUSPENDED; + } + return IDecError(idec, status); + } + + // Allocate/Verify output buffer now + dec->status_ = WebPAllocateDecBuffer(io->width, io->height, params->options, + output); + if (dec->status_ != VP8_STATUS_OK) { + return IDecError(idec, dec->status_); + } + // This change must be done before calling VP8InitFrame() + dec->mt_method_ = VP8GetThreadMethod(params->options, NULL, + io->width, io->height); + VP8InitDithering(params->options, dec); + + dec->status_ = CopyParts0Data(idec); + if (dec->status_ != VP8_STATUS_OK) { + return IDecError(idec, dec->status_); + } + + // Finish setting up the decoding parameters. Will call io->setup(). + if (VP8EnterCritical(dec, io) != VP8_STATUS_OK) { + return IDecError(idec, dec->status_); + } + + // Note: past this point, teardown() must always be called + // in case of error. + idec->state_ = STATE_VP8_DATA; + // Allocate memory and prepare everything. + if (!VP8InitFrame(dec, io)) { + return IDecError(idec, dec->status_); + } + return VP8_STATUS_OK; +} + +// Remaining partitions +static VP8StatusCode DecodeRemaining(WebPIDecoder* const idec) { + VP8Decoder* const dec = (VP8Decoder*)idec->dec_; + VP8Io* const io = &idec->io_; + + assert(dec->ready_); + for (; dec->mb_y_ < dec->mb_h_; ++dec->mb_y_) { + if (idec->last_mb_y_ != dec->mb_y_) { + if (!VP8ParseIntraModeRow(&dec->br_, dec)) { + // note: normally, error shouldn't occur since we already have the whole + // partition0 available here in DecodeRemaining(). Reaching EOF while + // reading intra modes really means a BITSTREAM_ERROR. + return IDecError(idec, VP8_STATUS_BITSTREAM_ERROR); + } + idec->last_mb_y_ = dec->mb_y_; + } + for (; dec->mb_x_ < dec->mb_w_; ++dec->mb_x_) { + VP8BitReader* const token_br = + &dec->parts_[dec->mb_y_ & dec->num_parts_minus_one_]; + MBContext context; + SaveContext(dec, token_br, &context); + if (!VP8DecodeMB(dec, token_br)) { + // We shouldn't fail when MAX_MB data was available + if (dec->num_parts_minus_one_ == 0 && + MemDataSize(&idec->mem_) > MAX_MB_SIZE) { + return IDecError(idec, VP8_STATUS_BITSTREAM_ERROR); + } + RestoreContext(&context, dec, token_br); + return VP8_STATUS_SUSPENDED; + } + // Release buffer only if there is only one partition + if (dec->num_parts_minus_one_ == 0) { + idec->mem_.start_ = token_br->buf_ - idec->mem_.buf_; + assert(idec->mem_.start_ <= idec->mem_.end_); + } + } + VP8InitScanline(dec); // Prepare for next scanline + + // Reconstruct, filter and emit the row. + if (!VP8ProcessRow(dec, io)) { + return IDecError(idec, VP8_STATUS_USER_ABORT); + } + } + // Synchronize the thread and check for errors. + if (!VP8ExitCritical(dec, io)) { + return IDecError(idec, VP8_STATUS_USER_ABORT); + } + dec->ready_ = 0; + return FinishDecoding(idec); +} + +static VP8StatusCode ErrorStatusLossless(WebPIDecoder* const idec, + VP8StatusCode status) { + if (status == VP8_STATUS_SUSPENDED || status == VP8_STATUS_NOT_ENOUGH_DATA) { + return VP8_STATUS_SUSPENDED; + } + return IDecError(idec, status); +} + +static VP8StatusCode DecodeVP8LHeader(WebPIDecoder* const idec) { + VP8Io* const io = &idec->io_; + VP8LDecoder* const dec = (VP8LDecoder*)idec->dec_; + const WebPDecParams* const params = &idec->params_; + WebPDecBuffer* const output = params->output; + size_t curr_size = MemDataSize(&idec->mem_); + assert(idec->is_lossless_); + + // Wait until there's enough data for decoding header. + if (curr_size < (idec->chunk_size_ >> 3)) { + dec->status_ = VP8_STATUS_SUSPENDED; + return ErrorStatusLossless(idec, dec->status_); + } + + if (!VP8LDecodeHeader(dec, io)) { + if (dec->status_ == VP8_STATUS_BITSTREAM_ERROR && + curr_size < idec->chunk_size_) { + dec->status_ = VP8_STATUS_SUSPENDED; + } + return ErrorStatusLossless(idec, dec->status_); + } + // Allocate/verify output buffer now. + dec->status_ = WebPAllocateDecBuffer(io->width, io->height, params->options, + output); + if (dec->status_ != VP8_STATUS_OK) { + return IDecError(idec, dec->status_); + } + + idec->state_ = STATE_VP8L_DATA; + return VP8_STATUS_OK; +} + +static VP8StatusCode DecodeVP8LData(WebPIDecoder* const idec) { + VP8LDecoder* const dec = (VP8LDecoder*)idec->dec_; + const size_t curr_size = MemDataSize(&idec->mem_); + assert(idec->is_lossless_); + + // Switch to incremental decoding if we don't have all the bytes available. + dec->incremental_ = (curr_size < idec->chunk_size_); + + if (!VP8LDecodeImage(dec)) { + return ErrorStatusLossless(idec, dec->status_); + } + assert(dec->status_ == VP8_STATUS_OK || dec->status_ == VP8_STATUS_SUSPENDED); + return (dec->status_ == VP8_STATUS_SUSPENDED) ? dec->status_ + : FinishDecoding(idec); +} + + // Main decoding loop +static VP8StatusCode IDecode(WebPIDecoder* idec) { + VP8StatusCode status = VP8_STATUS_SUSPENDED; + + if (idec->state_ == STATE_WEBP_HEADER) { + status = DecodeWebPHeaders(idec); + } else { + if (idec->dec_ == NULL) { + return VP8_STATUS_SUSPENDED; // can't continue if we have no decoder. + } + } + if (idec->state_ == STATE_VP8_HEADER) { + status = DecodeVP8FrameHeader(idec); + } + if (idec->state_ == STATE_VP8_PARTS0) { + status = DecodePartition0(idec); + } + if (idec->state_ == STATE_VP8_DATA) { + status = DecodeRemaining(idec); + } + if (idec->state_ == STATE_VP8L_HEADER) { + status = DecodeVP8LHeader(idec); + } + if (idec->state_ == STATE_VP8L_DATA) { + status = DecodeVP8LData(idec); + } + return status; +} + +//------------------------------------------------------------------------------ +// Internal constructor + +static WebPIDecoder* NewDecoder(WebPDecBuffer* const output_buffer, + const WebPBitstreamFeatures* const features) { + WebPIDecoder* idec = (WebPIDecoder*)WebPSafeCalloc(1ULL, sizeof(*idec)); + if (idec == NULL) { + return NULL; + } + + idec->state_ = STATE_WEBP_HEADER; + idec->chunk_size_ = 0; + + idec->last_mb_y_ = -1; + + InitMemBuffer(&idec->mem_); + WebPInitDecBuffer(&idec->output_); + VP8InitIo(&idec->io_); + + WebPResetDecParams(&idec->params_); + if (output_buffer == NULL || WebPAvoidSlowMemory(output_buffer, features)) { + idec->params_.output = &idec->output_; + idec->final_output_ = output_buffer; + if (output_buffer != NULL) { + idec->params_.output->colorspace = output_buffer->colorspace; + } + } else { + idec->params_.output = output_buffer; + idec->final_output_ = NULL; + } + WebPInitCustomIo(&idec->params_, &idec->io_); // Plug the I/O functions. + + return idec; +} + +//------------------------------------------------------------------------------ +// Public functions + +WebPIDecoder* WebPINewDecoder(WebPDecBuffer* output_buffer) { + return NewDecoder(output_buffer, NULL); +} + +WebPIDecoder* WebPIDecode(const uint8_t* data, size_t data_size, + WebPDecoderConfig* config) { + WebPIDecoder* idec; + WebPBitstreamFeatures tmp_features; + WebPBitstreamFeatures* const features = + (config == NULL) ? &tmp_features : &config->input; + memset(&tmp_features, 0, sizeof(tmp_features)); + + // Parse the bitstream's features, if requested: + if (data != NULL && data_size > 0) { + if (WebPGetFeatures(data, data_size, features) != VP8_STATUS_OK) { + return NULL; + } + } + + // Create an instance of the incremental decoder + idec = (config != NULL) ? NewDecoder(&config->output, features) + : NewDecoder(NULL, features); + if (idec == NULL) { + return NULL; + } + // Finish initialization + if (config != NULL) { + idec->params_.options = &config->options; + } + return idec; +} + +void WebPIDelete(WebPIDecoder* idec) { + if (idec == NULL) return; + if (idec->dec_ != NULL) { + if (!idec->is_lossless_) { + if (idec->state_ == STATE_VP8_DATA) { + // Synchronize the thread, clean-up and check for errors. + VP8ExitCritical((VP8Decoder*)idec->dec_, &idec->io_); + } + VP8Delete((VP8Decoder*)idec->dec_); + } else { + VP8LDelete((VP8LDecoder*)idec->dec_); + } + } + ClearMemBuffer(&idec->mem_); + WebPFreeDecBuffer(&idec->output_); + WebPSafeFree(idec); +} + +//------------------------------------------------------------------------------ +// Wrapper toward WebPINewDecoder + +WebPIDecoder* WebPINewRGB(WEBP_CSP_MODE mode, uint8_t* output_buffer, + size_t output_buffer_size, int output_stride) { + const int is_external_memory = (output_buffer != NULL) ? 1 : 0; + WebPIDecoder* idec; + + if (mode >= MODE_YUV) return NULL; + if (is_external_memory == 0) { // Overwrite parameters to sane values. + output_buffer_size = 0; + output_stride = 0; + } else { // A buffer was passed. Validate the other params. + if (output_stride == 0 || output_buffer_size == 0) { + return NULL; // invalid parameter. + } + } + idec = WebPINewDecoder(NULL); + if (idec == NULL) return NULL; + idec->output_.colorspace = mode; + idec->output_.is_external_memory = is_external_memory; + idec->output_.u.RGBA.rgba = output_buffer; + idec->output_.u.RGBA.stride = output_stride; + idec->output_.u.RGBA.size = output_buffer_size; + return idec; +} + +WebPIDecoder* WebPINewYUVA(uint8_t* luma, size_t luma_size, int luma_stride, + uint8_t* u, size_t u_size, int u_stride, + uint8_t* v, size_t v_size, int v_stride, + uint8_t* a, size_t a_size, int a_stride) { + const int is_external_memory = (luma != NULL) ? 1 : 0; + WebPIDecoder* idec; + WEBP_CSP_MODE colorspace; + + if (is_external_memory == 0) { // Overwrite parameters to sane values. + luma_size = u_size = v_size = a_size = 0; + luma_stride = u_stride = v_stride = a_stride = 0; + u = v = a = NULL; + colorspace = MODE_YUVA; + } else { // A luma buffer was passed. Validate the other parameters. + if (u == NULL || v == NULL) return NULL; + if (luma_size == 0 || u_size == 0 || v_size == 0) return NULL; + if (luma_stride == 0 || u_stride == 0 || v_stride == 0) return NULL; + if (a != NULL) { + if (a_size == 0 || a_stride == 0) return NULL; + } + colorspace = (a == NULL) ? MODE_YUV : MODE_YUVA; + } + + idec = WebPINewDecoder(NULL); + if (idec == NULL) return NULL; + + idec->output_.colorspace = colorspace; + idec->output_.is_external_memory = is_external_memory; + idec->output_.u.YUVA.y = luma; + idec->output_.u.YUVA.y_stride = luma_stride; + idec->output_.u.YUVA.y_size = luma_size; + idec->output_.u.YUVA.u = u; + idec->output_.u.YUVA.u_stride = u_stride; + idec->output_.u.YUVA.u_size = u_size; + idec->output_.u.YUVA.v = v; + idec->output_.u.YUVA.v_stride = v_stride; + idec->output_.u.YUVA.v_size = v_size; + idec->output_.u.YUVA.a = a; + idec->output_.u.YUVA.a_stride = a_stride; + idec->output_.u.YUVA.a_size = a_size; + return idec; +} + +WebPIDecoder* WebPINewYUV(uint8_t* luma, size_t luma_size, int luma_stride, + uint8_t* u, size_t u_size, int u_stride, + uint8_t* v, size_t v_size, int v_stride) { + return WebPINewYUVA(luma, luma_size, luma_stride, + u, u_size, u_stride, + v, v_size, v_stride, + NULL, 0, 0); +} + +//------------------------------------------------------------------------------ + +static VP8StatusCode IDecCheckStatus(const WebPIDecoder* const idec) { + assert(idec); + if (idec->state_ == STATE_ERROR) { + return VP8_STATUS_BITSTREAM_ERROR; + } + if (idec->state_ == STATE_DONE) { + return VP8_STATUS_OK; + } + return VP8_STATUS_SUSPENDED; +} + +VP8StatusCode WebPIAppend(WebPIDecoder* idec, + const uint8_t* data, size_t data_size) { + VP8StatusCode status; + if (idec == NULL || data == NULL) { + return VP8_STATUS_INVALID_PARAM; + } + status = IDecCheckStatus(idec); + if (status != VP8_STATUS_SUSPENDED) { + return status; + } + // Check mixed calls between RemapMemBuffer and AppendToMemBuffer. + if (!CheckMemBufferMode(&idec->mem_, MEM_MODE_APPEND)) { + return VP8_STATUS_INVALID_PARAM; + } + // Append data to memory buffer + if (!AppendToMemBuffer(idec, data, data_size)) { + return VP8_STATUS_OUT_OF_MEMORY; + } + return IDecode(idec); +} + +VP8StatusCode WebPIUpdate(WebPIDecoder* idec, + const uint8_t* data, size_t data_size) { + VP8StatusCode status; + if (idec == NULL || data == NULL) { + return VP8_STATUS_INVALID_PARAM; + } + status = IDecCheckStatus(idec); + if (status != VP8_STATUS_SUSPENDED) { + return status; + } + // Check mixed calls between RemapMemBuffer and AppendToMemBuffer. + if (!CheckMemBufferMode(&idec->mem_, MEM_MODE_MAP)) { + return VP8_STATUS_INVALID_PARAM; + } + // Make the memory buffer point to the new buffer + if (!RemapMemBuffer(idec, data, data_size)) { + return VP8_STATUS_INVALID_PARAM; + } + return IDecode(idec); +} + +//------------------------------------------------------------------------------ + +static const WebPDecBuffer* GetOutputBuffer(const WebPIDecoder* const idec) { + if (idec == NULL || idec->dec_ == NULL) { + return NULL; + } + if (idec->state_ <= STATE_VP8_PARTS0) { + return NULL; + } + if (idec->final_output_ != NULL) { + return NULL; // not yet slow-copied + } + return idec->params_.output; +} + +const WebPDecBuffer* WebPIDecodedArea(const WebPIDecoder* idec, + int* left, int* top, + int* width, int* height) { + const WebPDecBuffer* const src = GetOutputBuffer(idec); + if (left != NULL) *left = 0; + if (top != NULL) *top = 0; + if (src != NULL) { + if (width != NULL) *width = src->width; + if (height != NULL) *height = idec->params_.last_y; + } else { + if (width != NULL) *width = 0; + if (height != NULL) *height = 0; + } + return src; +} + +uint8_t* WebPIDecGetRGB(const WebPIDecoder* idec, int* last_y, + int* width, int* height, int* stride) { + const WebPDecBuffer* const src = GetOutputBuffer(idec); + if (src == NULL) return NULL; + if (src->colorspace >= MODE_YUV) { + return NULL; + } + + if (last_y != NULL) *last_y = idec->params_.last_y; + if (width != NULL) *width = src->width; + if (height != NULL) *height = src->height; + if (stride != NULL) *stride = src->u.RGBA.stride; + + return src->u.RGBA.rgba; +} + +uint8_t* WebPIDecGetYUVA(const WebPIDecoder* idec, int* last_y, + uint8_t** u, uint8_t** v, uint8_t** a, + int* width, int* height, + int* stride, int* uv_stride, int* a_stride) { + const WebPDecBuffer* const src = GetOutputBuffer(idec); + if (src == NULL) return NULL; + if (src->colorspace < MODE_YUV) { + return NULL; + } + + if (last_y != NULL) *last_y = idec->params_.last_y; + if (u != NULL) *u = src->u.YUVA.u; + if (v != NULL) *v = src->u.YUVA.v; + if (a != NULL) *a = src->u.YUVA.a; + if (width != NULL) *width = src->width; + if (height != NULL) *height = src->height; + if (stride != NULL) *stride = src->u.YUVA.y_stride; + if (uv_stride != NULL) *uv_stride = src->u.YUVA.u_stride; + if (a_stride != NULL) *a_stride = src->u.YUVA.a_stride; + + return src->u.YUVA.y; +} + +int WebPISetIOHooks(WebPIDecoder* const idec, + VP8IoPutHook put, + VP8IoSetupHook setup, + VP8IoTeardownHook teardown, + void* user_data) { + if (idec == NULL || idec->state_ > STATE_WEBP_HEADER) { + return 0; + } + + idec->io_.put = put; + idec->io_.setup = setup; + idec->io_.teardown = teardown; + idec->io_.opaque = user_data; + + return 1; +} diff --git a/thirdparty/libwebp/dec/io.c b/thirdparty/libwebp/dec/io.c deleted file mode 100644 index 8d5c43f325..0000000000 --- a/thirdparty/libwebp/dec/io.c +++ /dev/null @@ -1,624 +0,0 @@ -// Copyright 2011 Google Inc. All Rights Reserved. -// -// Use of this source code is governed by a BSD-style license -// that can be found in the COPYING file in the root of the source -// tree. An additional intellectual property rights grant can be found -// in the file PATENTS. All contributing project authors may -// be found in the AUTHORS file in the root of the source tree. -// ----------------------------------------------------------------------------- -// -// functions for sample output. -// -// Author: Skal (pascal.massimino@gmail.com) - -#include -#include -#include "../dec/vp8i.h" -#include "./webpi.h" -#include "../dsp/dsp.h" -#include "../dsp/yuv.h" -#include "../utils/utils.h" - -//------------------------------------------------------------------------------ -// Main YUV<->RGB conversion functions - -static int EmitYUV(const VP8Io* const io, WebPDecParams* const p) { - WebPDecBuffer* output = p->output; - const WebPYUVABuffer* const buf = &output->u.YUVA; - uint8_t* const y_dst = buf->y + io->mb_y * buf->y_stride; - uint8_t* const u_dst = buf->u + (io->mb_y >> 1) * buf->u_stride; - uint8_t* const v_dst = buf->v + (io->mb_y >> 1) * buf->v_stride; - const int mb_w = io->mb_w; - const int mb_h = io->mb_h; - const int uv_w = (mb_w + 1) / 2; - const int uv_h = (mb_h + 1) / 2; - int j; - for (j = 0; j < mb_h; ++j) { - memcpy(y_dst + j * buf->y_stride, io->y + j * io->y_stride, mb_w); - } - for (j = 0; j < uv_h; ++j) { - memcpy(u_dst + j * buf->u_stride, io->u + j * io->uv_stride, uv_w); - memcpy(v_dst + j * buf->v_stride, io->v + j * io->uv_stride, uv_w); - } - return io->mb_h; -} - -// Point-sampling U/V sampler. -static int EmitSampledRGB(const VP8Io* const io, WebPDecParams* const p) { - WebPDecBuffer* const output = p->output; - WebPRGBABuffer* const buf = &output->u.RGBA; - uint8_t* const dst = buf->rgba + io->mb_y * buf->stride; - WebPSamplerProcessPlane(io->y, io->y_stride, - io->u, io->v, io->uv_stride, - dst, buf->stride, io->mb_w, io->mb_h, - WebPSamplers[output->colorspace]); - return io->mb_h; -} - -//------------------------------------------------------------------------------ -// Fancy upsampling - -#ifdef FANCY_UPSAMPLING -static int EmitFancyRGB(const VP8Io* const io, WebPDecParams* const p) { - int num_lines_out = io->mb_h; // a priori guess - const WebPRGBABuffer* const buf = &p->output->u.RGBA; - uint8_t* dst = buf->rgba + io->mb_y * buf->stride; - WebPUpsampleLinePairFunc upsample = WebPUpsamplers[p->output->colorspace]; - const uint8_t* cur_y = io->y; - const uint8_t* cur_u = io->u; - const uint8_t* cur_v = io->v; - const uint8_t* top_u = p->tmp_u; - const uint8_t* top_v = p->tmp_v; - int y = io->mb_y; - const int y_end = io->mb_y + io->mb_h; - const int mb_w = io->mb_w; - const int uv_w = (mb_w + 1) / 2; - - if (y == 0) { - // First line is special cased. We mirror the u/v samples at boundary. - upsample(cur_y, NULL, cur_u, cur_v, cur_u, cur_v, dst, NULL, mb_w); - } else { - // We can finish the left-over line from previous call. - upsample(p->tmp_y, cur_y, top_u, top_v, cur_u, cur_v, - dst - buf->stride, dst, mb_w); - ++num_lines_out; - } - // Loop over each output pairs of row. - for (; y + 2 < y_end; y += 2) { - top_u = cur_u; - top_v = cur_v; - cur_u += io->uv_stride; - cur_v += io->uv_stride; - dst += 2 * buf->stride; - cur_y += 2 * io->y_stride; - upsample(cur_y - io->y_stride, cur_y, - top_u, top_v, cur_u, cur_v, - dst - buf->stride, dst, mb_w); - } - // move to last row - cur_y += io->y_stride; - if (io->crop_top + y_end < io->crop_bottom) { - // Save the unfinished samples for next call (as we're not done yet). - memcpy(p->tmp_y, cur_y, mb_w * sizeof(*p->tmp_y)); - memcpy(p->tmp_u, cur_u, uv_w * sizeof(*p->tmp_u)); - memcpy(p->tmp_v, cur_v, uv_w * sizeof(*p->tmp_v)); - // The fancy upsampler leaves a row unfinished behind - // (except for the very last row) - num_lines_out--; - } else { - // Process the very last row of even-sized picture - if (!(y_end & 1)) { - upsample(cur_y, NULL, cur_u, cur_v, cur_u, cur_v, - dst + buf->stride, NULL, mb_w); - } - } - return num_lines_out; -} - -#endif /* FANCY_UPSAMPLING */ - -//------------------------------------------------------------------------------ - -static void FillAlphaPlane(uint8_t* dst, int w, int h, int stride) { - int j; - for (j = 0; j < h; ++j) { - memset(dst, 0xff, w * sizeof(*dst)); - dst += stride; - } -} - -static int EmitAlphaYUV(const VP8Io* const io, WebPDecParams* const p, - int expected_num_lines_out) { - const uint8_t* alpha = io->a; - const WebPYUVABuffer* const buf = &p->output->u.YUVA; - const int mb_w = io->mb_w; - const int mb_h = io->mb_h; - uint8_t* dst = buf->a + io->mb_y * buf->a_stride; - int j; - (void)expected_num_lines_out; - assert(expected_num_lines_out == mb_h); - if (alpha != NULL) { - for (j = 0; j < mb_h; ++j) { - memcpy(dst, alpha, mb_w * sizeof(*dst)); - alpha += io->width; - dst += buf->a_stride; - } - } else if (buf->a != NULL) { - // the user requested alpha, but there is none, set it to opaque. - FillAlphaPlane(dst, mb_w, mb_h, buf->a_stride); - } - return 0; -} - -static int GetAlphaSourceRow(const VP8Io* const io, - const uint8_t** alpha, int* const num_rows) { - int start_y = io->mb_y; - *num_rows = io->mb_h; - - // Compensate for the 1-line delay of the fancy upscaler. - // This is similar to EmitFancyRGB(). - if (io->fancy_upsampling) { - if (start_y == 0) { - // We don't process the last row yet. It'll be done during the next call. - --*num_rows; - } else { - --start_y; - // Fortunately, *alpha data is persistent, so we can go back - // one row and finish alpha blending, now that the fancy upscaler - // completed the YUV->RGB interpolation. - *alpha -= io->width; - } - if (io->crop_top + io->mb_y + io->mb_h == io->crop_bottom) { - // If it's the very last call, we process all the remaining rows! - *num_rows = io->crop_bottom - io->crop_top - start_y; - } - } - return start_y; -} - -static int EmitAlphaRGB(const VP8Io* const io, WebPDecParams* const p, - int expected_num_lines_out) { - const uint8_t* alpha = io->a; - if (alpha != NULL) { - const int mb_w = io->mb_w; - const WEBP_CSP_MODE colorspace = p->output->colorspace; - const int alpha_first = - (colorspace == MODE_ARGB || colorspace == MODE_Argb); - const WebPRGBABuffer* const buf = &p->output->u.RGBA; - int num_rows; - const int start_y = GetAlphaSourceRow(io, &alpha, &num_rows); - uint8_t* const base_rgba = buf->rgba + start_y * buf->stride; - uint8_t* const dst = base_rgba + (alpha_first ? 0 : 3); - const int has_alpha = WebPDispatchAlpha(alpha, io->width, mb_w, - num_rows, dst, buf->stride); - (void)expected_num_lines_out; - assert(expected_num_lines_out == num_rows); - // has_alpha is true if there's non-trivial alpha to premultiply with. - if (has_alpha && WebPIsPremultipliedMode(colorspace)) { - WebPApplyAlphaMultiply(base_rgba, alpha_first, - mb_w, num_rows, buf->stride); - } - } - return 0; -} - -static int EmitAlphaRGBA4444(const VP8Io* const io, WebPDecParams* const p, - int expected_num_lines_out) { - const uint8_t* alpha = io->a; - if (alpha != NULL) { - const int mb_w = io->mb_w; - const WEBP_CSP_MODE colorspace = p->output->colorspace; - const WebPRGBABuffer* const buf = &p->output->u.RGBA; - int num_rows; - const int start_y = GetAlphaSourceRow(io, &alpha, &num_rows); - uint8_t* const base_rgba = buf->rgba + start_y * buf->stride; -#ifdef WEBP_SWAP_16BIT_CSP - uint8_t* alpha_dst = base_rgba; -#else - uint8_t* alpha_dst = base_rgba + 1; -#endif - uint32_t alpha_mask = 0x0f; - int i, j; - for (j = 0; j < num_rows; ++j) { - for (i = 0; i < mb_w; ++i) { - // Fill in the alpha value (converted to 4 bits). - const uint32_t alpha_value = alpha[i] >> 4; - alpha_dst[2 * i] = (alpha_dst[2 * i] & 0xf0) | alpha_value; - alpha_mask &= alpha_value; - } - alpha += io->width; - alpha_dst += buf->stride; - } - (void)expected_num_lines_out; - assert(expected_num_lines_out == num_rows); - if (alpha_mask != 0x0f && WebPIsPremultipliedMode(colorspace)) { - WebPApplyAlphaMultiply4444(base_rgba, mb_w, num_rows, buf->stride); - } - } - return 0; -} - -//------------------------------------------------------------------------------ -// YUV rescaling (no final RGB conversion needed) - -static int Rescale(const uint8_t* src, int src_stride, - int new_lines, WebPRescaler* const wrk) { - int num_lines_out = 0; - while (new_lines > 0) { // import new contributions of source rows. - const int lines_in = WebPRescalerImport(wrk, new_lines, src, src_stride); - src += lines_in * src_stride; - new_lines -= lines_in; - num_lines_out += WebPRescalerExport(wrk); // emit output row(s) - } - return num_lines_out; -} - -static int EmitRescaledYUV(const VP8Io* const io, WebPDecParams* const p) { - const int mb_h = io->mb_h; - const int uv_mb_h = (mb_h + 1) >> 1; - WebPRescaler* const scaler = &p->scaler_y; - int num_lines_out = 0; - if (WebPIsAlphaMode(p->output->colorspace) && io->a != NULL) { - // Before rescaling, we premultiply the luma directly into the io->y - // internal buffer. This is OK since these samples are not used for - // intra-prediction (the top samples are saved in cache_y_/u_/v_). - // But we need to cast the const away, though. - WebPMultRows((uint8_t*)io->y, io->y_stride, - io->a, io->width, io->mb_w, mb_h, 0); - } - num_lines_out = Rescale(io->y, io->y_stride, mb_h, scaler); - Rescale(io->u, io->uv_stride, uv_mb_h, &p->scaler_u); - Rescale(io->v, io->uv_stride, uv_mb_h, &p->scaler_v); - return num_lines_out; -} - -static int EmitRescaledAlphaYUV(const VP8Io* const io, WebPDecParams* const p, - int expected_num_lines_out) { - const WebPYUVABuffer* const buf = &p->output->u.YUVA; - if (io->a != NULL) { - uint8_t* dst_y = buf->y + p->last_y * buf->y_stride; - const uint8_t* src_a = buf->a + p->last_y * buf->a_stride; - const int num_lines_out = Rescale(io->a, io->width, io->mb_h, &p->scaler_a); - (void)expected_num_lines_out; - assert(expected_num_lines_out == num_lines_out); - if (num_lines_out > 0) { // unmultiply the Y - WebPMultRows(dst_y, buf->y_stride, src_a, buf->a_stride, - p->scaler_a.dst_width, num_lines_out, 1); - } - } else if (buf->a != NULL) { - // the user requested alpha, but there is none, set it to opaque. - assert(p->last_y + expected_num_lines_out <= io->scaled_height); - FillAlphaPlane(buf->a + p->last_y * buf->a_stride, - io->scaled_width, expected_num_lines_out, buf->a_stride); - } - return 0; -} - -static int InitYUVRescaler(const VP8Io* const io, WebPDecParams* const p) { - const int has_alpha = WebPIsAlphaMode(p->output->colorspace); - const WebPYUVABuffer* const buf = &p->output->u.YUVA; - const int out_width = io->scaled_width; - const int out_height = io->scaled_height; - const int uv_out_width = (out_width + 1) >> 1; - const int uv_out_height = (out_height + 1) >> 1; - const int uv_in_width = (io->mb_w + 1) >> 1; - const int uv_in_height = (io->mb_h + 1) >> 1; - const size_t work_size = 2 * out_width; // scratch memory for luma rescaler - const size_t uv_work_size = 2 * uv_out_width; // and for each u/v ones - size_t tmp_size; - rescaler_t* work; - - tmp_size = (work_size + 2 * uv_work_size) * sizeof(*work); - if (has_alpha) { - tmp_size += work_size * sizeof(*work); - } - p->memory = WebPSafeMalloc(1ULL, tmp_size); - if (p->memory == NULL) { - return 0; // memory error - } - work = (rescaler_t*)p->memory; - WebPRescalerInit(&p->scaler_y, io->mb_w, io->mb_h, - buf->y, out_width, out_height, buf->y_stride, 1, - work); - WebPRescalerInit(&p->scaler_u, uv_in_width, uv_in_height, - buf->u, uv_out_width, uv_out_height, buf->u_stride, 1, - work + work_size); - WebPRescalerInit(&p->scaler_v, uv_in_width, uv_in_height, - buf->v, uv_out_width, uv_out_height, buf->v_stride, 1, - work + work_size + uv_work_size); - p->emit = EmitRescaledYUV; - - if (has_alpha) { - WebPRescalerInit(&p->scaler_a, io->mb_w, io->mb_h, - buf->a, out_width, out_height, buf->a_stride, 1, - work + work_size + 2 * uv_work_size); - p->emit_alpha = EmitRescaledAlphaYUV; - WebPInitAlphaProcessing(); - } - return 1; -} - -//------------------------------------------------------------------------------ -// RGBA rescaling - -static int ExportRGB(WebPDecParams* const p, int y_pos) { - const WebPYUV444Converter convert = - WebPYUV444Converters[p->output->colorspace]; - const WebPRGBABuffer* const buf = &p->output->u.RGBA; - uint8_t* dst = buf->rgba + y_pos * buf->stride; - int num_lines_out = 0; - // For RGB rescaling, because of the YUV420, current scan position - // U/V can be +1/-1 line from the Y one. Hence the double test. - while (WebPRescalerHasPendingOutput(&p->scaler_y) && - WebPRescalerHasPendingOutput(&p->scaler_u)) { - assert(y_pos + num_lines_out < p->output->height); - assert(p->scaler_u.y_accum == p->scaler_v.y_accum); - WebPRescalerExportRow(&p->scaler_y); - WebPRescalerExportRow(&p->scaler_u); - WebPRescalerExportRow(&p->scaler_v); - convert(p->scaler_y.dst, p->scaler_u.dst, p->scaler_v.dst, - dst, p->scaler_y.dst_width); - dst += buf->stride; - ++num_lines_out; - } - return num_lines_out; -} - -static int EmitRescaledRGB(const VP8Io* const io, WebPDecParams* const p) { - const int mb_h = io->mb_h; - const int uv_mb_h = (mb_h + 1) >> 1; - int j = 0, uv_j = 0; - int num_lines_out = 0; - while (j < mb_h) { - const int y_lines_in = - WebPRescalerImport(&p->scaler_y, mb_h - j, - io->y + j * io->y_stride, io->y_stride); - j += y_lines_in; - if (WebPRescaleNeededLines(&p->scaler_u, uv_mb_h - uv_j)) { - const int u_lines_in = - WebPRescalerImport(&p->scaler_u, uv_mb_h - uv_j, - io->u + uv_j * io->uv_stride, io->uv_stride); - const int v_lines_in = - WebPRescalerImport(&p->scaler_v, uv_mb_h - uv_j, - io->v + uv_j * io->uv_stride, io->uv_stride); - (void)v_lines_in; // remove a gcc warning - assert(u_lines_in == v_lines_in); - uv_j += u_lines_in; - } - num_lines_out += ExportRGB(p, p->last_y + num_lines_out); - } - return num_lines_out; -} - -static int ExportAlpha(WebPDecParams* const p, int y_pos, int max_lines_out) { - const WebPRGBABuffer* const buf = &p->output->u.RGBA; - uint8_t* const base_rgba = buf->rgba + y_pos * buf->stride; - const WEBP_CSP_MODE colorspace = p->output->colorspace; - const int alpha_first = - (colorspace == MODE_ARGB || colorspace == MODE_Argb); - uint8_t* dst = base_rgba + (alpha_first ? 0 : 3); - int num_lines_out = 0; - const int is_premult_alpha = WebPIsPremultipliedMode(colorspace); - uint32_t non_opaque = 0; - const int width = p->scaler_a.dst_width; - - while (WebPRescalerHasPendingOutput(&p->scaler_a) && - num_lines_out < max_lines_out) { - assert(y_pos + num_lines_out < p->output->height); - WebPRescalerExportRow(&p->scaler_a); - non_opaque |= WebPDispatchAlpha(p->scaler_a.dst, 0, width, 1, dst, 0); - dst += buf->stride; - ++num_lines_out; - } - if (is_premult_alpha && non_opaque) { - WebPApplyAlphaMultiply(base_rgba, alpha_first, - width, num_lines_out, buf->stride); - } - return num_lines_out; -} - -static int ExportAlphaRGBA4444(WebPDecParams* const p, int y_pos, - int max_lines_out) { - const WebPRGBABuffer* const buf = &p->output->u.RGBA; - uint8_t* const base_rgba = buf->rgba + y_pos * buf->stride; -#ifdef WEBP_SWAP_16BIT_CSP - uint8_t* alpha_dst = base_rgba; -#else - uint8_t* alpha_dst = base_rgba + 1; -#endif - int num_lines_out = 0; - const WEBP_CSP_MODE colorspace = p->output->colorspace; - const int width = p->scaler_a.dst_width; - const int is_premult_alpha = WebPIsPremultipliedMode(colorspace); - uint32_t alpha_mask = 0x0f; - - while (WebPRescalerHasPendingOutput(&p->scaler_a) && - num_lines_out < max_lines_out) { - int i; - assert(y_pos + num_lines_out < p->output->height); - WebPRescalerExportRow(&p->scaler_a); - for (i = 0; i < width; ++i) { - // Fill in the alpha value (converted to 4 bits). - const uint32_t alpha_value = p->scaler_a.dst[i] >> 4; - alpha_dst[2 * i] = (alpha_dst[2 * i] & 0xf0) | alpha_value; - alpha_mask &= alpha_value; - } - alpha_dst += buf->stride; - ++num_lines_out; - } - if (is_premult_alpha && alpha_mask != 0x0f) { - WebPApplyAlphaMultiply4444(base_rgba, width, num_lines_out, buf->stride); - } - return num_lines_out; -} - -static int EmitRescaledAlphaRGB(const VP8Io* const io, WebPDecParams* const p, - int expected_num_out_lines) { - if (io->a != NULL) { - WebPRescaler* const scaler = &p->scaler_a; - int lines_left = expected_num_out_lines; - const int y_end = p->last_y + lines_left; - while (lines_left > 0) { - const int row_offset = scaler->src_y - io->mb_y; - WebPRescalerImport(scaler, io->mb_h + io->mb_y - scaler->src_y, - io->a + row_offset * io->width, io->width); - lines_left -= p->emit_alpha_row(p, y_end - lines_left, lines_left); - } - } - return 0; -} - -static int InitRGBRescaler(const VP8Io* const io, WebPDecParams* const p) { - const int has_alpha = WebPIsAlphaMode(p->output->colorspace); - const int out_width = io->scaled_width; - const int out_height = io->scaled_height; - const int uv_in_width = (io->mb_w + 1) >> 1; - const int uv_in_height = (io->mb_h + 1) >> 1; - const size_t work_size = 2 * out_width; // scratch memory for one rescaler - rescaler_t* work; // rescalers work area - uint8_t* tmp; // tmp storage for scaled YUV444 samples before RGB conversion - size_t tmp_size1, tmp_size2, total_size; - - tmp_size1 = 3 * work_size; - tmp_size2 = 3 * out_width; - if (has_alpha) { - tmp_size1 += work_size; - tmp_size2 += out_width; - } - total_size = tmp_size1 * sizeof(*work) + tmp_size2 * sizeof(*tmp); - p->memory = WebPSafeMalloc(1ULL, total_size); - if (p->memory == NULL) { - return 0; // memory error - } - work = (rescaler_t*)p->memory; - tmp = (uint8_t*)(work + tmp_size1); - WebPRescalerInit(&p->scaler_y, io->mb_w, io->mb_h, - tmp + 0 * out_width, out_width, out_height, 0, 1, - work + 0 * work_size); - WebPRescalerInit(&p->scaler_u, uv_in_width, uv_in_height, - tmp + 1 * out_width, out_width, out_height, 0, 1, - work + 1 * work_size); - WebPRescalerInit(&p->scaler_v, uv_in_width, uv_in_height, - tmp + 2 * out_width, out_width, out_height, 0, 1, - work + 2 * work_size); - p->emit = EmitRescaledRGB; - WebPInitYUV444Converters(); - - if (has_alpha) { - WebPRescalerInit(&p->scaler_a, io->mb_w, io->mb_h, - tmp + 3 * out_width, out_width, out_height, 0, 1, - work + 3 * work_size); - p->emit_alpha = EmitRescaledAlphaRGB; - if (p->output->colorspace == MODE_RGBA_4444 || - p->output->colorspace == MODE_rgbA_4444) { - p->emit_alpha_row = ExportAlphaRGBA4444; - } else { - p->emit_alpha_row = ExportAlpha; - } - WebPInitAlphaProcessing(); - } - return 1; -} - -//------------------------------------------------------------------------------ -// Default custom functions - -static int CustomSetup(VP8Io* io) { - WebPDecParams* const p = (WebPDecParams*)io->opaque; - const WEBP_CSP_MODE colorspace = p->output->colorspace; - const int is_rgb = WebPIsRGBMode(colorspace); - const int is_alpha = WebPIsAlphaMode(colorspace); - - p->memory = NULL; - p->emit = NULL; - p->emit_alpha = NULL; - p->emit_alpha_row = NULL; - if (!WebPIoInitFromOptions(p->options, io, is_alpha ? MODE_YUV : MODE_YUVA)) { - return 0; - } - if (is_alpha && WebPIsPremultipliedMode(colorspace)) { - WebPInitUpsamplers(); - } - if (io->use_scaling) { - const int ok = is_rgb ? InitRGBRescaler(io, p) : InitYUVRescaler(io, p); - if (!ok) { - return 0; // memory error - } - } else { - if (is_rgb) { - WebPInitSamplers(); - p->emit = EmitSampledRGB; // default - if (io->fancy_upsampling) { -#ifdef FANCY_UPSAMPLING - const int uv_width = (io->mb_w + 1) >> 1; - p->memory = WebPSafeMalloc(1ULL, (size_t)(io->mb_w + 2 * uv_width)); - if (p->memory == NULL) { - return 0; // memory error. - } - p->tmp_y = (uint8_t*)p->memory; - p->tmp_u = p->tmp_y + io->mb_w; - p->tmp_v = p->tmp_u + uv_width; - p->emit = EmitFancyRGB; - WebPInitUpsamplers(); -#endif - } - } else { - p->emit = EmitYUV; - } - if (is_alpha) { // need transparency output - p->emit_alpha = - (colorspace == MODE_RGBA_4444 || colorspace == MODE_rgbA_4444) ? - EmitAlphaRGBA4444 - : is_rgb ? EmitAlphaRGB - : EmitAlphaYUV; - if (is_rgb) { - WebPInitAlphaProcessing(); - } - } - } - - if (is_rgb) { - VP8YUVInit(); - } - return 1; -} - -//------------------------------------------------------------------------------ - -static int CustomPut(const VP8Io* io) { - WebPDecParams* const p = (WebPDecParams*)io->opaque; - const int mb_w = io->mb_w; - const int mb_h = io->mb_h; - int num_lines_out; - assert(!(io->mb_y & 1)); - - if (mb_w <= 0 || mb_h <= 0) { - return 0; - } - num_lines_out = p->emit(io, p); - if (p->emit_alpha != NULL) { - p->emit_alpha(io, p, num_lines_out); - } - p->last_y += num_lines_out; - return 1; -} - -//------------------------------------------------------------------------------ - -static void CustomTeardown(const VP8Io* io) { - WebPDecParams* const p = (WebPDecParams*)io->opaque; - WebPSafeFree(p->memory); - p->memory = NULL; -} - -//------------------------------------------------------------------------------ -// Main entry point - -void WebPInitCustomIo(WebPDecParams* const params, VP8Io* const io) { - io->put = CustomPut; - io->setup = CustomSetup; - io->teardown = CustomTeardown; - io->opaque = params; -} - -//------------------------------------------------------------------------------ diff --git a/thirdparty/libwebp/dec/io_dec.c b/thirdparty/libwebp/dec/io_dec.c new file mode 100644 index 0000000000..8bfab86959 --- /dev/null +++ b/thirdparty/libwebp/dec/io_dec.c @@ -0,0 +1,645 @@ +// Copyright 2011 Google Inc. All Rights Reserved. +// +// Use of this source code is governed by a BSD-style license +// that can be found in the COPYING file in the root of the source +// tree. An additional intellectual property rights grant can be found +// in the file PATENTS. All contributing project authors may +// be found in the AUTHORS file in the root of the source tree. +// ----------------------------------------------------------------------------- +// +// functions for sample output. +// +// Author: Skal (pascal.massimino@gmail.com) + +#include +#include +#include "../dec/vp8i_dec.h" +#include "./webpi_dec.h" +#include "../dsp/dsp.h" +#include "../dsp/yuv.h" +#include "../utils/utils.h" + +//------------------------------------------------------------------------------ +// Main YUV<->RGB conversion functions + +static int EmitYUV(const VP8Io* const io, WebPDecParams* const p) { + WebPDecBuffer* output = p->output; + const WebPYUVABuffer* const buf = &output->u.YUVA; + uint8_t* const y_dst = buf->y + io->mb_y * buf->y_stride; + uint8_t* const u_dst = buf->u + (io->mb_y >> 1) * buf->u_stride; + uint8_t* const v_dst = buf->v + (io->mb_y >> 1) * buf->v_stride; + const int mb_w = io->mb_w; + const int mb_h = io->mb_h; + const int uv_w = (mb_w + 1) / 2; + const int uv_h = (mb_h + 1) / 2; + int j; + for (j = 0; j < mb_h; ++j) { + memcpy(y_dst + j * buf->y_stride, io->y + j * io->y_stride, mb_w); + } + for (j = 0; j < uv_h; ++j) { + memcpy(u_dst + j * buf->u_stride, io->u + j * io->uv_stride, uv_w); + memcpy(v_dst + j * buf->v_stride, io->v + j * io->uv_stride, uv_w); + } + return io->mb_h; +} + +// Point-sampling U/V sampler. +static int EmitSampledRGB(const VP8Io* const io, WebPDecParams* const p) { + WebPDecBuffer* const output = p->output; + WebPRGBABuffer* const buf = &output->u.RGBA; + uint8_t* const dst = buf->rgba + io->mb_y * buf->stride; + WebPSamplerProcessPlane(io->y, io->y_stride, + io->u, io->v, io->uv_stride, + dst, buf->stride, io->mb_w, io->mb_h, + WebPSamplers[output->colorspace]); + return io->mb_h; +} + +//------------------------------------------------------------------------------ +// Fancy upsampling + +#ifdef FANCY_UPSAMPLING +static int EmitFancyRGB(const VP8Io* const io, WebPDecParams* const p) { + int num_lines_out = io->mb_h; // a priori guess + const WebPRGBABuffer* const buf = &p->output->u.RGBA; + uint8_t* dst = buf->rgba + io->mb_y * buf->stride; + WebPUpsampleLinePairFunc upsample = WebPUpsamplers[p->output->colorspace]; + const uint8_t* cur_y = io->y; + const uint8_t* cur_u = io->u; + const uint8_t* cur_v = io->v; + const uint8_t* top_u = p->tmp_u; + const uint8_t* top_v = p->tmp_v; + int y = io->mb_y; + const int y_end = io->mb_y + io->mb_h; + const int mb_w = io->mb_w; + const int uv_w = (mb_w + 1) / 2; + + if (y == 0) { + // First line is special cased. We mirror the u/v samples at boundary. + upsample(cur_y, NULL, cur_u, cur_v, cur_u, cur_v, dst, NULL, mb_w); + } else { + // We can finish the left-over line from previous call. + upsample(p->tmp_y, cur_y, top_u, top_v, cur_u, cur_v, + dst - buf->stride, dst, mb_w); + ++num_lines_out; + } + // Loop over each output pairs of row. + for (; y + 2 < y_end; y += 2) { + top_u = cur_u; + top_v = cur_v; + cur_u += io->uv_stride; + cur_v += io->uv_stride; + dst += 2 * buf->stride; + cur_y += 2 * io->y_stride; + upsample(cur_y - io->y_stride, cur_y, + top_u, top_v, cur_u, cur_v, + dst - buf->stride, dst, mb_w); + } + // move to last row + cur_y += io->y_stride; + if (io->crop_top + y_end < io->crop_bottom) { + // Save the unfinished samples for next call (as we're not done yet). + memcpy(p->tmp_y, cur_y, mb_w * sizeof(*p->tmp_y)); + memcpy(p->tmp_u, cur_u, uv_w * sizeof(*p->tmp_u)); + memcpy(p->tmp_v, cur_v, uv_w * sizeof(*p->tmp_v)); + // The fancy upsampler leaves a row unfinished behind + // (except for the very last row) + num_lines_out--; + } else { + // Process the very last row of even-sized picture + if (!(y_end & 1)) { + upsample(cur_y, NULL, cur_u, cur_v, cur_u, cur_v, + dst + buf->stride, NULL, mb_w); + } + } + return num_lines_out; +} + +#endif /* FANCY_UPSAMPLING */ + +//------------------------------------------------------------------------------ + +static void FillAlphaPlane(uint8_t* dst, int w, int h, int stride) { + int j; + for (j = 0; j < h; ++j) { + memset(dst, 0xff, w * sizeof(*dst)); + dst += stride; + } +} + +static int EmitAlphaYUV(const VP8Io* const io, WebPDecParams* const p, + int expected_num_lines_out) { + const uint8_t* alpha = io->a; + const WebPYUVABuffer* const buf = &p->output->u.YUVA; + const int mb_w = io->mb_w; + const int mb_h = io->mb_h; + uint8_t* dst = buf->a + io->mb_y * buf->a_stride; + int j; + (void)expected_num_lines_out; + assert(expected_num_lines_out == mb_h); + if (alpha != NULL) { + for (j = 0; j < mb_h; ++j) { + memcpy(dst, alpha, mb_w * sizeof(*dst)); + alpha += io->width; + dst += buf->a_stride; + } + } else if (buf->a != NULL) { + // the user requested alpha, but there is none, set it to opaque. + FillAlphaPlane(dst, mb_w, mb_h, buf->a_stride); + } + return 0; +} + +static int GetAlphaSourceRow(const VP8Io* const io, + const uint8_t** alpha, int* const num_rows) { + int start_y = io->mb_y; + *num_rows = io->mb_h; + + // Compensate for the 1-line delay of the fancy upscaler. + // This is similar to EmitFancyRGB(). + if (io->fancy_upsampling) { + if (start_y == 0) { + // We don't process the last row yet. It'll be done during the next call. + --*num_rows; + } else { + --start_y; + // Fortunately, *alpha data is persistent, so we can go back + // one row and finish alpha blending, now that the fancy upscaler + // completed the YUV->RGB interpolation. + *alpha -= io->width; + } + if (io->crop_top + io->mb_y + io->mb_h == io->crop_bottom) { + // If it's the very last call, we process all the remaining rows! + *num_rows = io->crop_bottom - io->crop_top - start_y; + } + } + return start_y; +} + +static int EmitAlphaRGB(const VP8Io* const io, WebPDecParams* const p, + int expected_num_lines_out) { + const uint8_t* alpha = io->a; + if (alpha != NULL) { + const int mb_w = io->mb_w; + const WEBP_CSP_MODE colorspace = p->output->colorspace; + const int alpha_first = + (colorspace == MODE_ARGB || colorspace == MODE_Argb); + const WebPRGBABuffer* const buf = &p->output->u.RGBA; + int num_rows; + const int start_y = GetAlphaSourceRow(io, &alpha, &num_rows); + uint8_t* const base_rgba = buf->rgba + start_y * buf->stride; + uint8_t* const dst = base_rgba + (alpha_first ? 0 : 3); + const int has_alpha = WebPDispatchAlpha(alpha, io->width, mb_w, + num_rows, dst, buf->stride); + (void)expected_num_lines_out; + assert(expected_num_lines_out == num_rows); + // has_alpha is true if there's non-trivial alpha to premultiply with. + if (has_alpha && WebPIsPremultipliedMode(colorspace)) { + WebPApplyAlphaMultiply(base_rgba, alpha_first, + mb_w, num_rows, buf->stride); + } + } + return 0; +} + +static int EmitAlphaRGBA4444(const VP8Io* const io, WebPDecParams* const p, + int expected_num_lines_out) { + const uint8_t* alpha = io->a; + if (alpha != NULL) { + const int mb_w = io->mb_w; + const WEBP_CSP_MODE colorspace = p->output->colorspace; + const WebPRGBABuffer* const buf = &p->output->u.RGBA; + int num_rows; + const int start_y = GetAlphaSourceRow(io, &alpha, &num_rows); + uint8_t* const base_rgba = buf->rgba + start_y * buf->stride; +#ifdef WEBP_SWAP_16BIT_CSP + uint8_t* alpha_dst = base_rgba; +#else + uint8_t* alpha_dst = base_rgba + 1; +#endif + uint32_t alpha_mask = 0x0f; + int i, j; + for (j = 0; j < num_rows; ++j) { + for (i = 0; i < mb_w; ++i) { + // Fill in the alpha value (converted to 4 bits). + const uint32_t alpha_value = alpha[i] >> 4; + alpha_dst[2 * i] = (alpha_dst[2 * i] & 0xf0) | alpha_value; + alpha_mask &= alpha_value; + } + alpha += io->width; + alpha_dst += buf->stride; + } + (void)expected_num_lines_out; + assert(expected_num_lines_out == num_rows); + if (alpha_mask != 0x0f && WebPIsPremultipliedMode(colorspace)) { + WebPApplyAlphaMultiply4444(base_rgba, mb_w, num_rows, buf->stride); + } + } + return 0; +} + +//------------------------------------------------------------------------------ +// YUV rescaling (no final RGB conversion needed) + +static int Rescale(const uint8_t* src, int src_stride, + int new_lines, WebPRescaler* const wrk) { + int num_lines_out = 0; + while (new_lines > 0) { // import new contributions of source rows. + const int lines_in = WebPRescalerImport(wrk, new_lines, src, src_stride); + src += lines_in * src_stride; + new_lines -= lines_in; + num_lines_out += WebPRescalerExport(wrk); // emit output row(s) + } + return num_lines_out; +} + +static int EmitRescaledYUV(const VP8Io* const io, WebPDecParams* const p) { + const int mb_h = io->mb_h; + const int uv_mb_h = (mb_h + 1) >> 1; + WebPRescaler* const scaler = p->scaler_y; + int num_lines_out = 0; + if (WebPIsAlphaMode(p->output->colorspace) && io->a != NULL) { + // Before rescaling, we premultiply the luma directly into the io->y + // internal buffer. This is OK since these samples are not used for + // intra-prediction (the top samples are saved in cache_y_/u_/v_). + // But we need to cast the const away, though. + WebPMultRows((uint8_t*)io->y, io->y_stride, + io->a, io->width, io->mb_w, mb_h, 0); + } + num_lines_out = Rescale(io->y, io->y_stride, mb_h, scaler); + Rescale(io->u, io->uv_stride, uv_mb_h, p->scaler_u); + Rescale(io->v, io->uv_stride, uv_mb_h, p->scaler_v); + return num_lines_out; +} + +static int EmitRescaledAlphaYUV(const VP8Io* const io, WebPDecParams* const p, + int expected_num_lines_out) { + const WebPYUVABuffer* const buf = &p->output->u.YUVA; + uint8_t* const dst_a = buf->a + p->last_y * buf->a_stride; + if (io->a != NULL) { + uint8_t* const dst_y = buf->y + p->last_y * buf->y_stride; + const int num_lines_out = Rescale(io->a, io->width, io->mb_h, p->scaler_a); + assert(expected_num_lines_out == num_lines_out); + if (num_lines_out > 0) { // unmultiply the Y + WebPMultRows(dst_y, buf->y_stride, dst_a, buf->a_stride, + p->scaler_a->dst_width, num_lines_out, 1); + } + } else if (buf->a != NULL) { + // the user requested alpha, but there is none, set it to opaque. + assert(p->last_y + expected_num_lines_out <= io->scaled_height); + FillAlphaPlane(dst_a, io->scaled_width, expected_num_lines_out, + buf->a_stride); + } + return 0; +} + +static int InitYUVRescaler(const VP8Io* const io, WebPDecParams* const p) { + const int has_alpha = WebPIsAlphaMode(p->output->colorspace); + const WebPYUVABuffer* const buf = &p->output->u.YUVA; + const int out_width = io->scaled_width; + const int out_height = io->scaled_height; + const int uv_out_width = (out_width + 1) >> 1; + const int uv_out_height = (out_height + 1) >> 1; + const int uv_in_width = (io->mb_w + 1) >> 1; + const int uv_in_height = (io->mb_h + 1) >> 1; + const size_t work_size = 2 * out_width; // scratch memory for luma rescaler + const size_t uv_work_size = 2 * uv_out_width; // and for each u/v ones + size_t tmp_size, rescaler_size; + rescaler_t* work; + WebPRescaler* scalers; + const int num_rescalers = has_alpha ? 4 : 3; + + tmp_size = (work_size + 2 * uv_work_size) * sizeof(*work); + if (has_alpha) { + tmp_size += work_size * sizeof(*work); + } + rescaler_size = num_rescalers * sizeof(*p->scaler_y) + WEBP_ALIGN_CST; + + p->memory = WebPSafeMalloc(1ULL, tmp_size + rescaler_size); + if (p->memory == NULL) { + return 0; // memory error + } + work = (rescaler_t*)p->memory; + + scalers = (WebPRescaler*)WEBP_ALIGN((const uint8_t*)work + tmp_size); + p->scaler_y = &scalers[0]; + p->scaler_u = &scalers[1]; + p->scaler_v = &scalers[2]; + p->scaler_a = has_alpha ? &scalers[3] : NULL; + + WebPRescalerInit(p->scaler_y, io->mb_w, io->mb_h, + buf->y, out_width, out_height, buf->y_stride, 1, + work); + WebPRescalerInit(p->scaler_u, uv_in_width, uv_in_height, + buf->u, uv_out_width, uv_out_height, buf->u_stride, 1, + work + work_size); + WebPRescalerInit(p->scaler_v, uv_in_width, uv_in_height, + buf->v, uv_out_width, uv_out_height, buf->v_stride, 1, + work + work_size + uv_work_size); + p->emit = EmitRescaledYUV; + + if (has_alpha) { + WebPRescalerInit(p->scaler_a, io->mb_w, io->mb_h, + buf->a, out_width, out_height, buf->a_stride, 1, + work + work_size + 2 * uv_work_size); + p->emit_alpha = EmitRescaledAlphaYUV; + WebPInitAlphaProcessing(); + } + return 1; +} + +//------------------------------------------------------------------------------ +// RGBA rescaling + +static int ExportRGB(WebPDecParams* const p, int y_pos) { + const WebPYUV444Converter convert = + WebPYUV444Converters[p->output->colorspace]; + const WebPRGBABuffer* const buf = &p->output->u.RGBA; + uint8_t* dst = buf->rgba + y_pos * buf->stride; + int num_lines_out = 0; + // For RGB rescaling, because of the YUV420, current scan position + // U/V can be +1/-1 line from the Y one. Hence the double test. + while (WebPRescalerHasPendingOutput(p->scaler_y) && + WebPRescalerHasPendingOutput(p->scaler_u)) { + assert(y_pos + num_lines_out < p->output->height); + assert(p->scaler_u->y_accum == p->scaler_v->y_accum); + WebPRescalerExportRow(p->scaler_y); + WebPRescalerExportRow(p->scaler_u); + WebPRescalerExportRow(p->scaler_v); + convert(p->scaler_y->dst, p->scaler_u->dst, p->scaler_v->dst, + dst, p->scaler_y->dst_width); + dst += buf->stride; + ++num_lines_out; + } + return num_lines_out; +} + +static int EmitRescaledRGB(const VP8Io* const io, WebPDecParams* const p) { + const int mb_h = io->mb_h; + const int uv_mb_h = (mb_h + 1) >> 1; + int j = 0, uv_j = 0; + int num_lines_out = 0; + while (j < mb_h) { + const int y_lines_in = + WebPRescalerImport(p->scaler_y, mb_h - j, + io->y + j * io->y_stride, io->y_stride); + j += y_lines_in; + if (WebPRescaleNeededLines(p->scaler_u, uv_mb_h - uv_j)) { + const int u_lines_in = + WebPRescalerImport(p->scaler_u, uv_mb_h - uv_j, + io->u + uv_j * io->uv_stride, io->uv_stride); + const int v_lines_in = + WebPRescalerImport(p->scaler_v, uv_mb_h - uv_j, + io->v + uv_j * io->uv_stride, io->uv_stride); + (void)v_lines_in; // remove a gcc warning + assert(u_lines_in == v_lines_in); + uv_j += u_lines_in; + } + num_lines_out += ExportRGB(p, p->last_y + num_lines_out); + } + return num_lines_out; +} + +static int ExportAlpha(WebPDecParams* const p, int y_pos, int max_lines_out) { + const WebPRGBABuffer* const buf = &p->output->u.RGBA; + uint8_t* const base_rgba = buf->rgba + y_pos * buf->stride; + const WEBP_CSP_MODE colorspace = p->output->colorspace; + const int alpha_first = + (colorspace == MODE_ARGB || colorspace == MODE_Argb); + uint8_t* dst = base_rgba + (alpha_first ? 0 : 3); + int num_lines_out = 0; + const int is_premult_alpha = WebPIsPremultipliedMode(colorspace); + uint32_t non_opaque = 0; + const int width = p->scaler_a->dst_width; + + while (WebPRescalerHasPendingOutput(p->scaler_a) && + num_lines_out < max_lines_out) { + assert(y_pos + num_lines_out < p->output->height); + WebPRescalerExportRow(p->scaler_a); + non_opaque |= WebPDispatchAlpha(p->scaler_a->dst, 0, width, 1, dst, 0); + dst += buf->stride; + ++num_lines_out; + } + if (is_premult_alpha && non_opaque) { + WebPApplyAlphaMultiply(base_rgba, alpha_first, + width, num_lines_out, buf->stride); + } + return num_lines_out; +} + +static int ExportAlphaRGBA4444(WebPDecParams* const p, int y_pos, + int max_lines_out) { + const WebPRGBABuffer* const buf = &p->output->u.RGBA; + uint8_t* const base_rgba = buf->rgba + y_pos * buf->stride; +#ifdef WEBP_SWAP_16BIT_CSP + uint8_t* alpha_dst = base_rgba; +#else + uint8_t* alpha_dst = base_rgba + 1; +#endif + int num_lines_out = 0; + const WEBP_CSP_MODE colorspace = p->output->colorspace; + const int width = p->scaler_a->dst_width; + const int is_premult_alpha = WebPIsPremultipliedMode(colorspace); + uint32_t alpha_mask = 0x0f; + + while (WebPRescalerHasPendingOutput(p->scaler_a) && + num_lines_out < max_lines_out) { + int i; + assert(y_pos + num_lines_out < p->output->height); + WebPRescalerExportRow(p->scaler_a); + for (i = 0; i < width; ++i) { + // Fill in the alpha value (converted to 4 bits). + const uint32_t alpha_value = p->scaler_a->dst[i] >> 4; + alpha_dst[2 * i] = (alpha_dst[2 * i] & 0xf0) | alpha_value; + alpha_mask &= alpha_value; + } + alpha_dst += buf->stride; + ++num_lines_out; + } + if (is_premult_alpha && alpha_mask != 0x0f) { + WebPApplyAlphaMultiply4444(base_rgba, width, num_lines_out, buf->stride); + } + return num_lines_out; +} + +static int EmitRescaledAlphaRGB(const VP8Io* const io, WebPDecParams* const p, + int expected_num_out_lines) { + if (io->a != NULL) { + WebPRescaler* const scaler = p->scaler_a; + int lines_left = expected_num_out_lines; + const int y_end = p->last_y + lines_left; + while (lines_left > 0) { + const int row_offset = scaler->src_y - io->mb_y; + WebPRescalerImport(scaler, io->mb_h + io->mb_y - scaler->src_y, + io->a + row_offset * io->width, io->width); + lines_left -= p->emit_alpha_row(p, y_end - lines_left, lines_left); + } + } + return 0; +} + +static int InitRGBRescaler(const VP8Io* const io, WebPDecParams* const p) { + const int has_alpha = WebPIsAlphaMode(p->output->colorspace); + const int out_width = io->scaled_width; + const int out_height = io->scaled_height; + const int uv_in_width = (io->mb_w + 1) >> 1; + const int uv_in_height = (io->mb_h + 1) >> 1; + const size_t work_size = 2 * out_width; // scratch memory for one rescaler + rescaler_t* work; // rescalers work area + uint8_t* tmp; // tmp storage for scaled YUV444 samples before RGB conversion + size_t tmp_size1, tmp_size2, total_size, rescaler_size; + WebPRescaler* scalers; + const int num_rescalers = has_alpha ? 4 : 3; + + tmp_size1 = 3 * work_size; + tmp_size2 = 3 * out_width; + if (has_alpha) { + tmp_size1 += work_size; + tmp_size2 += out_width; + } + total_size = tmp_size1 * sizeof(*work) + tmp_size2 * sizeof(*tmp); + rescaler_size = num_rescalers * sizeof(*p->scaler_y) + WEBP_ALIGN_CST; + + p->memory = WebPSafeMalloc(1ULL, total_size + rescaler_size); + if (p->memory == NULL) { + return 0; // memory error + } + work = (rescaler_t*)p->memory; + tmp = (uint8_t*)(work + tmp_size1); + + scalers = (WebPRescaler*)WEBP_ALIGN((const uint8_t*)work + total_size); + p->scaler_y = &scalers[0]; + p->scaler_u = &scalers[1]; + p->scaler_v = &scalers[2]; + p->scaler_a = has_alpha ? &scalers[3] : NULL; + + WebPRescalerInit(p->scaler_y, io->mb_w, io->mb_h, + tmp + 0 * out_width, out_width, out_height, 0, 1, + work + 0 * work_size); + WebPRescalerInit(p->scaler_u, uv_in_width, uv_in_height, + tmp + 1 * out_width, out_width, out_height, 0, 1, + work + 1 * work_size); + WebPRescalerInit(p->scaler_v, uv_in_width, uv_in_height, + tmp + 2 * out_width, out_width, out_height, 0, 1, + work + 2 * work_size); + p->emit = EmitRescaledRGB; + WebPInitYUV444Converters(); + + if (has_alpha) { + WebPRescalerInit(p->scaler_a, io->mb_w, io->mb_h, + tmp + 3 * out_width, out_width, out_height, 0, 1, + work + 3 * work_size); + p->emit_alpha = EmitRescaledAlphaRGB; + if (p->output->colorspace == MODE_RGBA_4444 || + p->output->colorspace == MODE_rgbA_4444) { + p->emit_alpha_row = ExportAlphaRGBA4444; + } else { + p->emit_alpha_row = ExportAlpha; + } + WebPInitAlphaProcessing(); + } + return 1; +} + +//------------------------------------------------------------------------------ +// Default custom functions + +static int CustomSetup(VP8Io* io) { + WebPDecParams* const p = (WebPDecParams*)io->opaque; + const WEBP_CSP_MODE colorspace = p->output->colorspace; + const int is_rgb = WebPIsRGBMode(colorspace); + const int is_alpha = WebPIsAlphaMode(colorspace); + + p->memory = NULL; + p->emit = NULL; + p->emit_alpha = NULL; + p->emit_alpha_row = NULL; + if (!WebPIoInitFromOptions(p->options, io, is_alpha ? MODE_YUV : MODE_YUVA)) { + return 0; + } + if (is_alpha && WebPIsPremultipliedMode(colorspace)) { + WebPInitUpsamplers(); + } + if (io->use_scaling) { + const int ok = is_rgb ? InitRGBRescaler(io, p) : InitYUVRescaler(io, p); + if (!ok) { + return 0; // memory error + } + } else { + if (is_rgb) { + WebPInitSamplers(); + p->emit = EmitSampledRGB; // default + if (io->fancy_upsampling) { +#ifdef FANCY_UPSAMPLING + const int uv_width = (io->mb_w + 1) >> 1; + p->memory = WebPSafeMalloc(1ULL, (size_t)(io->mb_w + 2 * uv_width)); + if (p->memory == NULL) { + return 0; // memory error. + } + p->tmp_y = (uint8_t*)p->memory; + p->tmp_u = p->tmp_y + io->mb_w; + p->tmp_v = p->tmp_u + uv_width; + p->emit = EmitFancyRGB; + WebPInitUpsamplers(); +#endif + } + } else { + p->emit = EmitYUV; + } + if (is_alpha) { // need transparency output + p->emit_alpha = + (colorspace == MODE_RGBA_4444 || colorspace == MODE_rgbA_4444) ? + EmitAlphaRGBA4444 + : is_rgb ? EmitAlphaRGB + : EmitAlphaYUV; + if (is_rgb) { + WebPInitAlphaProcessing(); + } + } + } + + if (is_rgb) { + VP8YUVInit(); + } + return 1; +} + +//------------------------------------------------------------------------------ + +static int CustomPut(const VP8Io* io) { + WebPDecParams* const p = (WebPDecParams*)io->opaque; + const int mb_w = io->mb_w; + const int mb_h = io->mb_h; + int num_lines_out; + assert(!(io->mb_y & 1)); + + if (mb_w <= 0 || mb_h <= 0) { + return 0; + } + num_lines_out = p->emit(io, p); + if (p->emit_alpha != NULL) { + p->emit_alpha(io, p, num_lines_out); + } + p->last_y += num_lines_out; + return 1; +} + +//------------------------------------------------------------------------------ + +static void CustomTeardown(const VP8Io* io) { + WebPDecParams* const p = (WebPDecParams*)io->opaque; + WebPSafeFree(p->memory); + p->memory = NULL; +} + +//------------------------------------------------------------------------------ +// Main entry point + +void WebPInitCustomIo(WebPDecParams* const params, VP8Io* const io) { + io->put = CustomPut; + io->setup = CustomSetup; + io->teardown = CustomTeardown; + io->opaque = params; +} + +//------------------------------------------------------------------------------ diff --git a/thirdparty/libwebp/dec/quant.c b/thirdparty/libwebp/dec/quant.c deleted file mode 100644 index 5b648f942c..0000000000 --- a/thirdparty/libwebp/dec/quant.c +++ /dev/null @@ -1,110 +0,0 @@ -// Copyright 2010 Google Inc. All Rights Reserved. -// -// Use of this source code is governed by a BSD-style license -// that can be found in the COPYING file in the root of the source -// tree. An additional intellectual property rights grant can be found -// in the file PATENTS. All contributing project authors may -// be found in the AUTHORS file in the root of the source tree. -// ----------------------------------------------------------------------------- -// -// Quantizer initialization -// -// Author: Skal (pascal.massimino@gmail.com) - -#include "./vp8i.h" - -static WEBP_INLINE int clip(int v, int M) { - return v < 0 ? 0 : v > M ? M : v; -} - -// Paragraph 14.1 -static const uint8_t kDcTable[128] = { - 4, 5, 6, 7, 8, 9, 10, 10, - 11, 12, 13, 14, 15, 16, 17, 17, - 18, 19, 20, 20, 21, 21, 22, 22, - 23, 23, 24, 25, 25, 26, 27, 28, - 29, 30, 31, 32, 33, 34, 35, 36, - 37, 37, 38, 39, 40, 41, 42, 43, - 44, 45, 46, 46, 47, 48, 49, 50, - 51, 52, 53, 54, 55, 56, 57, 58, - 59, 60, 61, 62, 63, 64, 65, 66, - 67, 68, 69, 70, 71, 72, 73, 74, - 75, 76, 76, 77, 78, 79, 80, 81, - 82, 83, 84, 85, 86, 87, 88, 89, - 91, 93, 95, 96, 98, 100, 101, 102, - 104, 106, 108, 110, 112, 114, 116, 118, - 122, 124, 126, 128, 130, 132, 134, 136, - 138, 140, 143, 145, 148, 151, 154, 157 -}; - -static const uint16_t kAcTable[128] = { - 4, 5, 6, 7, 8, 9, 10, 11, - 12, 13, 14, 15, 16, 17, 18, 19, - 20, 21, 22, 23, 24, 25, 26, 27, - 28, 29, 30, 31, 32, 33, 34, 35, - 36, 37, 38, 39, 40, 41, 42, 43, - 44, 45, 46, 47, 48, 49, 50, 51, - 52, 53, 54, 55, 56, 57, 58, 60, - 62, 64, 66, 68, 70, 72, 74, 76, - 78, 80, 82, 84, 86, 88, 90, 92, - 94, 96, 98, 100, 102, 104, 106, 108, - 110, 112, 114, 116, 119, 122, 125, 128, - 131, 134, 137, 140, 143, 146, 149, 152, - 155, 158, 161, 164, 167, 170, 173, 177, - 181, 185, 189, 193, 197, 201, 205, 209, - 213, 217, 221, 225, 229, 234, 239, 245, - 249, 254, 259, 264, 269, 274, 279, 284 -}; - -//------------------------------------------------------------------------------ -// Paragraph 9.6 - -void VP8ParseQuant(VP8Decoder* const dec) { - VP8BitReader* const br = &dec->br_; - const int base_q0 = VP8GetValue(br, 7); - const int dqy1_dc = VP8Get(br) ? VP8GetSignedValue(br, 4) : 0; - const int dqy2_dc = VP8Get(br) ? VP8GetSignedValue(br, 4) : 0; - const int dqy2_ac = VP8Get(br) ? VP8GetSignedValue(br, 4) : 0; - const int dquv_dc = VP8Get(br) ? VP8GetSignedValue(br, 4) : 0; - const int dquv_ac = VP8Get(br) ? VP8GetSignedValue(br, 4) : 0; - - const VP8SegmentHeader* const hdr = &dec->segment_hdr_; - int i; - - for (i = 0; i < NUM_MB_SEGMENTS; ++i) { - int q; - if (hdr->use_segment_) { - q = hdr->quantizer_[i]; - if (!hdr->absolute_delta_) { - q += base_q0; - } - } else { - if (i > 0) { - dec->dqm_[i] = dec->dqm_[0]; - continue; - } else { - q = base_q0; - } - } - { - VP8QuantMatrix* const m = &dec->dqm_[i]; - m->y1_mat_[0] = kDcTable[clip(q + dqy1_dc, 127)]; - m->y1_mat_[1] = kAcTable[clip(q + 0, 127)]; - - m->y2_mat_[0] = kDcTable[clip(q + dqy2_dc, 127)] * 2; - // For all x in [0..284], x*155/100 is bitwise equal to (x*101581) >> 16. - // The smallest precision for that is '(x*6349) >> 12' but 16 is a good - // word size. - m->y2_mat_[1] = (kAcTable[clip(q + dqy2_ac, 127)] * 101581) >> 16; - if (m->y2_mat_[1] < 8) m->y2_mat_[1] = 8; - - m->uv_mat_[0] = kDcTable[clip(q + dquv_dc, 117)]; - m->uv_mat_[1] = kAcTable[clip(q + dquv_ac, 127)]; - - m->uv_quant_ = q + dquv_ac; // for dithering strength evaluation - } - } -} - -//------------------------------------------------------------------------------ - diff --git a/thirdparty/libwebp/dec/quant_dec.c b/thirdparty/libwebp/dec/quant_dec.c new file mode 100644 index 0000000000..14e3198946 --- /dev/null +++ b/thirdparty/libwebp/dec/quant_dec.c @@ -0,0 +1,110 @@ +// Copyright 2010 Google Inc. All Rights Reserved. +// +// Use of this source code is governed by a BSD-style license +// that can be found in the COPYING file in the root of the source +// tree. An additional intellectual property rights grant can be found +// in the file PATENTS. All contributing project authors may +// be found in the AUTHORS file in the root of the source tree. +// ----------------------------------------------------------------------------- +// +// Quantizer initialization +// +// Author: Skal (pascal.massimino@gmail.com) + +#include "./vp8i_dec.h" + +static WEBP_INLINE int clip(int v, int M) { + return v < 0 ? 0 : v > M ? M : v; +} + +// Paragraph 14.1 +static const uint8_t kDcTable[128] = { + 4, 5, 6, 7, 8, 9, 10, 10, + 11, 12, 13, 14, 15, 16, 17, 17, + 18, 19, 20, 20, 21, 21, 22, 22, + 23, 23, 24, 25, 25, 26, 27, 28, + 29, 30, 31, 32, 33, 34, 35, 36, + 37, 37, 38, 39, 40, 41, 42, 43, + 44, 45, 46, 46, 47, 48, 49, 50, + 51, 52, 53, 54, 55, 56, 57, 58, + 59, 60, 61, 62, 63, 64, 65, 66, + 67, 68, 69, 70, 71, 72, 73, 74, + 75, 76, 76, 77, 78, 79, 80, 81, + 82, 83, 84, 85, 86, 87, 88, 89, + 91, 93, 95, 96, 98, 100, 101, 102, + 104, 106, 108, 110, 112, 114, 116, 118, + 122, 124, 126, 128, 130, 132, 134, 136, + 138, 140, 143, 145, 148, 151, 154, 157 +}; + +static const uint16_t kAcTable[128] = { + 4, 5, 6, 7, 8, 9, 10, 11, + 12, 13, 14, 15, 16, 17, 18, 19, + 20, 21, 22, 23, 24, 25, 26, 27, + 28, 29, 30, 31, 32, 33, 34, 35, + 36, 37, 38, 39, 40, 41, 42, 43, + 44, 45, 46, 47, 48, 49, 50, 51, + 52, 53, 54, 55, 56, 57, 58, 60, + 62, 64, 66, 68, 70, 72, 74, 76, + 78, 80, 82, 84, 86, 88, 90, 92, + 94, 96, 98, 100, 102, 104, 106, 108, + 110, 112, 114, 116, 119, 122, 125, 128, + 131, 134, 137, 140, 143, 146, 149, 152, + 155, 158, 161, 164, 167, 170, 173, 177, + 181, 185, 189, 193, 197, 201, 205, 209, + 213, 217, 221, 225, 229, 234, 239, 245, + 249, 254, 259, 264, 269, 274, 279, 284 +}; + +//------------------------------------------------------------------------------ +// Paragraph 9.6 + +void VP8ParseQuant(VP8Decoder* const dec) { + VP8BitReader* const br = &dec->br_; + const int base_q0 = VP8GetValue(br, 7); + const int dqy1_dc = VP8Get(br) ? VP8GetSignedValue(br, 4) : 0; + const int dqy2_dc = VP8Get(br) ? VP8GetSignedValue(br, 4) : 0; + const int dqy2_ac = VP8Get(br) ? VP8GetSignedValue(br, 4) : 0; + const int dquv_dc = VP8Get(br) ? VP8GetSignedValue(br, 4) : 0; + const int dquv_ac = VP8Get(br) ? VP8GetSignedValue(br, 4) : 0; + + const VP8SegmentHeader* const hdr = &dec->segment_hdr_; + int i; + + for (i = 0; i < NUM_MB_SEGMENTS; ++i) { + int q; + if (hdr->use_segment_) { + q = hdr->quantizer_[i]; + if (!hdr->absolute_delta_) { + q += base_q0; + } + } else { + if (i > 0) { + dec->dqm_[i] = dec->dqm_[0]; + continue; + } else { + q = base_q0; + } + } + { + VP8QuantMatrix* const m = &dec->dqm_[i]; + m->y1_mat_[0] = kDcTable[clip(q + dqy1_dc, 127)]; + m->y1_mat_[1] = kAcTable[clip(q + 0, 127)]; + + m->y2_mat_[0] = kDcTable[clip(q + dqy2_dc, 127)] * 2; + // For all x in [0..284], x*155/100 is bitwise equal to (x*101581) >> 16. + // The smallest precision for that is '(x*6349) >> 12' but 16 is a good + // word size. + m->y2_mat_[1] = (kAcTable[clip(q + dqy2_ac, 127)] * 101581) >> 16; + if (m->y2_mat_[1] < 8) m->y2_mat_[1] = 8; + + m->uv_mat_[0] = kDcTable[clip(q + dquv_dc, 117)]; + m->uv_mat_[1] = kAcTable[clip(q + dquv_ac, 127)]; + + m->uv_quant_ = q + dquv_ac; // for dithering strength evaluation + } + } +} + +//------------------------------------------------------------------------------ + diff --git a/thirdparty/libwebp/dec/tree.c b/thirdparty/libwebp/dec/tree.c deleted file mode 100644 index c2007ea733..0000000000 --- a/thirdparty/libwebp/dec/tree.c +++ /dev/null @@ -1,525 +0,0 @@ -// Copyright 2010 Google Inc. All Rights Reserved. -// -// Use of this source code is governed by a BSD-style license -// that can be found in the COPYING file in the root of the source -// tree. An additional intellectual property rights grant can be found -// in the file PATENTS. All contributing project authors may -// be found in the AUTHORS file in the root of the source tree. -// ----------------------------------------------------------------------------- -// -// Coding trees and probas -// -// Author: Skal (pascal.massimino@gmail.com) - -#include "./vp8i.h" -#include "../utils/bit_reader_inl.h" - -#define USE_GENERIC_TREE - -#ifdef USE_GENERIC_TREE -static const int8_t kYModesIntra4[18] = { - -B_DC_PRED, 1, - -B_TM_PRED, 2, - -B_VE_PRED, 3, - 4, 6, - -B_HE_PRED, 5, - -B_RD_PRED, -B_VR_PRED, - -B_LD_PRED, 7, - -B_VL_PRED, 8, - -B_HD_PRED, -B_HU_PRED -}; -#endif - -//------------------------------------------------------------------------------ -// Default probabilities - -// Paragraph 13.5 -static const uint8_t - CoeffsProba0[NUM_TYPES][NUM_BANDS][NUM_CTX][NUM_PROBAS] = { - { { { 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128 }, - { 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128 }, - { 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128 } - }, - { { 253, 136, 254, 255, 228, 219, 128, 128, 128, 128, 128 }, - { 189, 129, 242, 255, 227, 213, 255, 219, 128, 128, 128 }, - { 106, 126, 227, 252, 214, 209, 255, 255, 128, 128, 128 } - }, - { { 1, 98, 248, 255, 236, 226, 255, 255, 128, 128, 128 }, - { 181, 133, 238, 254, 221, 234, 255, 154, 128, 128, 128 }, - { 78, 134, 202, 247, 198, 180, 255, 219, 128, 128, 128 }, - }, - { { 1, 185, 249, 255, 243, 255, 128, 128, 128, 128, 128 }, - { 184, 150, 247, 255, 236, 224, 128, 128, 128, 128, 128 }, - { 77, 110, 216, 255, 236, 230, 128, 128, 128, 128, 128 }, - }, - { { 1, 101, 251, 255, 241, 255, 128, 128, 128, 128, 128 }, - { 170, 139, 241, 252, 236, 209, 255, 255, 128, 128, 128 }, - { 37, 116, 196, 243, 228, 255, 255, 255, 128, 128, 128 } - }, - { { 1, 204, 254, 255, 245, 255, 128, 128, 128, 128, 128 }, - { 207, 160, 250, 255, 238, 128, 128, 128, 128, 128, 128 }, - { 102, 103, 231, 255, 211, 171, 128, 128, 128, 128, 128 } - }, - { { 1, 152, 252, 255, 240, 255, 128, 128, 128, 128, 128 }, - { 177, 135, 243, 255, 234, 225, 128, 128, 128, 128, 128 }, - { 80, 129, 211, 255, 194, 224, 128, 128, 128, 128, 128 } - }, - { { 1, 1, 255, 128, 128, 128, 128, 128, 128, 128, 128 }, - { 246, 1, 255, 128, 128, 128, 128, 128, 128, 128, 128 }, - { 255, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128 } - } - }, - { { { 198, 35, 237, 223, 193, 187, 162, 160, 145, 155, 62 }, - { 131, 45, 198, 221, 172, 176, 220, 157, 252, 221, 1 }, - { 68, 47, 146, 208, 149, 167, 221, 162, 255, 223, 128 } - }, - { { 1, 149, 241, 255, 221, 224, 255, 255, 128, 128, 128 }, - { 184, 141, 234, 253, 222, 220, 255, 199, 128, 128, 128 }, - { 81, 99, 181, 242, 176, 190, 249, 202, 255, 255, 128 } - }, - { { 1, 129, 232, 253, 214, 197, 242, 196, 255, 255, 128 }, - { 99, 121, 210, 250, 201, 198, 255, 202, 128, 128, 128 }, - { 23, 91, 163, 242, 170, 187, 247, 210, 255, 255, 128 } - }, - { { 1, 200, 246, 255, 234, 255, 128, 128, 128, 128, 128 }, - { 109, 178, 241, 255, 231, 245, 255, 255, 128, 128, 128 }, - { 44, 130, 201, 253, 205, 192, 255, 255, 128, 128, 128 } - }, - { { 1, 132, 239, 251, 219, 209, 255, 165, 128, 128, 128 }, - { 94, 136, 225, 251, 218, 190, 255, 255, 128, 128, 128 }, - { 22, 100, 174, 245, 186, 161, 255, 199, 128, 128, 128 } - }, - { { 1, 182, 249, 255, 232, 235, 128, 128, 128, 128, 128 }, - { 124, 143, 241, 255, 227, 234, 128, 128, 128, 128, 128 }, - { 35, 77, 181, 251, 193, 211, 255, 205, 128, 128, 128 } - }, - { { 1, 157, 247, 255, 236, 231, 255, 255, 128, 128, 128 }, - { 121, 141, 235, 255, 225, 227, 255, 255, 128, 128, 128 }, - { 45, 99, 188, 251, 195, 217, 255, 224, 128, 128, 128 } - }, - { { 1, 1, 251, 255, 213, 255, 128, 128, 128, 128, 128 }, - { 203, 1, 248, 255, 255, 128, 128, 128, 128, 128, 128 }, - { 137, 1, 177, 255, 224, 255, 128, 128, 128, 128, 128 } - } - }, - { { { 253, 9, 248, 251, 207, 208, 255, 192, 128, 128, 128 }, - { 175, 13, 224, 243, 193, 185, 249, 198, 255, 255, 128 }, - { 73, 17, 171, 221, 161, 179, 236, 167, 255, 234, 128 } - }, - { { 1, 95, 247, 253, 212, 183, 255, 255, 128, 128, 128 }, - { 239, 90, 244, 250, 211, 209, 255, 255, 128, 128, 128 }, - { 155, 77, 195, 248, 188, 195, 255, 255, 128, 128, 128 } - }, - { { 1, 24, 239, 251, 218, 219, 255, 205, 128, 128, 128 }, - { 201, 51, 219, 255, 196, 186, 128, 128, 128, 128, 128 }, - { 69, 46, 190, 239, 201, 218, 255, 228, 128, 128, 128 } - }, - { { 1, 191, 251, 255, 255, 128, 128, 128, 128, 128, 128 }, - { 223, 165, 249, 255, 213, 255, 128, 128, 128, 128, 128 }, - { 141, 124, 248, 255, 255, 128, 128, 128, 128, 128, 128 } - }, - { { 1, 16, 248, 255, 255, 128, 128, 128, 128, 128, 128 }, - { 190, 36, 230, 255, 236, 255, 128, 128, 128, 128, 128 }, - { 149, 1, 255, 128, 128, 128, 128, 128, 128, 128, 128 } - }, - { { 1, 226, 255, 128, 128, 128, 128, 128, 128, 128, 128 }, - { 247, 192, 255, 128, 128, 128, 128, 128, 128, 128, 128 }, - { 240, 128, 255, 128, 128, 128, 128, 128, 128, 128, 128 } - }, - { { 1, 134, 252, 255, 255, 128, 128, 128, 128, 128, 128 }, - { 213, 62, 250, 255, 255, 128, 128, 128, 128, 128, 128 }, - { 55, 93, 255, 128, 128, 128, 128, 128, 128, 128, 128 } - }, - { { 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128 }, - { 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128 }, - { 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128 } - } - }, - { { { 202, 24, 213, 235, 186, 191, 220, 160, 240, 175, 255 }, - { 126, 38, 182, 232, 169, 184, 228, 174, 255, 187, 128 }, - { 61, 46, 138, 219, 151, 178, 240, 170, 255, 216, 128 } - }, - { { 1, 112, 230, 250, 199, 191, 247, 159, 255, 255, 128 }, - { 166, 109, 228, 252, 211, 215, 255, 174, 128, 128, 128 }, - { 39, 77, 162, 232, 172, 180, 245, 178, 255, 255, 128 } - }, - { { 1, 52, 220, 246, 198, 199, 249, 220, 255, 255, 128 }, - { 124, 74, 191, 243, 183, 193, 250, 221, 255, 255, 128 }, - { 24, 71, 130, 219, 154, 170, 243, 182, 255, 255, 128 } - }, - { { 1, 182, 225, 249, 219, 240, 255, 224, 128, 128, 128 }, - { 149, 150, 226, 252, 216, 205, 255, 171, 128, 128, 128 }, - { 28, 108, 170, 242, 183, 194, 254, 223, 255, 255, 128 } - }, - { { 1, 81, 230, 252, 204, 203, 255, 192, 128, 128, 128 }, - { 123, 102, 209, 247, 188, 196, 255, 233, 128, 128, 128 }, - { 20, 95, 153, 243, 164, 173, 255, 203, 128, 128, 128 } - }, - { { 1, 222, 248, 255, 216, 213, 128, 128, 128, 128, 128 }, - { 168, 175, 246, 252, 235, 205, 255, 255, 128, 128, 128 }, - { 47, 116, 215, 255, 211, 212, 255, 255, 128, 128, 128 } - }, - { { 1, 121, 236, 253, 212, 214, 255, 255, 128, 128, 128 }, - { 141, 84, 213, 252, 201, 202, 255, 219, 128, 128, 128 }, - { 42, 80, 160, 240, 162, 185, 255, 205, 128, 128, 128 } - }, - { { 1, 1, 255, 128, 128, 128, 128, 128, 128, 128, 128 }, - { 244, 1, 255, 128, 128, 128, 128, 128, 128, 128, 128 }, - { 238, 1, 255, 128, 128, 128, 128, 128, 128, 128, 128 } - } - } -}; - -// Paragraph 11.5 -static const uint8_t kBModesProba[NUM_BMODES][NUM_BMODES][NUM_BMODES - 1] = { - { { 231, 120, 48, 89, 115, 113, 120, 152, 112 }, - { 152, 179, 64, 126, 170, 118, 46, 70, 95 }, - { 175, 69, 143, 80, 85, 82, 72, 155, 103 }, - { 56, 58, 10, 171, 218, 189, 17, 13, 152 }, - { 114, 26, 17, 163, 44, 195, 21, 10, 173 }, - { 121, 24, 80, 195, 26, 62, 44, 64, 85 }, - { 144, 71, 10, 38, 171, 213, 144, 34, 26 }, - { 170, 46, 55, 19, 136, 160, 33, 206, 71 }, - { 63, 20, 8, 114, 114, 208, 12, 9, 226 }, - { 81, 40, 11, 96, 182, 84, 29, 16, 36 } }, - { { 134, 183, 89, 137, 98, 101, 106, 165, 148 }, - { 72, 187, 100, 130, 157, 111, 32, 75, 80 }, - { 66, 102, 167, 99, 74, 62, 40, 234, 128 }, - { 41, 53, 9, 178, 241, 141, 26, 8, 107 }, - { 74, 43, 26, 146, 73, 166, 49, 23, 157 }, - { 65, 38, 105, 160, 51, 52, 31, 115, 128 }, - { 104, 79, 12, 27, 217, 255, 87, 17, 7 }, - { 87, 68, 71, 44, 114, 51, 15, 186, 23 }, - { 47, 41, 14, 110, 182, 183, 21, 17, 194 }, - { 66, 45, 25, 102, 197, 189, 23, 18, 22 } }, - { { 88, 88, 147, 150, 42, 46, 45, 196, 205 }, - { 43, 97, 183, 117, 85, 38, 35, 179, 61 }, - { 39, 53, 200, 87, 26, 21, 43, 232, 171 }, - { 56, 34, 51, 104, 114, 102, 29, 93, 77 }, - { 39, 28, 85, 171, 58, 165, 90, 98, 64 }, - { 34, 22, 116, 206, 23, 34, 43, 166, 73 }, - { 107, 54, 32, 26, 51, 1, 81, 43, 31 }, - { 68, 25, 106, 22, 64, 171, 36, 225, 114 }, - { 34, 19, 21, 102, 132, 188, 16, 76, 124 }, - { 62, 18, 78, 95, 85, 57, 50, 48, 51 } }, - { { 193, 101, 35, 159, 215, 111, 89, 46, 111 }, - { 60, 148, 31, 172, 219, 228, 21, 18, 111 }, - { 112, 113, 77, 85, 179, 255, 38, 120, 114 }, - { 40, 42, 1, 196, 245, 209, 10, 25, 109 }, - { 88, 43, 29, 140, 166, 213, 37, 43, 154 }, - { 61, 63, 30, 155, 67, 45, 68, 1, 209 }, - { 100, 80, 8, 43, 154, 1, 51, 26, 71 }, - { 142, 78, 78, 16, 255, 128, 34, 197, 171 }, - { 41, 40, 5, 102, 211, 183, 4, 1, 221 }, - { 51, 50, 17, 168, 209, 192, 23, 25, 82 } }, - { { 138, 31, 36, 171, 27, 166, 38, 44, 229 }, - { 67, 87, 58, 169, 82, 115, 26, 59, 179 }, - { 63, 59, 90, 180, 59, 166, 93, 73, 154 }, - { 40, 40, 21, 116, 143, 209, 34, 39, 175 }, - { 47, 15, 16, 183, 34, 223, 49, 45, 183 }, - { 46, 17, 33, 183, 6, 98, 15, 32, 183 }, - { 57, 46, 22, 24, 128, 1, 54, 17, 37 }, - { 65, 32, 73, 115, 28, 128, 23, 128, 205 }, - { 40, 3, 9, 115, 51, 192, 18, 6, 223 }, - { 87, 37, 9, 115, 59, 77, 64, 21, 47 } }, - { { 104, 55, 44, 218, 9, 54, 53, 130, 226 }, - { 64, 90, 70, 205, 40, 41, 23, 26, 57 }, - { 54, 57, 112, 184, 5, 41, 38, 166, 213 }, - { 30, 34, 26, 133, 152, 116, 10, 32, 134 }, - { 39, 19, 53, 221, 26, 114, 32, 73, 255 }, - { 31, 9, 65, 234, 2, 15, 1, 118, 73 }, - { 75, 32, 12, 51, 192, 255, 160, 43, 51 }, - { 88, 31, 35, 67, 102, 85, 55, 186, 85 }, - { 56, 21, 23, 111, 59, 205, 45, 37, 192 }, - { 55, 38, 70, 124, 73, 102, 1, 34, 98 } }, - { { 125, 98, 42, 88, 104, 85, 117, 175, 82 }, - { 95, 84, 53, 89, 128, 100, 113, 101, 45 }, - { 75, 79, 123, 47, 51, 128, 81, 171, 1 }, - { 57, 17, 5, 71, 102, 57, 53, 41, 49 }, - { 38, 33, 13, 121, 57, 73, 26, 1, 85 }, - { 41, 10, 67, 138, 77, 110, 90, 47, 114 }, - { 115, 21, 2, 10, 102, 255, 166, 23, 6 }, - { 101, 29, 16, 10, 85, 128, 101, 196, 26 }, - { 57, 18, 10, 102, 102, 213, 34, 20, 43 }, - { 117, 20, 15, 36, 163, 128, 68, 1, 26 } }, - { { 102, 61, 71, 37, 34, 53, 31, 243, 192 }, - { 69, 60, 71, 38, 73, 119, 28, 222, 37 }, - { 68, 45, 128, 34, 1, 47, 11, 245, 171 }, - { 62, 17, 19, 70, 146, 85, 55, 62, 70 }, - { 37, 43, 37, 154, 100, 163, 85, 160, 1 }, - { 63, 9, 92, 136, 28, 64, 32, 201, 85 }, - { 75, 15, 9, 9, 64, 255, 184, 119, 16 }, - { 86, 6, 28, 5, 64, 255, 25, 248, 1 }, - { 56, 8, 17, 132, 137, 255, 55, 116, 128 }, - { 58, 15, 20, 82, 135, 57, 26, 121, 40 } }, - { { 164, 50, 31, 137, 154, 133, 25, 35, 218 }, - { 51, 103, 44, 131, 131, 123, 31, 6, 158 }, - { 86, 40, 64, 135, 148, 224, 45, 183, 128 }, - { 22, 26, 17, 131, 240, 154, 14, 1, 209 }, - { 45, 16, 21, 91, 64, 222, 7, 1, 197 }, - { 56, 21, 39, 155, 60, 138, 23, 102, 213 }, - { 83, 12, 13, 54, 192, 255, 68, 47, 28 }, - { 85, 26, 85, 85, 128, 128, 32, 146, 171 }, - { 18, 11, 7, 63, 144, 171, 4, 4, 246 }, - { 35, 27, 10, 146, 174, 171, 12, 26, 128 } }, - { { 190, 80, 35, 99, 180, 80, 126, 54, 45 }, - { 85, 126, 47, 87, 176, 51, 41, 20, 32 }, - { 101, 75, 128, 139, 118, 146, 116, 128, 85 }, - { 56, 41, 15, 176, 236, 85, 37, 9, 62 }, - { 71, 30, 17, 119, 118, 255, 17, 18, 138 }, - { 101, 38, 60, 138, 55, 70, 43, 26, 142 }, - { 146, 36, 19, 30, 171, 255, 97, 27, 20 }, - { 138, 45, 61, 62, 219, 1, 81, 188, 64 }, - { 32, 41, 20, 117, 151, 142, 20, 21, 163 }, - { 112, 19, 12, 61, 195, 128, 48, 4, 24 } } -}; - -void VP8ResetProba(VP8Proba* const proba) { - memset(proba->segments_, 255u, sizeof(proba->segments_)); - // proba->bands_[][] is initialized later -} - -static void ParseIntraMode(VP8BitReader* const br, - VP8Decoder* const dec, int mb_x) { - uint8_t* const top = dec->intra_t_ + 4 * mb_x; - uint8_t* const left = dec->intra_l_; - VP8MBData* const block = dec->mb_data_ + mb_x; - - // Note: we don't save segment map (yet), as we don't expect - // to decode more than 1 keyframe. - if (dec->segment_hdr_.update_map_) { - // Hardcoded tree parsing - block->segment_ = !VP8GetBit(br, dec->proba_.segments_[0]) - ? VP8GetBit(br, dec->proba_.segments_[1]) - : 2 + VP8GetBit(br, dec->proba_.segments_[2]); - } else { - block->segment_ = 0; // default for intra - } - if (dec->use_skip_proba_) block->skip_ = VP8GetBit(br, dec->skip_p_); - - block->is_i4x4_ = !VP8GetBit(br, 145); // decide for B_PRED first - if (!block->is_i4x4_) { - // Hardcoded 16x16 intra-mode decision tree. - const int ymode = - VP8GetBit(br, 156) ? (VP8GetBit(br, 128) ? TM_PRED : H_PRED) - : (VP8GetBit(br, 163) ? V_PRED : DC_PRED); - block->imodes_[0] = ymode; - memset(top, ymode, 4 * sizeof(*top)); - memset(left, ymode, 4 * sizeof(*left)); - } else { - uint8_t* modes = block->imodes_; - int y; - for (y = 0; y < 4; ++y) { - int ymode = left[y]; - int x; - for (x = 0; x < 4; ++x) { - const uint8_t* const prob = kBModesProba[top[x]][ymode]; -#ifdef USE_GENERIC_TREE - // Generic tree-parsing - int i = kYModesIntra4[VP8GetBit(br, prob[0])]; - while (i > 0) { - i = kYModesIntra4[2 * i + VP8GetBit(br, prob[i])]; - } - ymode = -i; -#else - // Hardcoded tree parsing - ymode = !VP8GetBit(br, prob[0]) ? B_DC_PRED : - !VP8GetBit(br, prob[1]) ? B_TM_PRED : - !VP8GetBit(br, prob[2]) ? B_VE_PRED : - !VP8GetBit(br, prob[3]) ? - (!VP8GetBit(br, prob[4]) ? B_HE_PRED : - (!VP8GetBit(br, prob[5]) ? B_RD_PRED : B_VR_PRED)) : - (!VP8GetBit(br, prob[6]) ? B_LD_PRED : - (!VP8GetBit(br, prob[7]) ? B_VL_PRED : - (!VP8GetBit(br, prob[8]) ? B_HD_PRED : B_HU_PRED))); -#endif // USE_GENERIC_TREE - top[x] = ymode; - } - memcpy(modes, top, 4 * sizeof(*top)); - modes += 4; - left[y] = ymode; - } - } - // Hardcoded UVMode decision tree - block->uvmode_ = !VP8GetBit(br, 142) ? DC_PRED - : !VP8GetBit(br, 114) ? V_PRED - : VP8GetBit(br, 183) ? TM_PRED : H_PRED; -} - -int VP8ParseIntraModeRow(VP8BitReader* const br, VP8Decoder* const dec) { - int mb_x; - for (mb_x = 0; mb_x < dec->mb_w_; ++mb_x) { - ParseIntraMode(br, dec, mb_x); - } - return !dec->br_.eof_; -} - -//------------------------------------------------------------------------------ -// Paragraph 13 - -static const uint8_t - CoeffsUpdateProba[NUM_TYPES][NUM_BANDS][NUM_CTX][NUM_PROBAS] = { - { { { 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 }, - { 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 }, - { 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 } - }, - { { 176, 246, 255, 255, 255, 255, 255, 255, 255, 255, 255 }, - { 223, 241, 252, 255, 255, 255, 255, 255, 255, 255, 255 }, - { 249, 253, 253, 255, 255, 255, 255, 255, 255, 255, 255 } - }, - { { 255, 244, 252, 255, 255, 255, 255, 255, 255, 255, 255 }, - { 234, 254, 254, 255, 255, 255, 255, 255, 255, 255, 255 }, - { 253, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 } - }, - { { 255, 246, 254, 255, 255, 255, 255, 255, 255, 255, 255 }, - { 239, 253, 254, 255, 255, 255, 255, 255, 255, 255, 255 }, - { 254, 255, 254, 255, 255, 255, 255, 255, 255, 255, 255 } - }, - { { 255, 248, 254, 255, 255, 255, 255, 255, 255, 255, 255 }, - { 251, 255, 254, 255, 255, 255, 255, 255, 255, 255, 255 }, - { 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 } - }, - { { 255, 253, 254, 255, 255, 255, 255, 255, 255, 255, 255 }, - { 251, 254, 254, 255, 255, 255, 255, 255, 255, 255, 255 }, - { 254, 255, 254, 255, 255, 255, 255, 255, 255, 255, 255 } - }, - { { 255, 254, 253, 255, 254, 255, 255, 255, 255, 255, 255 }, - { 250, 255, 254, 255, 254, 255, 255, 255, 255, 255, 255 }, - { 254, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 } - }, - { { 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 }, - { 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 }, - { 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 } - } - }, - { { { 217, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 }, - { 225, 252, 241, 253, 255, 255, 254, 255, 255, 255, 255 }, - { 234, 250, 241, 250, 253, 255, 253, 254, 255, 255, 255 } - }, - { { 255, 254, 255, 255, 255, 255, 255, 255, 255, 255, 255 }, - { 223, 254, 254, 255, 255, 255, 255, 255, 255, 255, 255 }, - { 238, 253, 254, 254, 255, 255, 255, 255, 255, 255, 255 } - }, - { { 255, 248, 254, 255, 255, 255, 255, 255, 255, 255, 255 }, - { 249, 254, 255, 255, 255, 255, 255, 255, 255, 255, 255 }, - { 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 } - }, - { { 255, 253, 255, 255, 255, 255, 255, 255, 255, 255, 255 }, - { 247, 254, 255, 255, 255, 255, 255, 255, 255, 255, 255 }, - { 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 } - }, - { { 255, 253, 254, 255, 255, 255, 255, 255, 255, 255, 255 }, - { 252, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 }, - { 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 } - }, - { { 255, 254, 254, 255, 255, 255, 255, 255, 255, 255, 255 }, - { 253, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 }, - { 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 } - }, - { { 255, 254, 253, 255, 255, 255, 255, 255, 255, 255, 255 }, - { 250, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 }, - { 254, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 } - }, - { { 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 }, - { 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 }, - { 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 } - } - }, - { { { 186, 251, 250, 255, 255, 255, 255, 255, 255, 255, 255 }, - { 234, 251, 244, 254, 255, 255, 255, 255, 255, 255, 255 }, - { 251, 251, 243, 253, 254, 255, 254, 255, 255, 255, 255 } - }, - { { 255, 253, 254, 255, 255, 255, 255, 255, 255, 255, 255 }, - { 236, 253, 254, 255, 255, 255, 255, 255, 255, 255, 255 }, - { 251, 253, 253, 254, 254, 255, 255, 255, 255, 255, 255 } - }, - { { 255, 254, 254, 255, 255, 255, 255, 255, 255, 255, 255 }, - { 254, 254, 254, 255, 255, 255, 255, 255, 255, 255, 255 }, - { 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 } - }, - { { 255, 254, 255, 255, 255, 255, 255, 255, 255, 255, 255 }, - { 254, 254, 255, 255, 255, 255, 255, 255, 255, 255, 255 }, - { 254, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 } - }, - { { 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 }, - { 254, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 }, - { 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 } - }, - { { 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 }, - { 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 }, - { 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 } - }, - { { 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 }, - { 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 }, - { 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 } - }, - { { 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 }, - { 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 }, - { 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 } - } - }, - { { { 248, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 }, - { 250, 254, 252, 254, 255, 255, 255, 255, 255, 255, 255 }, - { 248, 254, 249, 253, 255, 255, 255, 255, 255, 255, 255 } - }, - { { 255, 253, 253, 255, 255, 255, 255, 255, 255, 255, 255 }, - { 246, 253, 253, 255, 255, 255, 255, 255, 255, 255, 255 }, - { 252, 254, 251, 254, 254, 255, 255, 255, 255, 255, 255 } - }, - { { 255, 254, 252, 255, 255, 255, 255, 255, 255, 255, 255 }, - { 248, 254, 253, 255, 255, 255, 255, 255, 255, 255, 255 }, - { 253, 255, 254, 254, 255, 255, 255, 255, 255, 255, 255 } - }, - { { 255, 251, 254, 255, 255, 255, 255, 255, 255, 255, 255 }, - { 245, 251, 254, 255, 255, 255, 255, 255, 255, 255, 255 }, - { 253, 253, 254, 255, 255, 255, 255, 255, 255, 255, 255 } - }, - { { 255, 251, 253, 255, 255, 255, 255, 255, 255, 255, 255 }, - { 252, 253, 254, 255, 255, 255, 255, 255, 255, 255, 255 }, - { 255, 254, 255, 255, 255, 255, 255, 255, 255, 255, 255 } - }, - { { 255, 252, 255, 255, 255, 255, 255, 255, 255, 255, 255 }, - { 249, 255, 254, 255, 255, 255, 255, 255, 255, 255, 255 }, - { 255, 255, 254, 255, 255, 255, 255, 255, 255, 255, 255 } - }, - { { 255, 255, 253, 255, 255, 255, 255, 255, 255, 255, 255 }, - { 250, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 }, - { 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 } - }, - { { 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 }, - { 254, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 }, - { 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 } - } - } -}; - -// Paragraph 9.9 - -static const int kBands[16 + 1] = { - 0, 1, 2, 3, 6, 4, 5, 6, 6, 6, 6, 6, 6, 6, 6, 7, - 0 // extra entry as sentinel -}; - -void VP8ParseProba(VP8BitReader* const br, VP8Decoder* const dec) { - VP8Proba* const proba = &dec->proba_; - int t, b, c, p; - for (t = 0; t < NUM_TYPES; ++t) { - for (b = 0; b < NUM_BANDS; ++b) { - for (c = 0; c < NUM_CTX; ++c) { - for (p = 0; p < NUM_PROBAS; ++p) { - const int v = VP8GetBit(br, CoeffsUpdateProba[t][b][c][p]) ? - VP8GetValue(br, 8) : CoeffsProba0[t][b][c][p]; - proba->bands_[t][b].probas_[c][p] = v; - } - } - } - for (b = 0; b < 16 + 1; ++b) { - proba->bands_ptr_[t][b] = &proba->bands_[t][kBands[b]]; - } - } - dec->use_skip_proba_ = VP8Get(br); - if (dec->use_skip_proba_) { - dec->skip_p_ = VP8GetValue(br, 8); - } -} - diff --git a/thirdparty/libwebp/dec/tree_dec.c b/thirdparty/libwebp/dec/tree_dec.c new file mode 100644 index 0000000000..9e805f60f3 --- /dev/null +++ b/thirdparty/libwebp/dec/tree_dec.c @@ -0,0 +1,528 @@ +// Copyright 2010 Google Inc. All Rights Reserved. +// +// Use of this source code is governed by a BSD-style license +// that can be found in the COPYING file in the root of the source +// tree. An additional intellectual property rights grant can be found +// in the file PATENTS. All contributing project authors may +// be found in the AUTHORS file in the root of the source tree. +// ----------------------------------------------------------------------------- +// +// Coding trees and probas +// +// Author: Skal (pascal.massimino@gmail.com) + +#include "./vp8i_dec.h" +#include "../utils/bit_reader_inl_utils.h" + +#if !defined(__arm__) && !defined(_M_ARM) && !defined(__aarch64__) +// using a table is ~1-2% slower on ARM. Prefer the coded-tree approach then. +#define USE_GENERIC_TREE +#endif + +#ifdef USE_GENERIC_TREE +static const int8_t kYModesIntra4[18] = { + -B_DC_PRED, 1, + -B_TM_PRED, 2, + -B_VE_PRED, 3, + 4, 6, + -B_HE_PRED, 5, + -B_RD_PRED, -B_VR_PRED, + -B_LD_PRED, 7, + -B_VL_PRED, 8, + -B_HD_PRED, -B_HU_PRED +}; +#endif + +//------------------------------------------------------------------------------ +// Default probabilities + +// Paragraph 13.5 +static const uint8_t + CoeffsProba0[NUM_TYPES][NUM_BANDS][NUM_CTX][NUM_PROBAS] = { + { { { 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128 }, + { 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128 }, + { 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128 } + }, + { { 253, 136, 254, 255, 228, 219, 128, 128, 128, 128, 128 }, + { 189, 129, 242, 255, 227, 213, 255, 219, 128, 128, 128 }, + { 106, 126, 227, 252, 214, 209, 255, 255, 128, 128, 128 } + }, + { { 1, 98, 248, 255, 236, 226, 255, 255, 128, 128, 128 }, + { 181, 133, 238, 254, 221, 234, 255, 154, 128, 128, 128 }, + { 78, 134, 202, 247, 198, 180, 255, 219, 128, 128, 128 }, + }, + { { 1, 185, 249, 255, 243, 255, 128, 128, 128, 128, 128 }, + { 184, 150, 247, 255, 236, 224, 128, 128, 128, 128, 128 }, + { 77, 110, 216, 255, 236, 230, 128, 128, 128, 128, 128 }, + }, + { { 1, 101, 251, 255, 241, 255, 128, 128, 128, 128, 128 }, + { 170, 139, 241, 252, 236, 209, 255, 255, 128, 128, 128 }, + { 37, 116, 196, 243, 228, 255, 255, 255, 128, 128, 128 } + }, + { { 1, 204, 254, 255, 245, 255, 128, 128, 128, 128, 128 }, + { 207, 160, 250, 255, 238, 128, 128, 128, 128, 128, 128 }, + { 102, 103, 231, 255, 211, 171, 128, 128, 128, 128, 128 } + }, + { { 1, 152, 252, 255, 240, 255, 128, 128, 128, 128, 128 }, + { 177, 135, 243, 255, 234, 225, 128, 128, 128, 128, 128 }, + { 80, 129, 211, 255, 194, 224, 128, 128, 128, 128, 128 } + }, + { { 1, 1, 255, 128, 128, 128, 128, 128, 128, 128, 128 }, + { 246, 1, 255, 128, 128, 128, 128, 128, 128, 128, 128 }, + { 255, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128 } + } + }, + { { { 198, 35, 237, 223, 193, 187, 162, 160, 145, 155, 62 }, + { 131, 45, 198, 221, 172, 176, 220, 157, 252, 221, 1 }, + { 68, 47, 146, 208, 149, 167, 221, 162, 255, 223, 128 } + }, + { { 1, 149, 241, 255, 221, 224, 255, 255, 128, 128, 128 }, + { 184, 141, 234, 253, 222, 220, 255, 199, 128, 128, 128 }, + { 81, 99, 181, 242, 176, 190, 249, 202, 255, 255, 128 } + }, + { { 1, 129, 232, 253, 214, 197, 242, 196, 255, 255, 128 }, + { 99, 121, 210, 250, 201, 198, 255, 202, 128, 128, 128 }, + { 23, 91, 163, 242, 170, 187, 247, 210, 255, 255, 128 } + }, + { { 1, 200, 246, 255, 234, 255, 128, 128, 128, 128, 128 }, + { 109, 178, 241, 255, 231, 245, 255, 255, 128, 128, 128 }, + { 44, 130, 201, 253, 205, 192, 255, 255, 128, 128, 128 } + }, + { { 1, 132, 239, 251, 219, 209, 255, 165, 128, 128, 128 }, + { 94, 136, 225, 251, 218, 190, 255, 255, 128, 128, 128 }, + { 22, 100, 174, 245, 186, 161, 255, 199, 128, 128, 128 } + }, + { { 1, 182, 249, 255, 232, 235, 128, 128, 128, 128, 128 }, + { 124, 143, 241, 255, 227, 234, 128, 128, 128, 128, 128 }, + { 35, 77, 181, 251, 193, 211, 255, 205, 128, 128, 128 } + }, + { { 1, 157, 247, 255, 236, 231, 255, 255, 128, 128, 128 }, + { 121, 141, 235, 255, 225, 227, 255, 255, 128, 128, 128 }, + { 45, 99, 188, 251, 195, 217, 255, 224, 128, 128, 128 } + }, + { { 1, 1, 251, 255, 213, 255, 128, 128, 128, 128, 128 }, + { 203, 1, 248, 255, 255, 128, 128, 128, 128, 128, 128 }, + { 137, 1, 177, 255, 224, 255, 128, 128, 128, 128, 128 } + } + }, + { { { 253, 9, 248, 251, 207, 208, 255, 192, 128, 128, 128 }, + { 175, 13, 224, 243, 193, 185, 249, 198, 255, 255, 128 }, + { 73, 17, 171, 221, 161, 179, 236, 167, 255, 234, 128 } + }, + { { 1, 95, 247, 253, 212, 183, 255, 255, 128, 128, 128 }, + { 239, 90, 244, 250, 211, 209, 255, 255, 128, 128, 128 }, + { 155, 77, 195, 248, 188, 195, 255, 255, 128, 128, 128 } + }, + { { 1, 24, 239, 251, 218, 219, 255, 205, 128, 128, 128 }, + { 201, 51, 219, 255, 196, 186, 128, 128, 128, 128, 128 }, + { 69, 46, 190, 239, 201, 218, 255, 228, 128, 128, 128 } + }, + { { 1, 191, 251, 255, 255, 128, 128, 128, 128, 128, 128 }, + { 223, 165, 249, 255, 213, 255, 128, 128, 128, 128, 128 }, + { 141, 124, 248, 255, 255, 128, 128, 128, 128, 128, 128 } + }, + { { 1, 16, 248, 255, 255, 128, 128, 128, 128, 128, 128 }, + { 190, 36, 230, 255, 236, 255, 128, 128, 128, 128, 128 }, + { 149, 1, 255, 128, 128, 128, 128, 128, 128, 128, 128 } + }, + { { 1, 226, 255, 128, 128, 128, 128, 128, 128, 128, 128 }, + { 247, 192, 255, 128, 128, 128, 128, 128, 128, 128, 128 }, + { 240, 128, 255, 128, 128, 128, 128, 128, 128, 128, 128 } + }, + { { 1, 134, 252, 255, 255, 128, 128, 128, 128, 128, 128 }, + { 213, 62, 250, 255, 255, 128, 128, 128, 128, 128, 128 }, + { 55, 93, 255, 128, 128, 128, 128, 128, 128, 128, 128 } + }, + { { 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128 }, + { 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128 }, + { 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128 } + } + }, + { { { 202, 24, 213, 235, 186, 191, 220, 160, 240, 175, 255 }, + { 126, 38, 182, 232, 169, 184, 228, 174, 255, 187, 128 }, + { 61, 46, 138, 219, 151, 178, 240, 170, 255, 216, 128 } + }, + { { 1, 112, 230, 250, 199, 191, 247, 159, 255, 255, 128 }, + { 166, 109, 228, 252, 211, 215, 255, 174, 128, 128, 128 }, + { 39, 77, 162, 232, 172, 180, 245, 178, 255, 255, 128 } + }, + { { 1, 52, 220, 246, 198, 199, 249, 220, 255, 255, 128 }, + { 124, 74, 191, 243, 183, 193, 250, 221, 255, 255, 128 }, + { 24, 71, 130, 219, 154, 170, 243, 182, 255, 255, 128 } + }, + { { 1, 182, 225, 249, 219, 240, 255, 224, 128, 128, 128 }, + { 149, 150, 226, 252, 216, 205, 255, 171, 128, 128, 128 }, + { 28, 108, 170, 242, 183, 194, 254, 223, 255, 255, 128 } + }, + { { 1, 81, 230, 252, 204, 203, 255, 192, 128, 128, 128 }, + { 123, 102, 209, 247, 188, 196, 255, 233, 128, 128, 128 }, + { 20, 95, 153, 243, 164, 173, 255, 203, 128, 128, 128 } + }, + { { 1, 222, 248, 255, 216, 213, 128, 128, 128, 128, 128 }, + { 168, 175, 246, 252, 235, 205, 255, 255, 128, 128, 128 }, + { 47, 116, 215, 255, 211, 212, 255, 255, 128, 128, 128 } + }, + { { 1, 121, 236, 253, 212, 214, 255, 255, 128, 128, 128 }, + { 141, 84, 213, 252, 201, 202, 255, 219, 128, 128, 128 }, + { 42, 80, 160, 240, 162, 185, 255, 205, 128, 128, 128 } + }, + { { 1, 1, 255, 128, 128, 128, 128, 128, 128, 128, 128 }, + { 244, 1, 255, 128, 128, 128, 128, 128, 128, 128, 128 }, + { 238, 1, 255, 128, 128, 128, 128, 128, 128, 128, 128 } + } + } +}; + +// Paragraph 11.5 +static const uint8_t kBModesProba[NUM_BMODES][NUM_BMODES][NUM_BMODES - 1] = { + { { 231, 120, 48, 89, 115, 113, 120, 152, 112 }, + { 152, 179, 64, 126, 170, 118, 46, 70, 95 }, + { 175, 69, 143, 80, 85, 82, 72, 155, 103 }, + { 56, 58, 10, 171, 218, 189, 17, 13, 152 }, + { 114, 26, 17, 163, 44, 195, 21, 10, 173 }, + { 121, 24, 80, 195, 26, 62, 44, 64, 85 }, + { 144, 71, 10, 38, 171, 213, 144, 34, 26 }, + { 170, 46, 55, 19, 136, 160, 33, 206, 71 }, + { 63, 20, 8, 114, 114, 208, 12, 9, 226 }, + { 81, 40, 11, 96, 182, 84, 29, 16, 36 } }, + { { 134, 183, 89, 137, 98, 101, 106, 165, 148 }, + { 72, 187, 100, 130, 157, 111, 32, 75, 80 }, + { 66, 102, 167, 99, 74, 62, 40, 234, 128 }, + { 41, 53, 9, 178, 241, 141, 26, 8, 107 }, + { 74, 43, 26, 146, 73, 166, 49, 23, 157 }, + { 65, 38, 105, 160, 51, 52, 31, 115, 128 }, + { 104, 79, 12, 27, 217, 255, 87, 17, 7 }, + { 87, 68, 71, 44, 114, 51, 15, 186, 23 }, + { 47, 41, 14, 110, 182, 183, 21, 17, 194 }, + { 66, 45, 25, 102, 197, 189, 23, 18, 22 } }, + { { 88, 88, 147, 150, 42, 46, 45, 196, 205 }, + { 43, 97, 183, 117, 85, 38, 35, 179, 61 }, + { 39, 53, 200, 87, 26, 21, 43, 232, 171 }, + { 56, 34, 51, 104, 114, 102, 29, 93, 77 }, + { 39, 28, 85, 171, 58, 165, 90, 98, 64 }, + { 34, 22, 116, 206, 23, 34, 43, 166, 73 }, + { 107, 54, 32, 26, 51, 1, 81, 43, 31 }, + { 68, 25, 106, 22, 64, 171, 36, 225, 114 }, + { 34, 19, 21, 102, 132, 188, 16, 76, 124 }, + { 62, 18, 78, 95, 85, 57, 50, 48, 51 } }, + { { 193, 101, 35, 159, 215, 111, 89, 46, 111 }, + { 60, 148, 31, 172, 219, 228, 21, 18, 111 }, + { 112, 113, 77, 85, 179, 255, 38, 120, 114 }, + { 40, 42, 1, 196, 245, 209, 10, 25, 109 }, + { 88, 43, 29, 140, 166, 213, 37, 43, 154 }, + { 61, 63, 30, 155, 67, 45, 68, 1, 209 }, + { 100, 80, 8, 43, 154, 1, 51, 26, 71 }, + { 142, 78, 78, 16, 255, 128, 34, 197, 171 }, + { 41, 40, 5, 102, 211, 183, 4, 1, 221 }, + { 51, 50, 17, 168, 209, 192, 23, 25, 82 } }, + { { 138, 31, 36, 171, 27, 166, 38, 44, 229 }, + { 67, 87, 58, 169, 82, 115, 26, 59, 179 }, + { 63, 59, 90, 180, 59, 166, 93, 73, 154 }, + { 40, 40, 21, 116, 143, 209, 34, 39, 175 }, + { 47, 15, 16, 183, 34, 223, 49, 45, 183 }, + { 46, 17, 33, 183, 6, 98, 15, 32, 183 }, + { 57, 46, 22, 24, 128, 1, 54, 17, 37 }, + { 65, 32, 73, 115, 28, 128, 23, 128, 205 }, + { 40, 3, 9, 115, 51, 192, 18, 6, 223 }, + { 87, 37, 9, 115, 59, 77, 64, 21, 47 } }, + { { 104, 55, 44, 218, 9, 54, 53, 130, 226 }, + { 64, 90, 70, 205, 40, 41, 23, 26, 57 }, + { 54, 57, 112, 184, 5, 41, 38, 166, 213 }, + { 30, 34, 26, 133, 152, 116, 10, 32, 134 }, + { 39, 19, 53, 221, 26, 114, 32, 73, 255 }, + { 31, 9, 65, 234, 2, 15, 1, 118, 73 }, + { 75, 32, 12, 51, 192, 255, 160, 43, 51 }, + { 88, 31, 35, 67, 102, 85, 55, 186, 85 }, + { 56, 21, 23, 111, 59, 205, 45, 37, 192 }, + { 55, 38, 70, 124, 73, 102, 1, 34, 98 } }, + { { 125, 98, 42, 88, 104, 85, 117, 175, 82 }, + { 95, 84, 53, 89, 128, 100, 113, 101, 45 }, + { 75, 79, 123, 47, 51, 128, 81, 171, 1 }, + { 57, 17, 5, 71, 102, 57, 53, 41, 49 }, + { 38, 33, 13, 121, 57, 73, 26, 1, 85 }, + { 41, 10, 67, 138, 77, 110, 90, 47, 114 }, + { 115, 21, 2, 10, 102, 255, 166, 23, 6 }, + { 101, 29, 16, 10, 85, 128, 101, 196, 26 }, + { 57, 18, 10, 102, 102, 213, 34, 20, 43 }, + { 117, 20, 15, 36, 163, 128, 68, 1, 26 } }, + { { 102, 61, 71, 37, 34, 53, 31, 243, 192 }, + { 69, 60, 71, 38, 73, 119, 28, 222, 37 }, + { 68, 45, 128, 34, 1, 47, 11, 245, 171 }, + { 62, 17, 19, 70, 146, 85, 55, 62, 70 }, + { 37, 43, 37, 154, 100, 163, 85, 160, 1 }, + { 63, 9, 92, 136, 28, 64, 32, 201, 85 }, + { 75, 15, 9, 9, 64, 255, 184, 119, 16 }, + { 86, 6, 28, 5, 64, 255, 25, 248, 1 }, + { 56, 8, 17, 132, 137, 255, 55, 116, 128 }, + { 58, 15, 20, 82, 135, 57, 26, 121, 40 } }, + { { 164, 50, 31, 137, 154, 133, 25, 35, 218 }, + { 51, 103, 44, 131, 131, 123, 31, 6, 158 }, + { 86, 40, 64, 135, 148, 224, 45, 183, 128 }, + { 22, 26, 17, 131, 240, 154, 14, 1, 209 }, + { 45, 16, 21, 91, 64, 222, 7, 1, 197 }, + { 56, 21, 39, 155, 60, 138, 23, 102, 213 }, + { 83, 12, 13, 54, 192, 255, 68, 47, 28 }, + { 85, 26, 85, 85, 128, 128, 32, 146, 171 }, + { 18, 11, 7, 63, 144, 171, 4, 4, 246 }, + { 35, 27, 10, 146, 174, 171, 12, 26, 128 } }, + { { 190, 80, 35, 99, 180, 80, 126, 54, 45 }, + { 85, 126, 47, 87, 176, 51, 41, 20, 32 }, + { 101, 75, 128, 139, 118, 146, 116, 128, 85 }, + { 56, 41, 15, 176, 236, 85, 37, 9, 62 }, + { 71, 30, 17, 119, 118, 255, 17, 18, 138 }, + { 101, 38, 60, 138, 55, 70, 43, 26, 142 }, + { 146, 36, 19, 30, 171, 255, 97, 27, 20 }, + { 138, 45, 61, 62, 219, 1, 81, 188, 64 }, + { 32, 41, 20, 117, 151, 142, 20, 21, 163 }, + { 112, 19, 12, 61, 195, 128, 48, 4, 24 } } +}; + +void VP8ResetProba(VP8Proba* const proba) { + memset(proba->segments_, 255u, sizeof(proba->segments_)); + // proba->bands_[][] is initialized later +} + +static void ParseIntraMode(VP8BitReader* const br, + VP8Decoder* const dec, int mb_x) { + uint8_t* const top = dec->intra_t_ + 4 * mb_x; + uint8_t* const left = dec->intra_l_; + VP8MBData* const block = dec->mb_data_ + mb_x; + + // Note: we don't save segment map (yet), as we don't expect + // to decode more than 1 keyframe. + if (dec->segment_hdr_.update_map_) { + // Hardcoded tree parsing + block->segment_ = !VP8GetBit(br, dec->proba_.segments_[0]) + ? VP8GetBit(br, dec->proba_.segments_[1]) + : 2 + VP8GetBit(br, dec->proba_.segments_[2]); + } else { + block->segment_ = 0; // default for intra + } + if (dec->use_skip_proba_) block->skip_ = VP8GetBit(br, dec->skip_p_); + + block->is_i4x4_ = !VP8GetBit(br, 145); // decide for B_PRED first + if (!block->is_i4x4_) { + // Hardcoded 16x16 intra-mode decision tree. + const int ymode = + VP8GetBit(br, 156) ? (VP8GetBit(br, 128) ? TM_PRED : H_PRED) + : (VP8GetBit(br, 163) ? V_PRED : DC_PRED); + block->imodes_[0] = ymode; + memset(top, ymode, 4 * sizeof(*top)); + memset(left, ymode, 4 * sizeof(*left)); + } else { + uint8_t* modes = block->imodes_; + int y; + for (y = 0; y < 4; ++y) { + int ymode = left[y]; + int x; + for (x = 0; x < 4; ++x) { + const uint8_t* const prob = kBModesProba[top[x]][ymode]; +#ifdef USE_GENERIC_TREE + // Generic tree-parsing + int i = kYModesIntra4[VP8GetBit(br, prob[0])]; + while (i > 0) { + i = kYModesIntra4[2 * i + VP8GetBit(br, prob[i])]; + } + ymode = -i; +#else + // Hardcoded tree parsing + ymode = !VP8GetBit(br, prob[0]) ? B_DC_PRED : + !VP8GetBit(br, prob[1]) ? B_TM_PRED : + !VP8GetBit(br, prob[2]) ? B_VE_PRED : + !VP8GetBit(br, prob[3]) ? + (!VP8GetBit(br, prob[4]) ? B_HE_PRED : + (!VP8GetBit(br, prob[5]) ? B_RD_PRED : B_VR_PRED)) : + (!VP8GetBit(br, prob[6]) ? B_LD_PRED : + (!VP8GetBit(br, prob[7]) ? B_VL_PRED : + (!VP8GetBit(br, prob[8]) ? B_HD_PRED : B_HU_PRED))); +#endif // USE_GENERIC_TREE + top[x] = ymode; + } + memcpy(modes, top, 4 * sizeof(*top)); + modes += 4; + left[y] = ymode; + } + } + // Hardcoded UVMode decision tree + block->uvmode_ = !VP8GetBit(br, 142) ? DC_PRED + : !VP8GetBit(br, 114) ? V_PRED + : VP8GetBit(br, 183) ? TM_PRED : H_PRED; +} + +int VP8ParseIntraModeRow(VP8BitReader* const br, VP8Decoder* const dec) { + int mb_x; + for (mb_x = 0; mb_x < dec->mb_w_; ++mb_x) { + ParseIntraMode(br, dec, mb_x); + } + return !dec->br_.eof_; +} + +//------------------------------------------------------------------------------ +// Paragraph 13 + +static const uint8_t + CoeffsUpdateProba[NUM_TYPES][NUM_BANDS][NUM_CTX][NUM_PROBAS] = { + { { { 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 }, + { 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 }, + { 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 } + }, + { { 176, 246, 255, 255, 255, 255, 255, 255, 255, 255, 255 }, + { 223, 241, 252, 255, 255, 255, 255, 255, 255, 255, 255 }, + { 249, 253, 253, 255, 255, 255, 255, 255, 255, 255, 255 } + }, + { { 255, 244, 252, 255, 255, 255, 255, 255, 255, 255, 255 }, + { 234, 254, 254, 255, 255, 255, 255, 255, 255, 255, 255 }, + { 253, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 } + }, + { { 255, 246, 254, 255, 255, 255, 255, 255, 255, 255, 255 }, + { 239, 253, 254, 255, 255, 255, 255, 255, 255, 255, 255 }, + { 254, 255, 254, 255, 255, 255, 255, 255, 255, 255, 255 } + }, + { { 255, 248, 254, 255, 255, 255, 255, 255, 255, 255, 255 }, + { 251, 255, 254, 255, 255, 255, 255, 255, 255, 255, 255 }, + { 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 } + }, + { { 255, 253, 254, 255, 255, 255, 255, 255, 255, 255, 255 }, + { 251, 254, 254, 255, 255, 255, 255, 255, 255, 255, 255 }, + { 254, 255, 254, 255, 255, 255, 255, 255, 255, 255, 255 } + }, + { { 255, 254, 253, 255, 254, 255, 255, 255, 255, 255, 255 }, + { 250, 255, 254, 255, 254, 255, 255, 255, 255, 255, 255 }, + { 254, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 } + }, + { { 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 }, + { 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 }, + { 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 } + } + }, + { { { 217, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 }, + { 225, 252, 241, 253, 255, 255, 254, 255, 255, 255, 255 }, + { 234, 250, 241, 250, 253, 255, 253, 254, 255, 255, 255 } + }, + { { 255, 254, 255, 255, 255, 255, 255, 255, 255, 255, 255 }, + { 223, 254, 254, 255, 255, 255, 255, 255, 255, 255, 255 }, + { 238, 253, 254, 254, 255, 255, 255, 255, 255, 255, 255 } + }, + { { 255, 248, 254, 255, 255, 255, 255, 255, 255, 255, 255 }, + { 249, 254, 255, 255, 255, 255, 255, 255, 255, 255, 255 }, + { 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 } + }, + { { 255, 253, 255, 255, 255, 255, 255, 255, 255, 255, 255 }, + { 247, 254, 255, 255, 255, 255, 255, 255, 255, 255, 255 }, + { 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 } + }, + { { 255, 253, 254, 255, 255, 255, 255, 255, 255, 255, 255 }, + { 252, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 }, + { 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 } + }, + { { 255, 254, 254, 255, 255, 255, 255, 255, 255, 255, 255 }, + { 253, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 }, + { 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 } + }, + { { 255, 254, 253, 255, 255, 255, 255, 255, 255, 255, 255 }, + { 250, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 }, + { 254, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 } + }, + { { 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 }, + { 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 }, + { 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 } + } + }, + { { { 186, 251, 250, 255, 255, 255, 255, 255, 255, 255, 255 }, + { 234, 251, 244, 254, 255, 255, 255, 255, 255, 255, 255 }, + { 251, 251, 243, 253, 254, 255, 254, 255, 255, 255, 255 } + }, + { { 255, 253, 254, 255, 255, 255, 255, 255, 255, 255, 255 }, + { 236, 253, 254, 255, 255, 255, 255, 255, 255, 255, 255 }, + { 251, 253, 253, 254, 254, 255, 255, 255, 255, 255, 255 } + }, + { { 255, 254, 254, 255, 255, 255, 255, 255, 255, 255, 255 }, + { 254, 254, 254, 255, 255, 255, 255, 255, 255, 255, 255 }, + { 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 } + }, + { { 255, 254, 255, 255, 255, 255, 255, 255, 255, 255, 255 }, + { 254, 254, 255, 255, 255, 255, 255, 255, 255, 255, 255 }, + { 254, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 } + }, + { { 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 }, + { 254, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 }, + { 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 } + }, + { { 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 }, + { 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 }, + { 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 } + }, + { { 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 }, + { 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 }, + { 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 } + }, + { { 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 }, + { 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 }, + { 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 } + } + }, + { { { 248, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 }, + { 250, 254, 252, 254, 255, 255, 255, 255, 255, 255, 255 }, + { 248, 254, 249, 253, 255, 255, 255, 255, 255, 255, 255 } + }, + { { 255, 253, 253, 255, 255, 255, 255, 255, 255, 255, 255 }, + { 246, 253, 253, 255, 255, 255, 255, 255, 255, 255, 255 }, + { 252, 254, 251, 254, 254, 255, 255, 255, 255, 255, 255 } + }, + { { 255, 254, 252, 255, 255, 255, 255, 255, 255, 255, 255 }, + { 248, 254, 253, 255, 255, 255, 255, 255, 255, 255, 255 }, + { 253, 255, 254, 254, 255, 255, 255, 255, 255, 255, 255 } + }, + { { 255, 251, 254, 255, 255, 255, 255, 255, 255, 255, 255 }, + { 245, 251, 254, 255, 255, 255, 255, 255, 255, 255, 255 }, + { 253, 253, 254, 255, 255, 255, 255, 255, 255, 255, 255 } + }, + { { 255, 251, 253, 255, 255, 255, 255, 255, 255, 255, 255 }, + { 252, 253, 254, 255, 255, 255, 255, 255, 255, 255, 255 }, + { 255, 254, 255, 255, 255, 255, 255, 255, 255, 255, 255 } + }, + { { 255, 252, 255, 255, 255, 255, 255, 255, 255, 255, 255 }, + { 249, 255, 254, 255, 255, 255, 255, 255, 255, 255, 255 }, + { 255, 255, 254, 255, 255, 255, 255, 255, 255, 255, 255 } + }, + { { 255, 255, 253, 255, 255, 255, 255, 255, 255, 255, 255 }, + { 250, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 }, + { 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 } + }, + { { 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 }, + { 254, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 }, + { 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 } + } + } +}; + +// Paragraph 9.9 + +static const int kBands[16 + 1] = { + 0, 1, 2, 3, 6, 4, 5, 6, 6, 6, 6, 6, 6, 6, 6, 7, + 0 // extra entry as sentinel +}; + +void VP8ParseProba(VP8BitReader* const br, VP8Decoder* const dec) { + VP8Proba* const proba = &dec->proba_; + int t, b, c, p; + for (t = 0; t < NUM_TYPES; ++t) { + for (b = 0; b < NUM_BANDS; ++b) { + for (c = 0; c < NUM_CTX; ++c) { + for (p = 0; p < NUM_PROBAS; ++p) { + const int v = VP8GetBit(br, CoeffsUpdateProba[t][b][c][p]) ? + VP8GetValue(br, 8) : CoeffsProba0[t][b][c][p]; + proba->bands_[t][b].probas_[c][p] = v; + } + } + } + for (b = 0; b < 16 + 1; ++b) { + proba->bands_ptr_[t][b] = &proba->bands_[t][kBands[b]]; + } + } + dec->use_skip_proba_ = VP8Get(br); + if (dec->use_skip_proba_) { + dec->skip_p_ = VP8GetValue(br, 8); + } +} + diff --git a/thirdparty/libwebp/dec/vp8.c b/thirdparty/libwebp/dec/vp8.c deleted file mode 100644 index 336680c38c..0000000000 --- a/thirdparty/libwebp/dec/vp8.c +++ /dev/null @@ -1,667 +0,0 @@ -// Copyright 2010 Google Inc. All Rights Reserved. -// -// Use of this source code is governed by a BSD-style license -// that can be found in the COPYING file in the root of the source -// tree. An additional intellectual property rights grant can be found -// in the file PATENTS. All contributing project authors may -// be found in the AUTHORS file in the root of the source tree. -// ----------------------------------------------------------------------------- -// -// main entry for the decoder -// -// Author: Skal (pascal.massimino@gmail.com) - -#include - -#include "./alphai.h" -#include "./vp8i.h" -#include "./vp8li.h" -#include "./webpi.h" -#include "../utils/bit_reader_inl.h" -#include "../utils/utils.h" - -//------------------------------------------------------------------------------ - -int WebPGetDecoderVersion(void) { - return (DEC_MAJ_VERSION << 16) | (DEC_MIN_VERSION << 8) | DEC_REV_VERSION; -} - -//------------------------------------------------------------------------------ -// VP8Decoder - -static void SetOk(VP8Decoder* const dec) { - dec->status_ = VP8_STATUS_OK; - dec->error_msg_ = "OK"; -} - -int VP8InitIoInternal(VP8Io* const io, int version) { - if (WEBP_ABI_IS_INCOMPATIBLE(version, WEBP_DECODER_ABI_VERSION)) { - return 0; // mismatch error - } - if (io != NULL) { - memset(io, 0, sizeof(*io)); - } - return 1; -} - -VP8Decoder* VP8New(void) { - VP8Decoder* const dec = (VP8Decoder*)WebPSafeCalloc(1ULL, sizeof(*dec)); - if (dec != NULL) { - SetOk(dec); - WebPGetWorkerInterface()->Init(&dec->worker_); - dec->ready_ = 0; - dec->num_parts_minus_one_ = 0; - } - return dec; -} - -VP8StatusCode VP8Status(VP8Decoder* const dec) { - if (!dec) return VP8_STATUS_INVALID_PARAM; - return dec->status_; -} - -const char* VP8StatusMessage(VP8Decoder* const dec) { - if (dec == NULL) return "no object"; - if (!dec->error_msg_) return "OK"; - return dec->error_msg_; -} - -void VP8Delete(VP8Decoder* const dec) { - if (dec != NULL) { - VP8Clear(dec); - WebPSafeFree(dec); - } -} - -int VP8SetError(VP8Decoder* const dec, - VP8StatusCode error, const char* const msg) { - // The oldest error reported takes precedence over the new one. - if (dec->status_ == VP8_STATUS_OK) { - dec->status_ = error; - dec->error_msg_ = msg; - dec->ready_ = 0; - } - return 0; -} - -//------------------------------------------------------------------------------ - -int VP8CheckSignature(const uint8_t* const data, size_t data_size) { - return (data_size >= 3 && - data[0] == 0x9d && data[1] == 0x01 && data[2] == 0x2a); -} - -int VP8GetInfo(const uint8_t* data, size_t data_size, size_t chunk_size, - int* const width, int* const height) { - if (data == NULL || data_size < VP8_FRAME_HEADER_SIZE) { - return 0; // not enough data - } - // check signature - if (!VP8CheckSignature(data + 3, data_size - 3)) { - return 0; // Wrong signature. - } else { - const uint32_t bits = data[0] | (data[1] << 8) | (data[2] << 16); - const int key_frame = !(bits & 1); - const int w = ((data[7] << 8) | data[6]) & 0x3fff; - const int h = ((data[9] << 8) | data[8]) & 0x3fff; - - if (!key_frame) { // Not a keyframe. - return 0; - } - - if (((bits >> 1) & 7) > 3) { - return 0; // unknown profile - } - if (!((bits >> 4) & 1)) { - return 0; // first frame is invisible! - } - if (((bits >> 5)) >= chunk_size) { // partition_length - return 0; // inconsistent size information. - } - if (w == 0 || h == 0) { - return 0; // We don't support both width and height to be zero. - } - - if (width) { - *width = w; - } - if (height) { - *height = h; - } - - return 1; - } -} - -//------------------------------------------------------------------------------ -// Header parsing - -static void ResetSegmentHeader(VP8SegmentHeader* const hdr) { - assert(hdr != NULL); - hdr->use_segment_ = 0; - hdr->update_map_ = 0; - hdr->absolute_delta_ = 1; - memset(hdr->quantizer_, 0, sizeof(hdr->quantizer_)); - memset(hdr->filter_strength_, 0, sizeof(hdr->filter_strength_)); -} - -// Paragraph 9.3 -static int ParseSegmentHeader(VP8BitReader* br, - VP8SegmentHeader* hdr, VP8Proba* proba) { - assert(br != NULL); - assert(hdr != NULL); - hdr->use_segment_ = VP8Get(br); - if (hdr->use_segment_) { - hdr->update_map_ = VP8Get(br); - if (VP8Get(br)) { // update data - int s; - hdr->absolute_delta_ = VP8Get(br); - for (s = 0; s < NUM_MB_SEGMENTS; ++s) { - hdr->quantizer_[s] = VP8Get(br) ? VP8GetSignedValue(br, 7) : 0; - } - for (s = 0; s < NUM_MB_SEGMENTS; ++s) { - hdr->filter_strength_[s] = VP8Get(br) ? VP8GetSignedValue(br, 6) : 0; - } - } - if (hdr->update_map_) { - int s; - for (s = 0; s < MB_FEATURE_TREE_PROBS; ++s) { - proba->segments_[s] = VP8Get(br) ? VP8GetValue(br, 8) : 255u; - } - } - } else { - hdr->update_map_ = 0; - } - return !br->eof_; -} - -// Paragraph 9.5 -// This function returns VP8_STATUS_SUSPENDED if we don't have all the -// necessary data in 'buf'. -// This case is not necessarily an error (for incremental decoding). -// Still, no bitreader is ever initialized to make it possible to read -// unavailable memory. -// If we don't even have the partitions' sizes, than VP8_STATUS_NOT_ENOUGH_DATA -// is returned, and this is an unrecoverable error. -// If the partitions were positioned ok, VP8_STATUS_OK is returned. -static VP8StatusCode ParsePartitions(VP8Decoder* const dec, - const uint8_t* buf, size_t size) { - VP8BitReader* const br = &dec->br_; - const uint8_t* sz = buf; - const uint8_t* buf_end = buf + size; - const uint8_t* part_start; - size_t size_left = size; - size_t last_part; - size_t p; - - dec->num_parts_minus_one_ = (1 << VP8GetValue(br, 2)) - 1; - last_part = dec->num_parts_minus_one_; - if (size < 3 * last_part) { - // we can't even read the sizes with sz[]! That's a failure. - return VP8_STATUS_NOT_ENOUGH_DATA; - } - part_start = buf + last_part * 3; - size_left -= last_part * 3; - for (p = 0; p < last_part; ++p) { - size_t psize = sz[0] | (sz[1] << 8) | (sz[2] << 16); - if (psize > size_left) psize = size_left; - VP8InitBitReader(dec->parts_ + p, part_start, psize); - part_start += psize; - size_left -= psize; - sz += 3; - } - VP8InitBitReader(dec->parts_ + last_part, part_start, size_left); - return (part_start < buf_end) ? VP8_STATUS_OK : - VP8_STATUS_SUSPENDED; // Init is ok, but there's not enough data -} - -// Paragraph 9.4 -static int ParseFilterHeader(VP8BitReader* br, VP8Decoder* const dec) { - VP8FilterHeader* const hdr = &dec->filter_hdr_; - hdr->simple_ = VP8Get(br); - hdr->level_ = VP8GetValue(br, 6); - hdr->sharpness_ = VP8GetValue(br, 3); - hdr->use_lf_delta_ = VP8Get(br); - if (hdr->use_lf_delta_) { - if (VP8Get(br)) { // update lf-delta? - int i; - for (i = 0; i < NUM_REF_LF_DELTAS; ++i) { - if (VP8Get(br)) { - hdr->ref_lf_delta_[i] = VP8GetSignedValue(br, 6); - } - } - for (i = 0; i < NUM_MODE_LF_DELTAS; ++i) { - if (VP8Get(br)) { - hdr->mode_lf_delta_[i] = VP8GetSignedValue(br, 6); - } - } - } - } - dec->filter_type_ = (hdr->level_ == 0) ? 0 : hdr->simple_ ? 1 : 2; - return !br->eof_; -} - -// Topmost call -int VP8GetHeaders(VP8Decoder* const dec, VP8Io* const io) { - const uint8_t* buf; - size_t buf_size; - VP8FrameHeader* frm_hdr; - VP8PictureHeader* pic_hdr; - VP8BitReader* br; - VP8StatusCode status; - - if (dec == NULL) { - return 0; - } - SetOk(dec); - if (io == NULL) { - return VP8SetError(dec, VP8_STATUS_INVALID_PARAM, - "null VP8Io passed to VP8GetHeaders()"); - } - buf = io->data; - buf_size = io->data_size; - if (buf_size < 4) { - return VP8SetError(dec, VP8_STATUS_NOT_ENOUGH_DATA, - "Truncated header."); - } - - // Paragraph 9.1 - { - const uint32_t bits = buf[0] | (buf[1] << 8) | (buf[2] << 16); - frm_hdr = &dec->frm_hdr_; - frm_hdr->key_frame_ = !(bits & 1); - frm_hdr->profile_ = (bits >> 1) & 7; - frm_hdr->show_ = (bits >> 4) & 1; - frm_hdr->partition_length_ = (bits >> 5); - if (frm_hdr->profile_ > 3) - return VP8SetError(dec, VP8_STATUS_BITSTREAM_ERROR, - "Incorrect keyframe parameters."); - if (!frm_hdr->show_) - return VP8SetError(dec, VP8_STATUS_UNSUPPORTED_FEATURE, - "Frame not displayable."); - buf += 3; - buf_size -= 3; - } - - pic_hdr = &dec->pic_hdr_; - if (frm_hdr->key_frame_) { - // Paragraph 9.2 - if (buf_size < 7) { - return VP8SetError(dec, VP8_STATUS_NOT_ENOUGH_DATA, - "cannot parse picture header"); - } - if (!VP8CheckSignature(buf, buf_size)) { - return VP8SetError(dec, VP8_STATUS_BITSTREAM_ERROR, - "Bad code word"); - } - pic_hdr->width_ = ((buf[4] << 8) | buf[3]) & 0x3fff; - pic_hdr->xscale_ = buf[4] >> 6; // ratio: 1, 5/4 5/3 or 2 - pic_hdr->height_ = ((buf[6] << 8) | buf[5]) & 0x3fff; - pic_hdr->yscale_ = buf[6] >> 6; - buf += 7; - buf_size -= 7; - - dec->mb_w_ = (pic_hdr->width_ + 15) >> 4; - dec->mb_h_ = (pic_hdr->height_ + 15) >> 4; - - // Setup default output area (can be later modified during io->setup()) - io->width = pic_hdr->width_; - io->height = pic_hdr->height_; - // IMPORTANT! use some sane dimensions in crop_* and scaled_* fields. - // So they can be used interchangeably without always testing for - // 'use_cropping'. - io->use_cropping = 0; - io->crop_top = 0; - io->crop_left = 0; - io->crop_right = io->width; - io->crop_bottom = io->height; - io->use_scaling = 0; - io->scaled_width = io->width; - io->scaled_height = io->height; - - io->mb_w = io->width; // sanity check - io->mb_h = io->height; // ditto - - VP8ResetProba(&dec->proba_); - ResetSegmentHeader(&dec->segment_hdr_); - } - - // Check if we have all the partition #0 available, and initialize dec->br_ - // to read this partition (and this partition only). - if (frm_hdr->partition_length_ > buf_size) { - return VP8SetError(dec, VP8_STATUS_NOT_ENOUGH_DATA, - "bad partition length"); - } - - br = &dec->br_; - VP8InitBitReader(br, buf, frm_hdr->partition_length_); - buf += frm_hdr->partition_length_; - buf_size -= frm_hdr->partition_length_; - - if (frm_hdr->key_frame_) { - pic_hdr->colorspace_ = VP8Get(br); - pic_hdr->clamp_type_ = VP8Get(br); - } - if (!ParseSegmentHeader(br, &dec->segment_hdr_, &dec->proba_)) { - return VP8SetError(dec, VP8_STATUS_BITSTREAM_ERROR, - "cannot parse segment header"); - } - // Filter specs - if (!ParseFilterHeader(br, dec)) { - return VP8SetError(dec, VP8_STATUS_BITSTREAM_ERROR, - "cannot parse filter header"); - } - status = ParsePartitions(dec, buf, buf_size); - if (status != VP8_STATUS_OK) { - return VP8SetError(dec, status, "cannot parse partitions"); - } - - // quantizer change - VP8ParseQuant(dec); - - // Frame buffer marking - if (!frm_hdr->key_frame_) { - return VP8SetError(dec, VP8_STATUS_UNSUPPORTED_FEATURE, - "Not a key frame."); - } - - VP8Get(br); // ignore the value of update_proba_ - - VP8ParseProba(br, dec); - - // sanitized state - dec->ready_ = 1; - return 1; -} - -//------------------------------------------------------------------------------ -// Residual decoding (Paragraph 13.2 / 13.3) - -static const uint8_t kCat3[] = { 173, 148, 140, 0 }; -static const uint8_t kCat4[] = { 176, 155, 140, 135, 0 }; -static const uint8_t kCat5[] = { 180, 157, 141, 134, 130, 0 }; -static const uint8_t kCat6[] = - { 254, 254, 243, 230, 196, 177, 153, 140, 133, 130, 129, 0 }; -static const uint8_t* const kCat3456[] = { kCat3, kCat4, kCat5, kCat6 }; -static const uint8_t kZigzag[16] = { - 0, 1, 4, 8, 5, 2, 3, 6, 9, 12, 13, 10, 7, 11, 14, 15 -}; - -// See section 13-2: http://tools.ietf.org/html/rfc6386#section-13.2 -static int GetLargeValue(VP8BitReader* const br, const uint8_t* const p) { - int v; - if (!VP8GetBit(br, p[3])) { - if (!VP8GetBit(br, p[4])) { - v = 2; - } else { - v = 3 + VP8GetBit(br, p[5]); - } - } else { - if (!VP8GetBit(br, p[6])) { - if (!VP8GetBit(br, p[7])) { - v = 5 + VP8GetBit(br, 159); - } else { - v = 7 + 2 * VP8GetBit(br, 165); - v += VP8GetBit(br, 145); - } - } else { - const uint8_t* tab; - const int bit1 = VP8GetBit(br, p[8]); - const int bit0 = VP8GetBit(br, p[9 + bit1]); - const int cat = 2 * bit1 + bit0; - v = 0; - for (tab = kCat3456[cat]; *tab; ++tab) { - v += v + VP8GetBit(br, *tab); - } - v += 3 + (8 << cat); - } - } - return v; -} - -// Returns the position of the last non-zero coeff plus one -static int GetCoeffs(VP8BitReader* const br, const VP8BandProbas* const prob[], - int ctx, const quant_t dq, int n, int16_t* out) { - const uint8_t* p = prob[n]->probas_[ctx]; - for (; n < 16; ++n) { - if (!VP8GetBit(br, p[0])) { - return n; // previous coeff was last non-zero coeff - } - while (!VP8GetBit(br, p[1])) { // sequence of zero coeffs - p = prob[++n]->probas_[0]; - if (n == 16) return 16; - } - { // non zero coeff - const VP8ProbaArray* const p_ctx = &prob[n + 1]->probas_[0]; - int v; - if (!VP8GetBit(br, p[2])) { - v = 1; - p = p_ctx[1]; - } else { - v = GetLargeValue(br, p); - p = p_ctx[2]; - } - out[kZigzag[n]] = VP8GetSigned(br, v) * dq[n > 0]; - } - } - return 16; -} - -static WEBP_INLINE uint32_t NzCodeBits(uint32_t nz_coeffs, int nz, int dc_nz) { - nz_coeffs <<= 2; - nz_coeffs |= (nz > 3) ? 3 : (nz > 1) ? 2 : dc_nz; - return nz_coeffs; -} - -static int ParseResiduals(VP8Decoder* const dec, - VP8MB* const mb, VP8BitReader* const token_br) { - const VP8BandProbas* (* const bands)[16 + 1] = dec->proba_.bands_ptr_; - const VP8BandProbas* const * ac_proba; - VP8MBData* const block = dec->mb_data_ + dec->mb_x_; - const VP8QuantMatrix* const q = &dec->dqm_[block->segment_]; - int16_t* dst = block->coeffs_; - VP8MB* const left_mb = dec->mb_info_ - 1; - uint8_t tnz, lnz; - uint32_t non_zero_y = 0; - uint32_t non_zero_uv = 0; - int x, y, ch; - uint32_t out_t_nz, out_l_nz; - int first; - - memset(dst, 0, 384 * sizeof(*dst)); - if (!block->is_i4x4_) { // parse DC - int16_t dc[16] = { 0 }; - const int ctx = mb->nz_dc_ + left_mb->nz_dc_; - const int nz = GetCoeffs(token_br, bands[1], ctx, q->y2_mat_, 0, dc); - mb->nz_dc_ = left_mb->nz_dc_ = (nz > 0); - if (nz > 1) { // more than just the DC -> perform the full transform - VP8TransformWHT(dc, dst); - } else { // only DC is non-zero -> inlined simplified transform - int i; - const int dc0 = (dc[0] + 3) >> 3; - for (i = 0; i < 16 * 16; i += 16) dst[i] = dc0; - } - first = 1; - ac_proba = bands[0]; - } else { - first = 0; - ac_proba = bands[3]; - } - - tnz = mb->nz_ & 0x0f; - lnz = left_mb->nz_ & 0x0f; - for (y = 0; y < 4; ++y) { - int l = lnz & 1; - uint32_t nz_coeffs = 0; - for (x = 0; x < 4; ++x) { - const int ctx = l + (tnz & 1); - const int nz = GetCoeffs(token_br, ac_proba, ctx, q->y1_mat_, first, dst); - l = (nz > first); - tnz = (tnz >> 1) | (l << 7); - nz_coeffs = NzCodeBits(nz_coeffs, nz, dst[0] != 0); - dst += 16; - } - tnz >>= 4; - lnz = (lnz >> 1) | (l << 7); - non_zero_y = (non_zero_y << 8) | nz_coeffs; - } - out_t_nz = tnz; - out_l_nz = lnz >> 4; - - for (ch = 0; ch < 4; ch += 2) { - uint32_t nz_coeffs = 0; - tnz = mb->nz_ >> (4 + ch); - lnz = left_mb->nz_ >> (4 + ch); - for (y = 0; y < 2; ++y) { - int l = lnz & 1; - for (x = 0; x < 2; ++x) { - const int ctx = l + (tnz & 1); - const int nz = GetCoeffs(token_br, bands[2], ctx, q->uv_mat_, 0, dst); - l = (nz > 0); - tnz = (tnz >> 1) | (l << 3); - nz_coeffs = NzCodeBits(nz_coeffs, nz, dst[0] != 0); - dst += 16; - } - tnz >>= 2; - lnz = (lnz >> 1) | (l << 5); - } - // Note: we don't really need the per-4x4 details for U/V blocks. - non_zero_uv |= nz_coeffs << (4 * ch); - out_t_nz |= (tnz << 4) << ch; - out_l_nz |= (lnz & 0xf0) << ch; - } - mb->nz_ = out_t_nz; - left_mb->nz_ = out_l_nz; - - block->non_zero_y_ = non_zero_y; - block->non_zero_uv_ = non_zero_uv; - - // We look at the mode-code of each block and check if some blocks have less - // than three non-zero coeffs (code < 2). This is to avoid dithering flat and - // empty blocks. - block->dither_ = (non_zero_uv & 0xaaaa) ? 0 : q->dither_; - - return !(non_zero_y | non_zero_uv); // will be used for further optimization -} - -//------------------------------------------------------------------------------ -// Main loop - -int VP8DecodeMB(VP8Decoder* const dec, VP8BitReader* const token_br) { - VP8MB* const left = dec->mb_info_ - 1; - VP8MB* const mb = dec->mb_info_ + dec->mb_x_; - VP8MBData* const block = dec->mb_data_ + dec->mb_x_; - int skip = dec->use_skip_proba_ ? block->skip_ : 0; - - if (!skip) { - skip = ParseResiduals(dec, mb, token_br); - } else { - left->nz_ = mb->nz_ = 0; - if (!block->is_i4x4_) { - left->nz_dc_ = mb->nz_dc_ = 0; - } - block->non_zero_y_ = 0; - block->non_zero_uv_ = 0; - block->dither_ = 0; - } - - if (dec->filter_type_ > 0) { // store filter info - VP8FInfo* const finfo = dec->f_info_ + dec->mb_x_; - *finfo = dec->fstrengths_[block->segment_][block->is_i4x4_]; - finfo->f_inner_ |= !skip; - } - - return !token_br->eof_; -} - -void VP8InitScanline(VP8Decoder* const dec) { - VP8MB* const left = dec->mb_info_ - 1; - left->nz_ = 0; - left->nz_dc_ = 0; - memset(dec->intra_l_, B_DC_PRED, sizeof(dec->intra_l_)); - dec->mb_x_ = 0; -} - -static int ParseFrame(VP8Decoder* const dec, VP8Io* io) { - for (dec->mb_y_ = 0; dec->mb_y_ < dec->br_mb_y_; ++dec->mb_y_) { - // Parse bitstream for this row. - VP8BitReader* const token_br = - &dec->parts_[dec->mb_y_ & dec->num_parts_minus_one_]; - if (!VP8ParseIntraModeRow(&dec->br_, dec)) { - return VP8SetError(dec, VP8_STATUS_NOT_ENOUGH_DATA, - "Premature end-of-partition0 encountered."); - } - for (; dec->mb_x_ < dec->mb_w_; ++dec->mb_x_) { - if (!VP8DecodeMB(dec, token_br)) { - return VP8SetError(dec, VP8_STATUS_NOT_ENOUGH_DATA, - "Premature end-of-file encountered."); - } - } - VP8InitScanline(dec); // Prepare for next scanline - - // Reconstruct, filter and emit the row. - if (!VP8ProcessRow(dec, io)) { - return VP8SetError(dec, VP8_STATUS_USER_ABORT, "Output aborted."); - } - } - if (dec->mt_method_ > 0) { - if (!WebPGetWorkerInterface()->Sync(&dec->worker_)) return 0; - } - - return 1; -} - -// Main entry point -int VP8Decode(VP8Decoder* const dec, VP8Io* const io) { - int ok = 0; - if (dec == NULL) { - return 0; - } - if (io == NULL) { - return VP8SetError(dec, VP8_STATUS_INVALID_PARAM, - "NULL VP8Io parameter in VP8Decode()."); - } - - if (!dec->ready_) { - if (!VP8GetHeaders(dec, io)) { - return 0; - } - } - assert(dec->ready_); - - // Finish setting up the decoding parameter. Will call io->setup(). - ok = (VP8EnterCritical(dec, io) == VP8_STATUS_OK); - if (ok) { // good to go. - // Will allocate memory and prepare everything. - if (ok) ok = VP8InitFrame(dec, io); - - // Main decoding loop - if (ok) ok = ParseFrame(dec, io); - - // Exit. - ok &= VP8ExitCritical(dec, io); - } - - if (!ok) { - VP8Clear(dec); - return 0; - } - - dec->ready_ = 0; - return ok; -} - -void VP8Clear(VP8Decoder* const dec) { - if (dec == NULL) { - return; - } - WebPGetWorkerInterface()->End(&dec->worker_); - WebPDeallocateAlphaMemory(dec); - WebPSafeFree(dec->mem_); - dec->mem_ = NULL; - dec->mem_size_ = 0; - memset(&dec->br_, 0, sizeof(dec->br_)); - dec->ready_ = 0; -} - -//------------------------------------------------------------------------------ diff --git a/thirdparty/libwebp/dec/vp8_dec.c b/thirdparty/libwebp/dec/vp8_dec.c new file mode 100644 index 0000000000..fad8d9cf35 --- /dev/null +++ b/thirdparty/libwebp/dec/vp8_dec.c @@ -0,0 +1,721 @@ +// Copyright 2010 Google Inc. All Rights Reserved. +// +// Use of this source code is governed by a BSD-style license +// that can be found in the COPYING file in the root of the source +// tree. An additional intellectual property rights grant can be found +// in the file PATENTS. All contributing project authors may +// be found in the AUTHORS file in the root of the source tree. +// ----------------------------------------------------------------------------- +// +// main entry for the decoder +// +// Author: Skal (pascal.massimino@gmail.com) + +#include + +#include "./alphai_dec.h" +#include "./vp8i_dec.h" +#include "./vp8li_dec.h" +#include "./webpi_dec.h" +#include "../utils/bit_reader_inl_utils.h" +#include "../utils/utils.h" + +//------------------------------------------------------------------------------ + +int WebPGetDecoderVersion(void) { + return (DEC_MAJ_VERSION << 16) | (DEC_MIN_VERSION << 8) | DEC_REV_VERSION; +} + +//------------------------------------------------------------------------------ +// Signature and pointer-to-function for GetCoeffs() variants below. + +typedef int (*GetCoeffsFunc)(VP8BitReader* const br, + const VP8BandProbas* const prob[], + int ctx, const quant_t dq, int n, int16_t* out); +static volatile GetCoeffsFunc GetCoeffs = NULL; + +static void InitGetCoeffs(void); + +//------------------------------------------------------------------------------ +// VP8Decoder + +static void SetOk(VP8Decoder* const dec) { + dec->status_ = VP8_STATUS_OK; + dec->error_msg_ = "OK"; +} + +int VP8InitIoInternal(VP8Io* const io, int version) { + if (WEBP_ABI_IS_INCOMPATIBLE(version, WEBP_DECODER_ABI_VERSION)) { + return 0; // mismatch error + } + if (io != NULL) { + memset(io, 0, sizeof(*io)); + } + return 1; +} + +VP8Decoder* VP8New(void) { + VP8Decoder* const dec = (VP8Decoder*)WebPSafeCalloc(1ULL, sizeof(*dec)); + if (dec != NULL) { + SetOk(dec); + WebPGetWorkerInterface()->Init(&dec->worker_); + dec->ready_ = 0; + dec->num_parts_minus_one_ = 0; + InitGetCoeffs(); + } + return dec; +} + +VP8StatusCode VP8Status(VP8Decoder* const dec) { + if (!dec) return VP8_STATUS_INVALID_PARAM; + return dec->status_; +} + +const char* VP8StatusMessage(VP8Decoder* const dec) { + if (dec == NULL) return "no object"; + if (!dec->error_msg_) return "OK"; + return dec->error_msg_; +} + +void VP8Delete(VP8Decoder* const dec) { + if (dec != NULL) { + VP8Clear(dec); + WebPSafeFree(dec); + } +} + +int VP8SetError(VP8Decoder* const dec, + VP8StatusCode error, const char* const msg) { + // The oldest error reported takes precedence over the new one. + if (dec->status_ == VP8_STATUS_OK) { + dec->status_ = error; + dec->error_msg_ = msg; + dec->ready_ = 0; + } + return 0; +} + +//------------------------------------------------------------------------------ + +int VP8CheckSignature(const uint8_t* const data, size_t data_size) { + return (data_size >= 3 && + data[0] == 0x9d && data[1] == 0x01 && data[2] == 0x2a); +} + +int VP8GetInfo(const uint8_t* data, size_t data_size, size_t chunk_size, + int* const width, int* const height) { + if (data == NULL || data_size < VP8_FRAME_HEADER_SIZE) { + return 0; // not enough data + } + // check signature + if (!VP8CheckSignature(data + 3, data_size - 3)) { + return 0; // Wrong signature. + } else { + const uint32_t bits = data[0] | (data[1] << 8) | (data[2] << 16); + const int key_frame = !(bits & 1); + const int w = ((data[7] << 8) | data[6]) & 0x3fff; + const int h = ((data[9] << 8) | data[8]) & 0x3fff; + + if (!key_frame) { // Not a keyframe. + return 0; + } + + if (((bits >> 1) & 7) > 3) { + return 0; // unknown profile + } + if (!((bits >> 4) & 1)) { + return 0; // first frame is invisible! + } + if (((bits >> 5)) >= chunk_size) { // partition_length + return 0; // inconsistent size information. + } + if (w == 0 || h == 0) { + return 0; // We don't support both width and height to be zero. + } + + if (width) { + *width = w; + } + if (height) { + *height = h; + } + + return 1; + } +} + +//------------------------------------------------------------------------------ +// Header parsing + +static void ResetSegmentHeader(VP8SegmentHeader* const hdr) { + assert(hdr != NULL); + hdr->use_segment_ = 0; + hdr->update_map_ = 0; + hdr->absolute_delta_ = 1; + memset(hdr->quantizer_, 0, sizeof(hdr->quantizer_)); + memset(hdr->filter_strength_, 0, sizeof(hdr->filter_strength_)); +} + +// Paragraph 9.3 +static int ParseSegmentHeader(VP8BitReader* br, + VP8SegmentHeader* hdr, VP8Proba* proba) { + assert(br != NULL); + assert(hdr != NULL); + hdr->use_segment_ = VP8Get(br); + if (hdr->use_segment_) { + hdr->update_map_ = VP8Get(br); + if (VP8Get(br)) { // update data + int s; + hdr->absolute_delta_ = VP8Get(br); + for (s = 0; s < NUM_MB_SEGMENTS; ++s) { + hdr->quantizer_[s] = VP8Get(br) ? VP8GetSignedValue(br, 7) : 0; + } + for (s = 0; s < NUM_MB_SEGMENTS; ++s) { + hdr->filter_strength_[s] = VP8Get(br) ? VP8GetSignedValue(br, 6) : 0; + } + } + if (hdr->update_map_) { + int s; + for (s = 0; s < MB_FEATURE_TREE_PROBS; ++s) { + proba->segments_[s] = VP8Get(br) ? VP8GetValue(br, 8) : 255u; + } + } + } else { + hdr->update_map_ = 0; + } + return !br->eof_; +} + +// Paragraph 9.5 +// This function returns VP8_STATUS_SUSPENDED if we don't have all the +// necessary data in 'buf'. +// This case is not necessarily an error (for incremental decoding). +// Still, no bitreader is ever initialized to make it possible to read +// unavailable memory. +// If we don't even have the partitions' sizes, than VP8_STATUS_NOT_ENOUGH_DATA +// is returned, and this is an unrecoverable error. +// If the partitions were positioned ok, VP8_STATUS_OK is returned. +static VP8StatusCode ParsePartitions(VP8Decoder* const dec, + const uint8_t* buf, size_t size) { + VP8BitReader* const br = &dec->br_; + const uint8_t* sz = buf; + const uint8_t* buf_end = buf + size; + const uint8_t* part_start; + size_t size_left = size; + size_t last_part; + size_t p; + + dec->num_parts_minus_one_ = (1 << VP8GetValue(br, 2)) - 1; + last_part = dec->num_parts_minus_one_; + if (size < 3 * last_part) { + // we can't even read the sizes with sz[]! That's a failure. + return VP8_STATUS_NOT_ENOUGH_DATA; + } + part_start = buf + last_part * 3; + size_left -= last_part * 3; + for (p = 0; p < last_part; ++p) { + size_t psize = sz[0] | (sz[1] << 8) | (sz[2] << 16); + if (psize > size_left) psize = size_left; + VP8InitBitReader(dec->parts_ + p, part_start, psize); + part_start += psize; + size_left -= psize; + sz += 3; + } + VP8InitBitReader(dec->parts_ + last_part, part_start, size_left); + return (part_start < buf_end) ? VP8_STATUS_OK : + VP8_STATUS_SUSPENDED; // Init is ok, but there's not enough data +} + +// Paragraph 9.4 +static int ParseFilterHeader(VP8BitReader* br, VP8Decoder* const dec) { + VP8FilterHeader* const hdr = &dec->filter_hdr_; + hdr->simple_ = VP8Get(br); + hdr->level_ = VP8GetValue(br, 6); + hdr->sharpness_ = VP8GetValue(br, 3); + hdr->use_lf_delta_ = VP8Get(br); + if (hdr->use_lf_delta_) { + if (VP8Get(br)) { // update lf-delta? + int i; + for (i = 0; i < NUM_REF_LF_DELTAS; ++i) { + if (VP8Get(br)) { + hdr->ref_lf_delta_[i] = VP8GetSignedValue(br, 6); + } + } + for (i = 0; i < NUM_MODE_LF_DELTAS; ++i) { + if (VP8Get(br)) { + hdr->mode_lf_delta_[i] = VP8GetSignedValue(br, 6); + } + } + } + } + dec->filter_type_ = (hdr->level_ == 0) ? 0 : hdr->simple_ ? 1 : 2; + return !br->eof_; +} + +// Topmost call +int VP8GetHeaders(VP8Decoder* const dec, VP8Io* const io) { + const uint8_t* buf; + size_t buf_size; + VP8FrameHeader* frm_hdr; + VP8PictureHeader* pic_hdr; + VP8BitReader* br; + VP8StatusCode status; + + if (dec == NULL) { + return 0; + } + SetOk(dec); + if (io == NULL) { + return VP8SetError(dec, VP8_STATUS_INVALID_PARAM, + "null VP8Io passed to VP8GetHeaders()"); + } + buf = io->data; + buf_size = io->data_size; + if (buf_size < 4) { + return VP8SetError(dec, VP8_STATUS_NOT_ENOUGH_DATA, + "Truncated header."); + } + + // Paragraph 9.1 + { + const uint32_t bits = buf[0] | (buf[1] << 8) | (buf[2] << 16); + frm_hdr = &dec->frm_hdr_; + frm_hdr->key_frame_ = !(bits & 1); + frm_hdr->profile_ = (bits >> 1) & 7; + frm_hdr->show_ = (bits >> 4) & 1; + frm_hdr->partition_length_ = (bits >> 5); + if (frm_hdr->profile_ > 3) { + return VP8SetError(dec, VP8_STATUS_BITSTREAM_ERROR, + "Incorrect keyframe parameters."); + } + if (!frm_hdr->show_) { + return VP8SetError(dec, VP8_STATUS_UNSUPPORTED_FEATURE, + "Frame not displayable."); + } + buf += 3; + buf_size -= 3; + } + + pic_hdr = &dec->pic_hdr_; + if (frm_hdr->key_frame_) { + // Paragraph 9.2 + if (buf_size < 7) { + return VP8SetError(dec, VP8_STATUS_NOT_ENOUGH_DATA, + "cannot parse picture header"); + } + if (!VP8CheckSignature(buf, buf_size)) { + return VP8SetError(dec, VP8_STATUS_BITSTREAM_ERROR, + "Bad code word"); + } + pic_hdr->width_ = ((buf[4] << 8) | buf[3]) & 0x3fff; + pic_hdr->xscale_ = buf[4] >> 6; // ratio: 1, 5/4 5/3 or 2 + pic_hdr->height_ = ((buf[6] << 8) | buf[5]) & 0x3fff; + pic_hdr->yscale_ = buf[6] >> 6; + buf += 7; + buf_size -= 7; + + dec->mb_w_ = (pic_hdr->width_ + 15) >> 4; + dec->mb_h_ = (pic_hdr->height_ + 15) >> 4; + + // Setup default output area (can be later modified during io->setup()) + io->width = pic_hdr->width_; + io->height = pic_hdr->height_; + // IMPORTANT! use some sane dimensions in crop_* and scaled_* fields. + // So they can be used interchangeably without always testing for + // 'use_cropping'. + io->use_cropping = 0; + io->crop_top = 0; + io->crop_left = 0; + io->crop_right = io->width; + io->crop_bottom = io->height; + io->use_scaling = 0; + io->scaled_width = io->width; + io->scaled_height = io->height; + + io->mb_w = io->width; // sanity check + io->mb_h = io->height; // ditto + + VP8ResetProba(&dec->proba_); + ResetSegmentHeader(&dec->segment_hdr_); + } + + // Check if we have all the partition #0 available, and initialize dec->br_ + // to read this partition (and this partition only). + if (frm_hdr->partition_length_ > buf_size) { + return VP8SetError(dec, VP8_STATUS_NOT_ENOUGH_DATA, + "bad partition length"); + } + + br = &dec->br_; + VP8InitBitReader(br, buf, frm_hdr->partition_length_); + buf += frm_hdr->partition_length_; + buf_size -= frm_hdr->partition_length_; + + if (frm_hdr->key_frame_) { + pic_hdr->colorspace_ = VP8Get(br); + pic_hdr->clamp_type_ = VP8Get(br); + } + if (!ParseSegmentHeader(br, &dec->segment_hdr_, &dec->proba_)) { + return VP8SetError(dec, VP8_STATUS_BITSTREAM_ERROR, + "cannot parse segment header"); + } + // Filter specs + if (!ParseFilterHeader(br, dec)) { + return VP8SetError(dec, VP8_STATUS_BITSTREAM_ERROR, + "cannot parse filter header"); + } + status = ParsePartitions(dec, buf, buf_size); + if (status != VP8_STATUS_OK) { + return VP8SetError(dec, status, "cannot parse partitions"); + } + + // quantizer change + VP8ParseQuant(dec); + + // Frame buffer marking + if (!frm_hdr->key_frame_) { + return VP8SetError(dec, VP8_STATUS_UNSUPPORTED_FEATURE, + "Not a key frame."); + } + + VP8Get(br); // ignore the value of update_proba_ + + VP8ParseProba(br, dec); + + // sanitized state + dec->ready_ = 1; + return 1; +} + +//------------------------------------------------------------------------------ +// Residual decoding (Paragraph 13.2 / 13.3) + +static const uint8_t kCat3[] = { 173, 148, 140, 0 }; +static const uint8_t kCat4[] = { 176, 155, 140, 135, 0 }; +static const uint8_t kCat5[] = { 180, 157, 141, 134, 130, 0 }; +static const uint8_t kCat6[] = + { 254, 254, 243, 230, 196, 177, 153, 140, 133, 130, 129, 0 }; +static const uint8_t* const kCat3456[] = { kCat3, kCat4, kCat5, kCat6 }; +static const uint8_t kZigzag[16] = { + 0, 1, 4, 8, 5, 2, 3, 6, 9, 12, 13, 10, 7, 11, 14, 15 +}; + +// See section 13-2: http://tools.ietf.org/html/rfc6386#section-13.2 +static int GetLargeValue(VP8BitReader* const br, const uint8_t* const p) { + int v; + if (!VP8GetBit(br, p[3])) { + if (!VP8GetBit(br, p[4])) { + v = 2; + } else { + v = 3 + VP8GetBit(br, p[5]); + } + } else { + if (!VP8GetBit(br, p[6])) { + if (!VP8GetBit(br, p[7])) { + v = 5 + VP8GetBit(br, 159); + } else { + v = 7 + 2 * VP8GetBit(br, 165); + v += VP8GetBit(br, 145); + } + } else { + const uint8_t* tab; + const int bit1 = VP8GetBit(br, p[8]); + const int bit0 = VP8GetBit(br, p[9 + bit1]); + const int cat = 2 * bit1 + bit0; + v = 0; + for (tab = kCat3456[cat]; *tab; ++tab) { + v += v + VP8GetBit(br, *tab); + } + v += 3 + (8 << cat); + } + } + return v; +} + +// Returns the position of the last non-zero coeff plus one +static int GetCoeffsFast(VP8BitReader* const br, + const VP8BandProbas* const prob[], + int ctx, const quant_t dq, int n, int16_t* out) { + const uint8_t* p = prob[n]->probas_[ctx]; + for (; n < 16; ++n) { + if (!VP8GetBit(br, p[0])) { + return n; // previous coeff was last non-zero coeff + } + while (!VP8GetBit(br, p[1])) { // sequence of zero coeffs + p = prob[++n]->probas_[0]; + if (n == 16) return 16; + } + { // non zero coeff + const VP8ProbaArray* const p_ctx = &prob[n + 1]->probas_[0]; + int v; + if (!VP8GetBit(br, p[2])) { + v = 1; + p = p_ctx[1]; + } else { + v = GetLargeValue(br, p); + p = p_ctx[2]; + } + out[kZigzag[n]] = VP8GetSigned(br, v) * dq[n > 0]; + } + } + return 16; +} + +// This version of GetCoeffs() uses VP8GetBitAlt() which is an alternate version +// of VP8GetBitAlt() targeting specific platforms. +static int GetCoeffsAlt(VP8BitReader* const br, + const VP8BandProbas* const prob[], + int ctx, const quant_t dq, int n, int16_t* out) { + const uint8_t* p = prob[n]->probas_[ctx]; + for (; n < 16; ++n) { + if (!VP8GetBitAlt(br, p[0])) { + return n; // previous coeff was last non-zero coeff + } + while (!VP8GetBitAlt(br, p[1])) { // sequence of zero coeffs + p = prob[++n]->probas_[0]; + if (n == 16) return 16; + } + { // non zero coeff + const VP8ProbaArray* const p_ctx = &prob[n + 1]->probas_[0]; + int v; + if (!VP8GetBitAlt(br, p[2])) { + v = 1; + p = p_ctx[1]; + } else { + v = GetLargeValue(br, p); + p = p_ctx[2]; + } + out[kZigzag[n]] = VP8GetSigned(br, v) * dq[n > 0]; + } + } + return 16; +} + +WEBP_TSAN_IGNORE_FUNCTION static void InitGetCoeffs(void) { + if (GetCoeffs == NULL) { + if (VP8GetCPUInfo != NULL && VP8GetCPUInfo(kSlowSSSE3)) { + GetCoeffs = GetCoeffsAlt; + } else { + GetCoeffs = GetCoeffsFast; + } + } +} + +static WEBP_INLINE uint32_t NzCodeBits(uint32_t nz_coeffs, int nz, int dc_nz) { + nz_coeffs <<= 2; + nz_coeffs |= (nz > 3) ? 3 : (nz > 1) ? 2 : dc_nz; + return nz_coeffs; +} + +static int ParseResiduals(VP8Decoder* const dec, + VP8MB* const mb, VP8BitReader* const token_br) { + const VP8BandProbas* (* const bands)[16 + 1] = dec->proba_.bands_ptr_; + const VP8BandProbas* const * ac_proba; + VP8MBData* const block = dec->mb_data_ + dec->mb_x_; + const VP8QuantMatrix* const q = &dec->dqm_[block->segment_]; + int16_t* dst = block->coeffs_; + VP8MB* const left_mb = dec->mb_info_ - 1; + uint8_t tnz, lnz; + uint32_t non_zero_y = 0; + uint32_t non_zero_uv = 0; + int x, y, ch; + uint32_t out_t_nz, out_l_nz; + int first; + + memset(dst, 0, 384 * sizeof(*dst)); + if (!block->is_i4x4_) { // parse DC + int16_t dc[16] = { 0 }; + const int ctx = mb->nz_dc_ + left_mb->nz_dc_; + const int nz = GetCoeffs(token_br, bands[1], ctx, q->y2_mat_, 0, dc); + mb->nz_dc_ = left_mb->nz_dc_ = (nz > 0); + if (nz > 1) { // more than just the DC -> perform the full transform + VP8TransformWHT(dc, dst); + } else { // only DC is non-zero -> inlined simplified transform + int i; + const int dc0 = (dc[0] + 3) >> 3; + for (i = 0; i < 16 * 16; i += 16) dst[i] = dc0; + } + first = 1; + ac_proba = bands[0]; + } else { + first = 0; + ac_proba = bands[3]; + } + + tnz = mb->nz_ & 0x0f; + lnz = left_mb->nz_ & 0x0f; + for (y = 0; y < 4; ++y) { + int l = lnz & 1; + uint32_t nz_coeffs = 0; + for (x = 0; x < 4; ++x) { + const int ctx = l + (tnz & 1); + const int nz = GetCoeffs(token_br, ac_proba, ctx, q->y1_mat_, first, dst); + l = (nz > first); + tnz = (tnz >> 1) | (l << 7); + nz_coeffs = NzCodeBits(nz_coeffs, nz, dst[0] != 0); + dst += 16; + } + tnz >>= 4; + lnz = (lnz >> 1) | (l << 7); + non_zero_y = (non_zero_y << 8) | nz_coeffs; + } + out_t_nz = tnz; + out_l_nz = lnz >> 4; + + for (ch = 0; ch < 4; ch += 2) { + uint32_t nz_coeffs = 0; + tnz = mb->nz_ >> (4 + ch); + lnz = left_mb->nz_ >> (4 + ch); + for (y = 0; y < 2; ++y) { + int l = lnz & 1; + for (x = 0; x < 2; ++x) { + const int ctx = l + (tnz & 1); + const int nz = GetCoeffs(token_br, bands[2], ctx, q->uv_mat_, 0, dst); + l = (nz > 0); + tnz = (tnz >> 1) | (l << 3); + nz_coeffs = NzCodeBits(nz_coeffs, nz, dst[0] != 0); + dst += 16; + } + tnz >>= 2; + lnz = (lnz >> 1) | (l << 5); + } + // Note: we don't really need the per-4x4 details for U/V blocks. + non_zero_uv |= nz_coeffs << (4 * ch); + out_t_nz |= (tnz << 4) << ch; + out_l_nz |= (lnz & 0xf0) << ch; + } + mb->nz_ = out_t_nz; + left_mb->nz_ = out_l_nz; + + block->non_zero_y_ = non_zero_y; + block->non_zero_uv_ = non_zero_uv; + + // We look at the mode-code of each block and check if some blocks have less + // than three non-zero coeffs (code < 2). This is to avoid dithering flat and + // empty blocks. + block->dither_ = (non_zero_uv & 0xaaaa) ? 0 : q->dither_; + + return !(non_zero_y | non_zero_uv); // will be used for further optimization +} + +//------------------------------------------------------------------------------ +// Main loop + +int VP8DecodeMB(VP8Decoder* const dec, VP8BitReader* const token_br) { + VP8MB* const left = dec->mb_info_ - 1; + VP8MB* const mb = dec->mb_info_ + dec->mb_x_; + VP8MBData* const block = dec->mb_data_ + dec->mb_x_; + int skip = dec->use_skip_proba_ ? block->skip_ : 0; + + if (!skip) { + skip = ParseResiduals(dec, mb, token_br); + } else { + left->nz_ = mb->nz_ = 0; + if (!block->is_i4x4_) { + left->nz_dc_ = mb->nz_dc_ = 0; + } + block->non_zero_y_ = 0; + block->non_zero_uv_ = 0; + block->dither_ = 0; + } + + if (dec->filter_type_ > 0) { // store filter info + VP8FInfo* const finfo = dec->f_info_ + dec->mb_x_; + *finfo = dec->fstrengths_[block->segment_][block->is_i4x4_]; + finfo->f_inner_ |= !skip; + } + + return !token_br->eof_; +} + +void VP8InitScanline(VP8Decoder* const dec) { + VP8MB* const left = dec->mb_info_ - 1; + left->nz_ = 0; + left->nz_dc_ = 0; + memset(dec->intra_l_, B_DC_PRED, sizeof(dec->intra_l_)); + dec->mb_x_ = 0; +} + +static int ParseFrame(VP8Decoder* const dec, VP8Io* io) { + for (dec->mb_y_ = 0; dec->mb_y_ < dec->br_mb_y_; ++dec->mb_y_) { + // Parse bitstream for this row. + VP8BitReader* const token_br = + &dec->parts_[dec->mb_y_ & dec->num_parts_minus_one_]; + if (!VP8ParseIntraModeRow(&dec->br_, dec)) { + return VP8SetError(dec, VP8_STATUS_NOT_ENOUGH_DATA, + "Premature end-of-partition0 encountered."); + } + for (; dec->mb_x_ < dec->mb_w_; ++dec->mb_x_) { + if (!VP8DecodeMB(dec, token_br)) { + return VP8SetError(dec, VP8_STATUS_NOT_ENOUGH_DATA, + "Premature end-of-file encountered."); + } + } + VP8InitScanline(dec); // Prepare for next scanline + + // Reconstruct, filter and emit the row. + if (!VP8ProcessRow(dec, io)) { + return VP8SetError(dec, VP8_STATUS_USER_ABORT, "Output aborted."); + } + } + if (dec->mt_method_ > 0) { + if (!WebPGetWorkerInterface()->Sync(&dec->worker_)) return 0; + } + + return 1; +} + +// Main entry point +int VP8Decode(VP8Decoder* const dec, VP8Io* const io) { + int ok = 0; + if (dec == NULL) { + return 0; + } + if (io == NULL) { + return VP8SetError(dec, VP8_STATUS_INVALID_PARAM, + "NULL VP8Io parameter in VP8Decode()."); + } + + if (!dec->ready_) { + if (!VP8GetHeaders(dec, io)) { + return 0; + } + } + assert(dec->ready_); + + // Finish setting up the decoding parameter. Will call io->setup(). + ok = (VP8EnterCritical(dec, io) == VP8_STATUS_OK); + if (ok) { // good to go. + // Will allocate memory and prepare everything. + if (ok) ok = VP8InitFrame(dec, io); + + // Main decoding loop + if (ok) ok = ParseFrame(dec, io); + + // Exit. + ok &= VP8ExitCritical(dec, io); + } + + if (!ok) { + VP8Clear(dec); + return 0; + } + + dec->ready_ = 0; + return ok; +} + +void VP8Clear(VP8Decoder* const dec) { + if (dec == NULL) { + return; + } + WebPGetWorkerInterface()->End(&dec->worker_); + WebPDeallocateAlphaMemory(dec); + WebPSafeFree(dec->mem_); + dec->mem_ = NULL; + dec->mem_size_ = 0; + memset(&dec->br_, 0, sizeof(dec->br_)); + dec->ready_ = 0; +} + +//------------------------------------------------------------------------------ diff --git a/thirdparty/libwebp/dec/vp8_dec.h b/thirdparty/libwebp/dec/vp8_dec.h new file mode 100644 index 0000000000..b9337bbec0 --- /dev/null +++ b/thirdparty/libwebp/dec/vp8_dec.h @@ -0,0 +1,185 @@ +// Copyright 2010 Google Inc. All Rights Reserved. +// +// Use of this source code is governed by a BSD-style license +// that can be found in the COPYING file in the root of the source +// tree. An additional intellectual property rights grant can be found +// in the file PATENTS. All contributing project authors may +// be found in the AUTHORS file in the root of the source tree. +// ----------------------------------------------------------------------------- +// +// Low-level API for VP8 decoder +// +// Author: Skal (pascal.massimino@gmail.com) + +#ifndef WEBP_WEBP_DECODE_VP8_H_ +#define WEBP_WEBP_DECODE_VP8_H_ + +#include "../webp/decode.h" + +#ifdef __cplusplus +extern "C" { +#endif + +//------------------------------------------------------------------------------ +// Lower-level API +// +// These functions provide fine-grained control of the decoding process. +// The call flow should resemble: +// +// VP8Io io; +// VP8InitIo(&io); +// io.data = data; +// io.data_size = size; +// /* customize io's functions (setup()/put()/teardown()) if needed. */ +// +// VP8Decoder* dec = VP8New(); +// bool ok = VP8Decode(dec); +// if (!ok) printf("Error: %s\n", VP8StatusMessage(dec)); +// VP8Delete(dec); +// return ok; + +// Input / Output +typedef struct VP8Io VP8Io; +typedef int (*VP8IoPutHook)(const VP8Io* io); +typedef int (*VP8IoSetupHook)(VP8Io* io); +typedef void (*VP8IoTeardownHook)(const VP8Io* io); + +struct VP8Io { + // set by VP8GetHeaders() + int width, height; // picture dimensions, in pixels (invariable). + // These are the original, uncropped dimensions. + // The actual area passed to put() is stored + // in mb_w / mb_h fields. + + // set before calling put() + int mb_y; // position of the current rows (in pixels) + int mb_w; // number of columns in the sample + int mb_h; // number of rows in the sample + const uint8_t* y, *u, *v; // rows to copy (in yuv420 format) + int y_stride; // row stride for luma + int uv_stride; // row stride for chroma + + void* opaque; // user data + + // called when fresh samples are available. Currently, samples are in + // YUV420 format, and can be up to width x 24 in size (depending on the + // in-loop filtering level, e.g.). Should return false in case of error + // or abort request. The actual size of the area to update is mb_w x mb_h + // in size, taking cropping into account. + VP8IoPutHook put; + + // called just before starting to decode the blocks. + // Must return false in case of setup error, true otherwise. If false is + // returned, teardown() will NOT be called. But if the setup succeeded + // and true is returned, then teardown() will always be called afterward. + VP8IoSetupHook setup; + + // Called just after block decoding is finished (or when an error occurred + // during put()). Is NOT called if setup() failed. + VP8IoTeardownHook teardown; + + // this is a recommendation for the user-side yuv->rgb converter. This flag + // is set when calling setup() hook and can be overwritten by it. It then + // can be taken into consideration during the put() method. + int fancy_upsampling; + + // Input buffer. + size_t data_size; + const uint8_t* data; + + // If true, in-loop filtering will not be performed even if present in the + // bitstream. Switching off filtering may speed up decoding at the expense + // of more visible blocking. Note that output will also be non-compliant + // with the VP8 specifications. + int bypass_filtering; + + // Cropping parameters. + int use_cropping; + int crop_left, crop_right, crop_top, crop_bottom; + + // Scaling parameters. + int use_scaling; + int scaled_width, scaled_height; + + // If non NULL, pointer to the alpha data (if present) corresponding to the + // start of the current row (That is: it is pre-offset by mb_y and takes + // cropping into account). + const uint8_t* a; +}; + +// Internal, version-checked, entry point +int VP8InitIoInternal(VP8Io* const, int); + +// Set the custom IO function pointers and user-data. The setter for IO hooks +// should be called before initiating incremental decoding. Returns true if +// WebPIDecoder object is successfully modified, false otherwise. +int WebPISetIOHooks(WebPIDecoder* const idec, + VP8IoPutHook put, + VP8IoSetupHook setup, + VP8IoTeardownHook teardown, + void* user_data); + +// Main decoding object. This is an opaque structure. +typedef struct VP8Decoder VP8Decoder; + +// Create a new decoder object. +VP8Decoder* VP8New(void); + +// Must be called to make sure 'io' is initialized properly. +// Returns false in case of version mismatch. Upon such failure, no other +// decoding function should be called (VP8Decode, VP8GetHeaders, ...) +static WEBP_INLINE int VP8InitIo(VP8Io* const io) { + return VP8InitIoInternal(io, WEBP_DECODER_ABI_VERSION); +} + +// Decode the VP8 frame header. Returns true if ok. +// Note: 'io->data' must be pointing to the start of the VP8 frame header. +int VP8GetHeaders(VP8Decoder* const dec, VP8Io* const io); + +// Decode a picture. Will call VP8GetHeaders() if it wasn't done already. +// Returns false in case of error. +int VP8Decode(VP8Decoder* const dec, VP8Io* const io); + +// Return current status of the decoder: +VP8StatusCode VP8Status(VP8Decoder* const dec); + +// return readable string corresponding to the last status. +const char* VP8StatusMessage(VP8Decoder* const dec); + +// Resets the decoder in its initial state, reclaiming memory. +// Not a mandatory call between calls to VP8Decode(). +void VP8Clear(VP8Decoder* const dec); + +// Destroy the decoder object. +void VP8Delete(VP8Decoder* const dec); + +//------------------------------------------------------------------------------ +// Miscellaneous VP8/VP8L bitstream probing functions. + +// Returns true if the next 3 bytes in data contain the VP8 signature. +WEBP_EXTERN(int) VP8CheckSignature(const uint8_t* const data, size_t data_size); + +// Validates the VP8 data-header and retrieves basic header information viz +// width and height. Returns 0 in case of formatting error. *width/*height +// can be passed NULL. +WEBP_EXTERN(int) VP8GetInfo( + const uint8_t* data, + size_t data_size, // data available so far + size_t chunk_size, // total data size expected in the chunk + int* const width, int* const height); + +// Returns true if the next byte(s) in data is a VP8L signature. +WEBP_EXTERN(int) VP8LCheckSignature(const uint8_t* const data, size_t size); + +// Validates the VP8L data-header and retrieves basic header information viz +// width, height and alpha. Returns 0 in case of formatting error. +// width/height/has_alpha can be passed NULL. +WEBP_EXTERN(int) VP8LGetInfo( + const uint8_t* data, size_t data_size, // data available so far + int* const width, int* const height, int* const has_alpha); + +#ifdef __cplusplus +} // extern "C" +#endif + +#endif /* WEBP_WEBP_DECODE_VP8_H_ */ diff --git a/thirdparty/libwebp/dec/vp8i.h b/thirdparty/libwebp/dec/vp8i.h deleted file mode 100644 index 313d8a7b94..0000000000 --- a/thirdparty/libwebp/dec/vp8i.h +++ /dev/null @@ -1,320 +0,0 @@ -// Copyright 2010 Google Inc. All Rights Reserved. -// -// Use of this source code is governed by a BSD-style license -// that can be found in the COPYING file in the root of the source -// tree. An additional intellectual property rights grant can be found -// in the file PATENTS. All contributing project authors may -// be found in the AUTHORS file in the root of the source tree. -// ----------------------------------------------------------------------------- -// -// VP8 decoder: internal header. -// -// Author: Skal (pascal.massimino@gmail.com) - -#ifndef WEBP_DEC_VP8I_H_ -#define WEBP_DEC_VP8I_H_ - -#include // for memcpy() -#include "./common.h" -#include "./vp8li.h" -#include "../utils/bit_reader.h" -#include "../utils/random.h" -#include "../utils/thread.h" -#include "../dsp/dsp.h" - -#ifdef __cplusplus -extern "C" { -#endif - -//------------------------------------------------------------------------------ -// Various defines and enums - -// version numbers -#define DEC_MAJ_VERSION 0 -#define DEC_MIN_VERSION 5 -#define DEC_REV_VERSION 2 - -// YUV-cache parameters. Cache is 32-bytes wide (= one cacheline). -// Constraints are: We need to store one 16x16 block of luma samples (y), -// and two 8x8 chroma blocks (u/v). These are better be 16-bytes aligned, -// in order to be SIMD-friendly. We also need to store the top, left and -// top-left samples (from previously decoded blocks), along with four -// extra top-right samples for luma (intra4x4 prediction only). -// One possible layout is, using 32 * (17 + 9) bytes: -// -// .+------ <- only 1 pixel high -// .|yyyyt. -// .|yyyyt. -// .|yyyyt. -// .|yyyy.. -// .+--.+-- <- only 1 pixel high -// .|uu.|vv -// .|uu.|vv -// -// Every character is a 4x4 block, with legend: -// '.' = unused -// 'y' = y-samples 'u' = u-samples 'v' = u-samples -// '|' = left sample, '-' = top sample, '+' = top-left sample -// 't' = extra top-right sample for 4x4 modes -#define YUV_SIZE (BPS * 17 + BPS * 9) -#define Y_SIZE (BPS * 17) -#define Y_OFF (BPS * 1 + 8) -#define U_OFF (Y_OFF + BPS * 16 + BPS) -#define V_OFF (U_OFF + 16) - -// minimal width under which lossy multi-threading is always disabled -#define MIN_WIDTH_FOR_THREADS 512 - -//------------------------------------------------------------------------------ -// Headers - -typedef struct { - uint8_t key_frame_; - uint8_t profile_; - uint8_t show_; - uint32_t partition_length_; -} VP8FrameHeader; - -typedef struct { - uint16_t width_; - uint16_t height_; - uint8_t xscale_; - uint8_t yscale_; - uint8_t colorspace_; // 0 = YCbCr - uint8_t clamp_type_; -} VP8PictureHeader; - -// segment features -typedef struct { - int use_segment_; - int update_map_; // whether to update the segment map or not - int absolute_delta_; // absolute or delta values for quantizer and filter - int8_t quantizer_[NUM_MB_SEGMENTS]; // quantization changes - int8_t filter_strength_[NUM_MB_SEGMENTS]; // filter strength for segments -} VP8SegmentHeader; - -// probas associated to one of the contexts -typedef uint8_t VP8ProbaArray[NUM_PROBAS]; - -typedef struct { // all the probas associated to one band - VP8ProbaArray probas_[NUM_CTX]; -} VP8BandProbas; - -// Struct collecting all frame-persistent probabilities. -typedef struct { - uint8_t segments_[MB_FEATURE_TREE_PROBS]; - // Type: 0:Intra16-AC 1:Intra16-DC 2:Chroma 3:Intra4 - VP8BandProbas bands_[NUM_TYPES][NUM_BANDS]; - const VP8BandProbas* bands_ptr_[NUM_TYPES][16 + 1]; -} VP8Proba; - -// Filter parameters -typedef struct { - int simple_; // 0=complex, 1=simple - int level_; // [0..63] - int sharpness_; // [0..7] - int use_lf_delta_; - int ref_lf_delta_[NUM_REF_LF_DELTAS]; - int mode_lf_delta_[NUM_MODE_LF_DELTAS]; -} VP8FilterHeader; - -//------------------------------------------------------------------------------ -// Informations about the macroblocks. - -typedef struct { // filter specs - uint8_t f_limit_; // filter limit in [3..189], or 0 if no filtering - uint8_t f_ilevel_; // inner limit in [1..63] - uint8_t f_inner_; // do inner filtering? - uint8_t hev_thresh_; // high edge variance threshold in [0..2] -} VP8FInfo; - -typedef struct { // Top/Left Contexts used for syntax-parsing - uint8_t nz_; // non-zero AC/DC coeffs (4bit for luma + 4bit for chroma) - uint8_t nz_dc_; // non-zero DC coeff (1bit) -} VP8MB; - -// Dequantization matrices -typedef int quant_t[2]; // [DC / AC]. Can be 'uint16_t[2]' too (~slower). -typedef struct { - quant_t y1_mat_, y2_mat_, uv_mat_; - - int uv_quant_; // U/V quantizer value - int dither_; // dithering amplitude (0 = off, max=255) -} VP8QuantMatrix; - -// Data needed to reconstruct a macroblock -typedef struct { - int16_t coeffs_[384]; // 384 coeffs = (16+4+4) * 4*4 - uint8_t is_i4x4_; // true if intra4x4 - uint8_t imodes_[16]; // one 16x16 mode (#0) or sixteen 4x4 modes - uint8_t uvmode_; // chroma prediction mode - // bit-wise info about the content of each sub-4x4 blocks (in decoding order). - // Each of the 4x4 blocks for y/u/v is associated with a 2b code according to: - // code=0 -> no coefficient - // code=1 -> only DC - // code=2 -> first three coefficients are non-zero - // code=3 -> more than three coefficients are non-zero - // This allows to call specialized transform functions. - uint32_t non_zero_y_; - uint32_t non_zero_uv_; - uint8_t dither_; // local dithering strength (deduced from non_zero_*) - uint8_t skip_; - uint8_t segment_; -} VP8MBData; - -// Persistent information needed by the parallel processing -typedef struct { - int id_; // cache row to process (in [0..2]) - int mb_y_; // macroblock position of the row - int filter_row_; // true if row-filtering is needed - VP8FInfo* f_info_; // filter strengths (swapped with dec->f_info_) - VP8MBData* mb_data_; // reconstruction data (swapped with dec->mb_data_) - VP8Io io_; // copy of the VP8Io to pass to put() -} VP8ThreadContext; - -// Saved top samples, per macroblock. Fits into a cache-line. -typedef struct { - uint8_t y[16], u[8], v[8]; -} VP8TopSamples; - -//------------------------------------------------------------------------------ -// VP8Decoder: the main opaque structure handed over to user - -struct VP8Decoder { - VP8StatusCode status_; - int ready_; // true if ready to decode a picture with VP8Decode() - const char* error_msg_; // set when status_ is not OK. - - // Main data source - VP8BitReader br_; - - // headers - VP8FrameHeader frm_hdr_; - VP8PictureHeader pic_hdr_; - VP8FilterHeader filter_hdr_; - VP8SegmentHeader segment_hdr_; - - // Worker - WebPWorker worker_; - int mt_method_; // multi-thread method: 0=off, 1=[parse+recon][filter] - // 2=[parse][recon+filter] - int cache_id_; // current cache row - int num_caches_; // number of cached rows of 16 pixels (1, 2 or 3) - VP8ThreadContext thread_ctx_; // Thread context - - // dimension, in macroblock units. - int mb_w_, mb_h_; - - // Macroblock to process/filter, depending on cropping and filter_type. - int tl_mb_x_, tl_mb_y_; // top-left MB that must be in-loop filtered - int br_mb_x_, br_mb_y_; // last bottom-right MB that must be decoded - - // number of partitions minus one. - uint32_t num_parts_minus_one_; - // per-partition boolean decoders. - VP8BitReader parts_[MAX_NUM_PARTITIONS]; - - // Dithering strength, deduced from decoding options - int dither_; // whether to use dithering or not - VP8Random dithering_rg_; // random generator for dithering - - // dequantization (one set of DC/AC dequant factor per segment) - VP8QuantMatrix dqm_[NUM_MB_SEGMENTS]; - - // probabilities - VP8Proba proba_; - int use_skip_proba_; - uint8_t skip_p_; - - // Boundary data cache and persistent buffers. - uint8_t* intra_t_; // top intra modes values: 4 * mb_w_ - uint8_t intra_l_[4]; // left intra modes values - - VP8TopSamples* yuv_t_; // top y/u/v samples - - VP8MB* mb_info_; // contextual macroblock info (mb_w_ + 1) - VP8FInfo* f_info_; // filter strength info - uint8_t* yuv_b_; // main block for Y/U/V (size = YUV_SIZE) - - uint8_t* cache_y_; // macroblock row for storing unfiltered samples - uint8_t* cache_u_; - uint8_t* cache_v_; - int cache_y_stride_; - int cache_uv_stride_; - - // main memory chunk for the above data. Persistent. - void* mem_; - size_t mem_size_; - - // Per macroblock non-persistent infos. - int mb_x_, mb_y_; // current position, in macroblock units - VP8MBData* mb_data_; // parsed reconstruction data - - // Filtering side-info - int filter_type_; // 0=off, 1=simple, 2=complex - VP8FInfo fstrengths_[NUM_MB_SEGMENTS][2]; // precalculated per-segment/type - - // Alpha - struct ALPHDecoder* alph_dec_; // alpha-plane decoder object - const uint8_t* alpha_data_; // compressed alpha data (if present) - size_t alpha_data_size_; - int is_alpha_decoded_; // true if alpha_data_ is decoded in alpha_plane_ - uint8_t* alpha_plane_mem_; // memory allocated for alpha_plane_ - uint8_t* alpha_plane_; // output. Persistent, contains the whole data. - const uint8_t* alpha_prev_line_; // last decoded alpha row (or NULL) - int alpha_dithering_; // derived from decoding options (0=off, 100=full) -}; - -//------------------------------------------------------------------------------ -// internal functions. Not public. - -// in vp8.c -int VP8SetError(VP8Decoder* const dec, - VP8StatusCode error, const char* const msg); - -// in tree.c -void VP8ResetProba(VP8Proba* const proba); -void VP8ParseProba(VP8BitReader* const br, VP8Decoder* const dec); -// parses one row of intra mode data in partition 0, returns !eof -int VP8ParseIntraModeRow(VP8BitReader* const br, VP8Decoder* const dec); - -// in quant.c -void VP8ParseQuant(VP8Decoder* const dec); - -// in frame.c -int VP8InitFrame(VP8Decoder* const dec, VP8Io* const io); -// Call io->setup() and finish setting up scan parameters. -// After this call returns, one must always call VP8ExitCritical() with the -// same parameters. Both functions should be used in pair. Returns VP8_STATUS_OK -// if ok, otherwise sets and returns the error status on *dec. -VP8StatusCode VP8EnterCritical(VP8Decoder* const dec, VP8Io* const io); -// Must always be called in pair with VP8EnterCritical(). -// Returns false in case of error. -int VP8ExitCritical(VP8Decoder* const dec, VP8Io* const io); -// Return the multi-threading method to use (0=off), depending -// on options and bitstream size. Only for lossy decoding. -int VP8GetThreadMethod(const WebPDecoderOptions* const options, - const WebPHeaderStructure* const headers, - int width, int height); -// Initialize dithering post-process if needed. -void VP8InitDithering(const WebPDecoderOptions* const options, - VP8Decoder* const dec); -// Process the last decoded row (filtering + output). -int VP8ProcessRow(VP8Decoder* const dec, VP8Io* const io); -// To be called at the start of a new scanline, to initialize predictors. -void VP8InitScanline(VP8Decoder* const dec); -// Decode one macroblock. Returns false if there is not enough data. -int VP8DecodeMB(VP8Decoder* const dec, VP8BitReader* const token_br); - -// in alpha.c -const uint8_t* VP8DecompressAlphaRows(VP8Decoder* const dec, - const VP8Io* const io, - int row, int num_rows); - -//------------------------------------------------------------------------------ - -#ifdef __cplusplus -} // extern "C" -#endif - -#endif /* WEBP_DEC_VP8I_H_ */ diff --git a/thirdparty/libwebp/dec/vp8i_dec.h b/thirdparty/libwebp/dec/vp8i_dec.h new file mode 100644 index 0000000000..555853e8f8 --- /dev/null +++ b/thirdparty/libwebp/dec/vp8i_dec.h @@ -0,0 +1,320 @@ +// Copyright 2010 Google Inc. All Rights Reserved. +// +// Use of this source code is governed by a BSD-style license +// that can be found in the COPYING file in the root of the source +// tree. An additional intellectual property rights grant can be found +// in the file PATENTS. All contributing project authors may +// be found in the AUTHORS file in the root of the source tree. +// ----------------------------------------------------------------------------- +// +// VP8 decoder: internal header. +// +// Author: Skal (pascal.massimino@gmail.com) + +#ifndef WEBP_DEC_VP8I_H_ +#define WEBP_DEC_VP8I_H_ + +#include // for memcpy() +#include "./common_dec.h" +#include "./vp8li_dec.h" +#include "../utils/bit_reader_utils.h" +#include "../utils/random_utils.h" +#include "../utils/thread_utils.h" +#include "../dsp/dsp.h" + +#ifdef __cplusplus +extern "C" { +#endif + +//------------------------------------------------------------------------------ +// Various defines and enums + +// version numbers +#define DEC_MAJ_VERSION 0 +#define DEC_MIN_VERSION 6 +#define DEC_REV_VERSION 0 + +// YUV-cache parameters. Cache is 32-bytes wide (= one cacheline). +// Constraints are: We need to store one 16x16 block of luma samples (y), +// and two 8x8 chroma blocks (u/v). These are better be 16-bytes aligned, +// in order to be SIMD-friendly. We also need to store the top, left and +// top-left samples (from previously decoded blocks), along with four +// extra top-right samples for luma (intra4x4 prediction only). +// One possible layout is, using 32 * (17 + 9) bytes: +// +// .+------ <- only 1 pixel high +// .|yyyyt. +// .|yyyyt. +// .|yyyyt. +// .|yyyy.. +// .+--.+-- <- only 1 pixel high +// .|uu.|vv +// .|uu.|vv +// +// Every character is a 4x4 block, with legend: +// '.' = unused +// 'y' = y-samples 'u' = u-samples 'v' = u-samples +// '|' = left sample, '-' = top sample, '+' = top-left sample +// 't' = extra top-right sample for 4x4 modes +#define YUV_SIZE (BPS * 17 + BPS * 9) +#define Y_SIZE (BPS * 17) +#define Y_OFF (BPS * 1 + 8) +#define U_OFF (Y_OFF + BPS * 16 + BPS) +#define V_OFF (U_OFF + 16) + +// minimal width under which lossy multi-threading is always disabled +#define MIN_WIDTH_FOR_THREADS 512 + +//------------------------------------------------------------------------------ +// Headers + +typedef struct { + uint8_t key_frame_; + uint8_t profile_; + uint8_t show_; + uint32_t partition_length_; +} VP8FrameHeader; + +typedef struct { + uint16_t width_; + uint16_t height_; + uint8_t xscale_; + uint8_t yscale_; + uint8_t colorspace_; // 0 = YCbCr + uint8_t clamp_type_; +} VP8PictureHeader; + +// segment features +typedef struct { + int use_segment_; + int update_map_; // whether to update the segment map or not + int absolute_delta_; // absolute or delta values for quantizer and filter + int8_t quantizer_[NUM_MB_SEGMENTS]; // quantization changes + int8_t filter_strength_[NUM_MB_SEGMENTS]; // filter strength for segments +} VP8SegmentHeader; + +// probas associated to one of the contexts +typedef uint8_t VP8ProbaArray[NUM_PROBAS]; + +typedef struct { // all the probas associated to one band + VP8ProbaArray probas_[NUM_CTX]; +} VP8BandProbas; + +// Struct collecting all frame-persistent probabilities. +typedef struct { + uint8_t segments_[MB_FEATURE_TREE_PROBS]; + // Type: 0:Intra16-AC 1:Intra16-DC 2:Chroma 3:Intra4 + VP8BandProbas bands_[NUM_TYPES][NUM_BANDS]; + const VP8BandProbas* bands_ptr_[NUM_TYPES][16 + 1]; +} VP8Proba; + +// Filter parameters +typedef struct { + int simple_; // 0=complex, 1=simple + int level_; // [0..63] + int sharpness_; // [0..7] + int use_lf_delta_; + int ref_lf_delta_[NUM_REF_LF_DELTAS]; + int mode_lf_delta_[NUM_MODE_LF_DELTAS]; +} VP8FilterHeader; + +//------------------------------------------------------------------------------ +// Informations about the macroblocks. + +typedef struct { // filter specs + uint8_t f_limit_; // filter limit in [3..189], or 0 if no filtering + uint8_t f_ilevel_; // inner limit in [1..63] + uint8_t f_inner_; // do inner filtering? + uint8_t hev_thresh_; // high edge variance threshold in [0..2] +} VP8FInfo; + +typedef struct { // Top/Left Contexts used for syntax-parsing + uint8_t nz_; // non-zero AC/DC coeffs (4bit for luma + 4bit for chroma) + uint8_t nz_dc_; // non-zero DC coeff (1bit) +} VP8MB; + +// Dequantization matrices +typedef int quant_t[2]; // [DC / AC]. Can be 'uint16_t[2]' too (~slower). +typedef struct { + quant_t y1_mat_, y2_mat_, uv_mat_; + + int uv_quant_; // U/V quantizer value + int dither_; // dithering amplitude (0 = off, max=255) +} VP8QuantMatrix; + +// Data needed to reconstruct a macroblock +typedef struct { + int16_t coeffs_[384]; // 384 coeffs = (16+4+4) * 4*4 + uint8_t is_i4x4_; // true if intra4x4 + uint8_t imodes_[16]; // one 16x16 mode (#0) or sixteen 4x4 modes + uint8_t uvmode_; // chroma prediction mode + // bit-wise info about the content of each sub-4x4 blocks (in decoding order). + // Each of the 4x4 blocks for y/u/v is associated with a 2b code according to: + // code=0 -> no coefficient + // code=1 -> only DC + // code=2 -> first three coefficients are non-zero + // code=3 -> more than three coefficients are non-zero + // This allows to call specialized transform functions. + uint32_t non_zero_y_; + uint32_t non_zero_uv_; + uint8_t dither_; // local dithering strength (deduced from non_zero_*) + uint8_t skip_; + uint8_t segment_; +} VP8MBData; + +// Persistent information needed by the parallel processing +typedef struct { + int id_; // cache row to process (in [0..2]) + int mb_y_; // macroblock position of the row + int filter_row_; // true if row-filtering is needed + VP8FInfo* f_info_; // filter strengths (swapped with dec->f_info_) + VP8MBData* mb_data_; // reconstruction data (swapped with dec->mb_data_) + VP8Io io_; // copy of the VP8Io to pass to put() +} VP8ThreadContext; + +// Saved top samples, per macroblock. Fits into a cache-line. +typedef struct { + uint8_t y[16], u[8], v[8]; +} VP8TopSamples; + +//------------------------------------------------------------------------------ +// VP8Decoder: the main opaque structure handed over to user + +struct VP8Decoder { + VP8StatusCode status_; + int ready_; // true if ready to decode a picture with VP8Decode() + const char* error_msg_; // set when status_ is not OK. + + // Main data source + VP8BitReader br_; + + // headers + VP8FrameHeader frm_hdr_; + VP8PictureHeader pic_hdr_; + VP8FilterHeader filter_hdr_; + VP8SegmentHeader segment_hdr_; + + // Worker + WebPWorker worker_; + int mt_method_; // multi-thread method: 0=off, 1=[parse+recon][filter] + // 2=[parse][recon+filter] + int cache_id_; // current cache row + int num_caches_; // number of cached rows of 16 pixels (1, 2 or 3) + VP8ThreadContext thread_ctx_; // Thread context + + // dimension, in macroblock units. + int mb_w_, mb_h_; + + // Macroblock to process/filter, depending on cropping and filter_type. + int tl_mb_x_, tl_mb_y_; // top-left MB that must be in-loop filtered + int br_mb_x_, br_mb_y_; // last bottom-right MB that must be decoded + + // number of partitions minus one. + uint32_t num_parts_minus_one_; + // per-partition boolean decoders. + VP8BitReader parts_[MAX_NUM_PARTITIONS]; + + // Dithering strength, deduced from decoding options + int dither_; // whether to use dithering or not + VP8Random dithering_rg_; // random generator for dithering + + // dequantization (one set of DC/AC dequant factor per segment) + VP8QuantMatrix dqm_[NUM_MB_SEGMENTS]; + + // probabilities + VP8Proba proba_; + int use_skip_proba_; + uint8_t skip_p_; + + // Boundary data cache and persistent buffers. + uint8_t* intra_t_; // top intra modes values: 4 * mb_w_ + uint8_t intra_l_[4]; // left intra modes values + + VP8TopSamples* yuv_t_; // top y/u/v samples + + VP8MB* mb_info_; // contextual macroblock info (mb_w_ + 1) + VP8FInfo* f_info_; // filter strength info + uint8_t* yuv_b_; // main block for Y/U/V (size = YUV_SIZE) + + uint8_t* cache_y_; // macroblock row for storing unfiltered samples + uint8_t* cache_u_; + uint8_t* cache_v_; + int cache_y_stride_; + int cache_uv_stride_; + + // main memory chunk for the above data. Persistent. + void* mem_; + size_t mem_size_; + + // Per macroblock non-persistent infos. + int mb_x_, mb_y_; // current position, in macroblock units + VP8MBData* mb_data_; // parsed reconstruction data + + // Filtering side-info + int filter_type_; // 0=off, 1=simple, 2=complex + VP8FInfo fstrengths_[NUM_MB_SEGMENTS][2]; // precalculated per-segment/type + + // Alpha + struct ALPHDecoder* alph_dec_; // alpha-plane decoder object + const uint8_t* alpha_data_; // compressed alpha data (if present) + size_t alpha_data_size_; + int is_alpha_decoded_; // true if alpha_data_ is decoded in alpha_plane_ + uint8_t* alpha_plane_mem_; // memory allocated for alpha_plane_ + uint8_t* alpha_plane_; // output. Persistent, contains the whole data. + const uint8_t* alpha_prev_line_; // last decoded alpha row (or NULL) + int alpha_dithering_; // derived from decoding options (0=off, 100=full) +}; + +//------------------------------------------------------------------------------ +// internal functions. Not public. + +// in vp8.c +int VP8SetError(VP8Decoder* const dec, + VP8StatusCode error, const char* const msg); + +// in tree.c +void VP8ResetProba(VP8Proba* const proba); +void VP8ParseProba(VP8BitReader* const br, VP8Decoder* const dec); +// parses one row of intra mode data in partition 0, returns !eof +int VP8ParseIntraModeRow(VP8BitReader* const br, VP8Decoder* const dec); + +// in quant.c +void VP8ParseQuant(VP8Decoder* const dec); + +// in frame.c +int VP8InitFrame(VP8Decoder* const dec, VP8Io* const io); +// Call io->setup() and finish setting up scan parameters. +// After this call returns, one must always call VP8ExitCritical() with the +// same parameters. Both functions should be used in pair. Returns VP8_STATUS_OK +// if ok, otherwise sets and returns the error status on *dec. +VP8StatusCode VP8EnterCritical(VP8Decoder* const dec, VP8Io* const io); +// Must always be called in pair with VP8EnterCritical(). +// Returns false in case of error. +int VP8ExitCritical(VP8Decoder* const dec, VP8Io* const io); +// Return the multi-threading method to use (0=off), depending +// on options and bitstream size. Only for lossy decoding. +int VP8GetThreadMethod(const WebPDecoderOptions* const options, + const WebPHeaderStructure* const headers, + int width, int height); +// Initialize dithering post-process if needed. +void VP8InitDithering(const WebPDecoderOptions* const options, + VP8Decoder* const dec); +// Process the last decoded row (filtering + output). +int VP8ProcessRow(VP8Decoder* const dec, VP8Io* const io); +// To be called at the start of a new scanline, to initialize predictors. +void VP8InitScanline(VP8Decoder* const dec); +// Decode one macroblock. Returns false if there is not enough data. +int VP8DecodeMB(VP8Decoder* const dec, VP8BitReader* const token_br); + +// in alpha.c +const uint8_t* VP8DecompressAlphaRows(VP8Decoder* const dec, + const VP8Io* const io, + int row, int num_rows); + +//------------------------------------------------------------------------------ + +#ifdef __cplusplus +} // extern "C" +#endif + +#endif /* WEBP_DEC_VP8I_H_ */ diff --git a/thirdparty/libwebp/dec/vp8l.c b/thirdparty/libwebp/dec/vp8l.c deleted file mode 100644 index cb2e3176b6..0000000000 --- a/thirdparty/libwebp/dec/vp8l.c +++ /dev/null @@ -1,1660 +0,0 @@ -// Copyright 2012 Google Inc. All Rights Reserved. -// -// Use of this source code is governed by a BSD-style license -// that can be found in the COPYING file in the root of the source -// tree. An additional intellectual property rights grant can be found -// in the file PATENTS. All contributing project authors may -// be found in the AUTHORS file in the root of the source tree. -// ----------------------------------------------------------------------------- -// -// main entry for the decoder -// -// Authors: Vikas Arora (vikaas.arora@gmail.com) -// Jyrki Alakuijala (jyrki@google.com) - -#include - -#include "./alphai.h" -#include "./vp8li.h" -#include "../dsp/dsp.h" -#include "../dsp/lossless.h" -#include "../dsp/yuv.h" -#include "../utils/endian_inl.h" -#include "../utils/huffman.h" -#include "../utils/utils.h" - -#define NUM_ARGB_CACHE_ROWS 16 - -static const int kCodeLengthLiterals = 16; -static const int kCodeLengthRepeatCode = 16; -static const int kCodeLengthExtraBits[3] = { 2, 3, 7 }; -static const int kCodeLengthRepeatOffsets[3] = { 3, 3, 11 }; - -// ----------------------------------------------------------------------------- -// Five Huffman codes are used at each meta code: -// 1. green + length prefix codes + color cache codes, -// 2. alpha, -// 3. red, -// 4. blue, and, -// 5. distance prefix codes. -typedef enum { - GREEN = 0, - RED = 1, - BLUE = 2, - ALPHA = 3, - DIST = 4 -} HuffIndex; - -static const uint16_t kAlphabetSize[HUFFMAN_CODES_PER_META_CODE] = { - NUM_LITERAL_CODES + NUM_LENGTH_CODES, - NUM_LITERAL_CODES, NUM_LITERAL_CODES, NUM_LITERAL_CODES, - NUM_DISTANCE_CODES -}; - -static const uint8_t kLiteralMap[HUFFMAN_CODES_PER_META_CODE] = { - 0, 1, 1, 1, 0 -}; - -#define NUM_CODE_LENGTH_CODES 19 -static const uint8_t kCodeLengthCodeOrder[NUM_CODE_LENGTH_CODES] = { - 17, 18, 0, 1, 2, 3, 4, 5, 16, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 -}; - -#define CODE_TO_PLANE_CODES 120 -static const uint8_t kCodeToPlane[CODE_TO_PLANE_CODES] = { - 0x18, 0x07, 0x17, 0x19, 0x28, 0x06, 0x27, 0x29, 0x16, 0x1a, - 0x26, 0x2a, 0x38, 0x05, 0x37, 0x39, 0x15, 0x1b, 0x36, 0x3a, - 0x25, 0x2b, 0x48, 0x04, 0x47, 0x49, 0x14, 0x1c, 0x35, 0x3b, - 0x46, 0x4a, 0x24, 0x2c, 0x58, 0x45, 0x4b, 0x34, 0x3c, 0x03, - 0x57, 0x59, 0x13, 0x1d, 0x56, 0x5a, 0x23, 0x2d, 0x44, 0x4c, - 0x55, 0x5b, 0x33, 0x3d, 0x68, 0x02, 0x67, 0x69, 0x12, 0x1e, - 0x66, 0x6a, 0x22, 0x2e, 0x54, 0x5c, 0x43, 0x4d, 0x65, 0x6b, - 0x32, 0x3e, 0x78, 0x01, 0x77, 0x79, 0x53, 0x5d, 0x11, 0x1f, - 0x64, 0x6c, 0x42, 0x4e, 0x76, 0x7a, 0x21, 0x2f, 0x75, 0x7b, - 0x31, 0x3f, 0x63, 0x6d, 0x52, 0x5e, 0x00, 0x74, 0x7c, 0x41, - 0x4f, 0x10, 0x20, 0x62, 0x6e, 0x30, 0x73, 0x7d, 0x51, 0x5f, - 0x40, 0x72, 0x7e, 0x61, 0x6f, 0x50, 0x71, 0x7f, 0x60, 0x70 -}; - -// Memory needed for lookup tables of one Huffman tree group. Red, blue, alpha -// and distance alphabets are constant (256 for red, blue and alpha, 40 for -// distance) and lookup table sizes for them in worst case are 630 and 410 -// respectively. Size of green alphabet depends on color cache size and is equal -// to 256 (green component values) + 24 (length prefix values) -// + color_cache_size (between 0 and 2048). -// All values computed for 8-bit first level lookup with Mark Adler's tool: -// http://www.hdfgroup.org/ftp/lib-external/zlib/zlib-1.2.5/examples/enough.c -#define FIXED_TABLE_SIZE (630 * 3 + 410) -static const int kTableSize[12] = { - FIXED_TABLE_SIZE + 654, - FIXED_TABLE_SIZE + 656, - FIXED_TABLE_SIZE + 658, - FIXED_TABLE_SIZE + 662, - FIXED_TABLE_SIZE + 670, - FIXED_TABLE_SIZE + 686, - FIXED_TABLE_SIZE + 718, - FIXED_TABLE_SIZE + 782, - FIXED_TABLE_SIZE + 912, - FIXED_TABLE_SIZE + 1168, - FIXED_TABLE_SIZE + 1680, - FIXED_TABLE_SIZE + 2704 -}; - -static int DecodeImageStream(int xsize, int ysize, - int is_level0, - VP8LDecoder* const dec, - uint32_t** const decoded_data); - -//------------------------------------------------------------------------------ - -int VP8LCheckSignature(const uint8_t* const data, size_t size) { - return (size >= VP8L_FRAME_HEADER_SIZE && - data[0] == VP8L_MAGIC_BYTE && - (data[4] >> 5) == 0); // version -} - -static int ReadImageInfo(VP8LBitReader* const br, - int* const width, int* const height, - int* const has_alpha) { - if (VP8LReadBits(br, 8) != VP8L_MAGIC_BYTE) return 0; - *width = VP8LReadBits(br, VP8L_IMAGE_SIZE_BITS) + 1; - *height = VP8LReadBits(br, VP8L_IMAGE_SIZE_BITS) + 1; - *has_alpha = VP8LReadBits(br, 1); - if (VP8LReadBits(br, VP8L_VERSION_BITS) != 0) return 0; - return !br->eos_; -} - -int VP8LGetInfo(const uint8_t* data, size_t data_size, - int* const width, int* const height, int* const has_alpha) { - if (data == NULL || data_size < VP8L_FRAME_HEADER_SIZE) { - return 0; // not enough data - } else if (!VP8LCheckSignature(data, data_size)) { - return 0; // bad signature - } else { - int w, h, a; - VP8LBitReader br; - VP8LInitBitReader(&br, data, data_size); - if (!ReadImageInfo(&br, &w, &h, &a)) { - return 0; - } - if (width != NULL) *width = w; - if (height != NULL) *height = h; - if (has_alpha != NULL) *has_alpha = a; - return 1; - } -} - -//------------------------------------------------------------------------------ - -static WEBP_INLINE int GetCopyDistance(int distance_symbol, - VP8LBitReader* const br) { - int extra_bits, offset; - if (distance_symbol < 4) { - return distance_symbol + 1; - } - extra_bits = (distance_symbol - 2) >> 1; - offset = (2 + (distance_symbol & 1)) << extra_bits; - return offset + VP8LReadBits(br, extra_bits) + 1; -} - -static WEBP_INLINE int GetCopyLength(int length_symbol, - VP8LBitReader* const br) { - // Length and distance prefixes are encoded the same way. - return GetCopyDistance(length_symbol, br); -} - -static WEBP_INLINE int PlaneCodeToDistance(int xsize, int plane_code) { - if (plane_code > CODE_TO_PLANE_CODES) { - return plane_code - CODE_TO_PLANE_CODES; - } else { - const int dist_code = kCodeToPlane[plane_code - 1]; - const int yoffset = dist_code >> 4; - const int xoffset = 8 - (dist_code & 0xf); - const int dist = yoffset * xsize + xoffset; - return (dist >= 1) ? dist : 1; // dist<1 can happen if xsize is very small - } -} - -//------------------------------------------------------------------------------ -// Decodes the next Huffman code from bit-stream. -// FillBitWindow(br) needs to be called at minimum every second call -// to ReadSymbol, in order to pre-fetch enough bits. -static WEBP_INLINE int ReadSymbol(const HuffmanCode* table, - VP8LBitReader* const br) { - int nbits; - uint32_t val = VP8LPrefetchBits(br); - table += val & HUFFMAN_TABLE_MASK; - nbits = table->bits - HUFFMAN_TABLE_BITS; - if (nbits > 0) { - VP8LSetBitPos(br, br->bit_pos_ + HUFFMAN_TABLE_BITS); - val = VP8LPrefetchBits(br); - table += table->value; - table += val & ((1 << nbits) - 1); - } - VP8LSetBitPos(br, br->bit_pos_ + table->bits); - return table->value; -} - -// Reads packed symbol depending on GREEN channel -#define BITS_SPECIAL_MARKER 0x100 // something large enough (and a bit-mask) -#define PACKED_NON_LITERAL_CODE 0 // must be < NUM_LITERAL_CODES -static WEBP_INLINE int ReadPackedSymbols(const HTreeGroup* group, - VP8LBitReader* const br, - uint32_t* const dst) { - const uint32_t val = VP8LPrefetchBits(br) & (HUFFMAN_PACKED_TABLE_SIZE - 1); - const HuffmanCode32 code = group->packed_table[val]; - assert(group->use_packed_table); - if (code.bits < BITS_SPECIAL_MARKER) { - VP8LSetBitPos(br, br->bit_pos_ + code.bits); - *dst = code.value; - return PACKED_NON_LITERAL_CODE; - } else { - VP8LSetBitPos(br, br->bit_pos_ + code.bits - BITS_SPECIAL_MARKER); - assert(code.value >= NUM_LITERAL_CODES); - return code.value; - } -} - -static int AccumulateHCode(HuffmanCode hcode, int shift, - HuffmanCode32* const huff) { - huff->bits += hcode.bits; - huff->value |= (uint32_t)hcode.value << shift; - assert(huff->bits <= HUFFMAN_TABLE_BITS); - return hcode.bits; -} - -static void BuildPackedTable(HTreeGroup* const htree_group) { - uint32_t code; - for (code = 0; code < HUFFMAN_PACKED_TABLE_SIZE; ++code) { - uint32_t bits = code; - HuffmanCode32* const huff = &htree_group->packed_table[bits]; - HuffmanCode hcode = htree_group->htrees[GREEN][bits]; - if (hcode.value >= NUM_LITERAL_CODES) { - huff->bits = hcode.bits + BITS_SPECIAL_MARKER; - huff->value = hcode.value; - } else { - huff->bits = 0; - huff->value = 0; - bits >>= AccumulateHCode(hcode, 8, huff); - bits >>= AccumulateHCode(htree_group->htrees[RED][bits], 16, huff); - bits >>= AccumulateHCode(htree_group->htrees[BLUE][bits], 0, huff); - bits >>= AccumulateHCode(htree_group->htrees[ALPHA][bits], 24, huff); - (void)bits; - } - } -} - -static int ReadHuffmanCodeLengths( - VP8LDecoder* const dec, const int* const code_length_code_lengths, - int num_symbols, int* const code_lengths) { - int ok = 0; - VP8LBitReader* const br = &dec->br_; - int symbol; - int max_symbol; - int prev_code_len = DEFAULT_CODE_LENGTH; - HuffmanCode table[1 << LENGTHS_TABLE_BITS]; - - if (!VP8LBuildHuffmanTable(table, LENGTHS_TABLE_BITS, - code_length_code_lengths, - NUM_CODE_LENGTH_CODES)) { - goto End; - } - - if (VP8LReadBits(br, 1)) { // use length - const int length_nbits = 2 + 2 * VP8LReadBits(br, 3); - max_symbol = 2 + VP8LReadBits(br, length_nbits); - if (max_symbol > num_symbols) { - goto End; - } - } else { - max_symbol = num_symbols; - } - - symbol = 0; - while (symbol < num_symbols) { - const HuffmanCode* p; - int code_len; - if (max_symbol-- == 0) break; - VP8LFillBitWindow(br); - p = &table[VP8LPrefetchBits(br) & LENGTHS_TABLE_MASK]; - VP8LSetBitPos(br, br->bit_pos_ + p->bits); - code_len = p->value; - if (code_len < kCodeLengthLiterals) { - code_lengths[symbol++] = code_len; - if (code_len != 0) prev_code_len = code_len; - } else { - const int use_prev = (code_len == kCodeLengthRepeatCode); - const int slot = code_len - kCodeLengthLiterals; - const int extra_bits = kCodeLengthExtraBits[slot]; - const int repeat_offset = kCodeLengthRepeatOffsets[slot]; - int repeat = VP8LReadBits(br, extra_bits) + repeat_offset; - if (symbol + repeat > num_symbols) { - goto End; - } else { - const int length = use_prev ? prev_code_len : 0; - while (repeat-- > 0) code_lengths[symbol++] = length; - } - } - } - ok = 1; - - End: - if (!ok) dec->status_ = VP8_STATUS_BITSTREAM_ERROR; - return ok; -} - -// 'code_lengths' is pre-allocated temporary buffer, used for creating Huffman -// tree. -static int ReadHuffmanCode(int alphabet_size, VP8LDecoder* const dec, - int* const code_lengths, HuffmanCode* const table) { - int ok = 0; - int size = 0; - VP8LBitReader* const br = &dec->br_; - const int simple_code = VP8LReadBits(br, 1); - - memset(code_lengths, 0, alphabet_size * sizeof(*code_lengths)); - - if (simple_code) { // Read symbols, codes & code lengths directly. - const int num_symbols = VP8LReadBits(br, 1) + 1; - const int first_symbol_len_code = VP8LReadBits(br, 1); - // The first code is either 1 bit or 8 bit code. - int symbol = VP8LReadBits(br, (first_symbol_len_code == 0) ? 1 : 8); - code_lengths[symbol] = 1; - // The second code (if present), is always 8 bit long. - if (num_symbols == 2) { - symbol = VP8LReadBits(br, 8); - code_lengths[symbol] = 1; - } - ok = 1; - } else { // Decode Huffman-coded code lengths. - int i; - int code_length_code_lengths[NUM_CODE_LENGTH_CODES] = { 0 }; - const int num_codes = VP8LReadBits(br, 4) + 4; - if (num_codes > NUM_CODE_LENGTH_CODES) { - dec->status_ = VP8_STATUS_BITSTREAM_ERROR; - return 0; - } - - for (i = 0; i < num_codes; ++i) { - code_length_code_lengths[kCodeLengthCodeOrder[i]] = VP8LReadBits(br, 3); - } - ok = ReadHuffmanCodeLengths(dec, code_length_code_lengths, alphabet_size, - code_lengths); - } - - ok = ok && !br->eos_; - if (ok) { - size = VP8LBuildHuffmanTable(table, HUFFMAN_TABLE_BITS, - code_lengths, alphabet_size); - } - if (!ok || size == 0) { - dec->status_ = VP8_STATUS_BITSTREAM_ERROR; - return 0; - } - return size; -} - -static int ReadHuffmanCodes(VP8LDecoder* const dec, int xsize, int ysize, - int color_cache_bits, int allow_recursion) { - int i, j; - VP8LBitReader* const br = &dec->br_; - VP8LMetadata* const hdr = &dec->hdr_; - uint32_t* huffman_image = NULL; - HTreeGroup* htree_groups = NULL; - HuffmanCode* huffman_tables = NULL; - HuffmanCode* next = NULL; - int num_htree_groups = 1; - int max_alphabet_size = 0; - int* code_lengths = NULL; - const int table_size = kTableSize[color_cache_bits]; - - if (allow_recursion && VP8LReadBits(br, 1)) { - // use meta Huffman codes. - const int huffman_precision = VP8LReadBits(br, 3) + 2; - const int huffman_xsize = VP8LSubSampleSize(xsize, huffman_precision); - const int huffman_ysize = VP8LSubSampleSize(ysize, huffman_precision); - const int huffman_pixs = huffman_xsize * huffman_ysize; - if (!DecodeImageStream(huffman_xsize, huffman_ysize, 0, dec, - &huffman_image)) { - goto Error; - } - hdr->huffman_subsample_bits_ = huffman_precision; - for (i = 0; i < huffman_pixs; ++i) { - // The huffman data is stored in red and green bytes. - const int group = (huffman_image[i] >> 8) & 0xffff; - huffman_image[i] = group; - if (group >= num_htree_groups) { - num_htree_groups = group + 1; - } - } - } - - if (br->eos_) goto Error; - - // Find maximum alphabet size for the htree group. - for (j = 0; j < HUFFMAN_CODES_PER_META_CODE; ++j) { - int alphabet_size = kAlphabetSize[j]; - if (j == 0 && color_cache_bits > 0) { - alphabet_size += 1 << color_cache_bits; - } - if (max_alphabet_size < alphabet_size) { - max_alphabet_size = alphabet_size; - } - } - - huffman_tables = (HuffmanCode*)WebPSafeMalloc(num_htree_groups * table_size, - sizeof(*huffman_tables)); - htree_groups = VP8LHtreeGroupsNew(num_htree_groups); - code_lengths = (int*)WebPSafeCalloc((uint64_t)max_alphabet_size, - sizeof(*code_lengths)); - - if (htree_groups == NULL || code_lengths == NULL || huffman_tables == NULL) { - dec->status_ = VP8_STATUS_OUT_OF_MEMORY; - goto Error; - } - - next = huffman_tables; - for (i = 0; i < num_htree_groups; ++i) { - HTreeGroup* const htree_group = &htree_groups[i]; - HuffmanCode** const htrees = htree_group->htrees; - int size; - int total_size = 0; - int is_trivial_literal = 1; - int max_bits = 0; - for (j = 0; j < HUFFMAN_CODES_PER_META_CODE; ++j) { - int alphabet_size = kAlphabetSize[j]; - htrees[j] = next; - if (j == 0 && color_cache_bits > 0) { - alphabet_size += 1 << color_cache_bits; - } - size = ReadHuffmanCode(alphabet_size, dec, code_lengths, next); - if (size == 0) { - goto Error; - } - if (is_trivial_literal && kLiteralMap[j] == 1) { - is_trivial_literal = (next->bits == 0); - } - total_size += next->bits; - next += size; - if (j <= ALPHA) { - int local_max_bits = code_lengths[0]; - int k; - for (k = 1; k < alphabet_size; ++k) { - if (code_lengths[k] > local_max_bits) { - local_max_bits = code_lengths[k]; - } - } - max_bits += local_max_bits; - } - } - htree_group->is_trivial_literal = is_trivial_literal; - htree_group->is_trivial_code = 0; - if (is_trivial_literal) { - const int red = htrees[RED][0].value; - const int blue = htrees[BLUE][0].value; - const int alpha = htrees[ALPHA][0].value; - htree_group->literal_arb = - ((uint32_t)alpha << 24) | (red << 16) | blue; - if (total_size == 0 && htrees[GREEN][0].value < NUM_LITERAL_CODES) { - htree_group->is_trivial_code = 1; - htree_group->literal_arb |= htrees[GREEN][0].value << 8; - } - } - htree_group->use_packed_table = !htree_group->is_trivial_code && - (max_bits < HUFFMAN_PACKED_BITS); - if (htree_group->use_packed_table) BuildPackedTable(htree_group); - } - WebPSafeFree(code_lengths); - - // All OK. Finalize pointers and return. - hdr->huffman_image_ = huffman_image; - hdr->num_htree_groups_ = num_htree_groups; - hdr->htree_groups_ = htree_groups; - hdr->huffman_tables_ = huffman_tables; - return 1; - - Error: - WebPSafeFree(code_lengths); - WebPSafeFree(huffman_image); - WebPSafeFree(huffman_tables); - VP8LHtreeGroupsFree(htree_groups); - return 0; -} - -//------------------------------------------------------------------------------ -// Scaling. - -static int AllocateAndInitRescaler(VP8LDecoder* const dec, VP8Io* const io) { - const int num_channels = 4; - const int in_width = io->mb_w; - const int out_width = io->scaled_width; - const int in_height = io->mb_h; - const int out_height = io->scaled_height; - const uint64_t work_size = 2 * num_channels * (uint64_t)out_width; - rescaler_t* work; // Rescaler work area. - const uint64_t scaled_data_size = (uint64_t)out_width; - uint32_t* scaled_data; // Temporary storage for scaled BGRA data. - const uint64_t memory_size = sizeof(*dec->rescaler) + - work_size * sizeof(*work) + - scaled_data_size * sizeof(*scaled_data); - uint8_t* memory = (uint8_t*)WebPSafeMalloc(memory_size, sizeof(*memory)); - if (memory == NULL) { - dec->status_ = VP8_STATUS_OUT_OF_MEMORY; - return 0; - } - assert(dec->rescaler_memory == NULL); - dec->rescaler_memory = memory; - - dec->rescaler = (WebPRescaler*)memory; - memory += sizeof(*dec->rescaler); - work = (rescaler_t*)memory; - memory += work_size * sizeof(*work); - scaled_data = (uint32_t*)memory; - - WebPRescalerInit(dec->rescaler, in_width, in_height, (uint8_t*)scaled_data, - out_width, out_height, 0, num_channels, work); - return 1; -} - -//------------------------------------------------------------------------------ -// Export to ARGB - -// We have special "export" function since we need to convert from BGRA -static int Export(WebPRescaler* const rescaler, WEBP_CSP_MODE colorspace, - int rgba_stride, uint8_t* const rgba) { - uint32_t* const src = (uint32_t*)rescaler->dst; - const int dst_width = rescaler->dst_width; - int num_lines_out = 0; - while (WebPRescalerHasPendingOutput(rescaler)) { - uint8_t* const dst = rgba + num_lines_out * rgba_stride; - WebPRescalerExportRow(rescaler); - WebPMultARGBRow(src, dst_width, 1); - VP8LConvertFromBGRA(src, dst_width, colorspace, dst); - ++num_lines_out; - } - return num_lines_out; -} - -// Emit scaled rows. -static int EmitRescaledRowsRGBA(const VP8LDecoder* const dec, - uint8_t* in, int in_stride, int mb_h, - uint8_t* const out, int out_stride) { - const WEBP_CSP_MODE colorspace = dec->output_->colorspace; - int num_lines_in = 0; - int num_lines_out = 0; - while (num_lines_in < mb_h) { - uint8_t* const row_in = in + num_lines_in * in_stride; - uint8_t* const row_out = out + num_lines_out * out_stride; - const int lines_left = mb_h - num_lines_in; - const int needed_lines = WebPRescaleNeededLines(dec->rescaler, lines_left); - assert(needed_lines > 0 && needed_lines <= lines_left); - WebPMultARGBRows(row_in, in_stride, - dec->rescaler->src_width, needed_lines, 0); - WebPRescalerImport(dec->rescaler, lines_left, row_in, in_stride); - num_lines_in += needed_lines; - num_lines_out += Export(dec->rescaler, colorspace, out_stride, row_out); - } - return num_lines_out; -} - -// Emit rows without any scaling. -static int EmitRows(WEBP_CSP_MODE colorspace, - const uint8_t* row_in, int in_stride, - int mb_w, int mb_h, - uint8_t* const out, int out_stride) { - int lines = mb_h; - uint8_t* row_out = out; - while (lines-- > 0) { - VP8LConvertFromBGRA((const uint32_t*)row_in, mb_w, colorspace, row_out); - row_in += in_stride; - row_out += out_stride; - } - return mb_h; // Num rows out == num rows in. -} - -//------------------------------------------------------------------------------ -// Export to YUVA - -static void ConvertToYUVA(const uint32_t* const src, int width, int y_pos, - const WebPDecBuffer* const output) { - const WebPYUVABuffer* const buf = &output->u.YUVA; - - // first, the luma plane - WebPConvertARGBToY(src, buf->y + y_pos * buf->y_stride, width); - - // then U/V planes - { - uint8_t* const u = buf->u + (y_pos >> 1) * buf->u_stride; - uint8_t* const v = buf->v + (y_pos >> 1) * buf->v_stride; - // even lines: store values - // odd lines: average with previous values - WebPConvertARGBToUV(src, u, v, width, !(y_pos & 1)); - } - // Lastly, store alpha if needed. - if (buf->a != NULL) { - uint8_t* const a = buf->a + y_pos * buf->a_stride; -#if defined(WORDS_BIGENDIAN) - WebPExtractAlpha((uint8_t*)src + 0, 0, width, 1, a, 0); -#else - WebPExtractAlpha((uint8_t*)src + 3, 0, width, 1, a, 0); -#endif - } -} - -static int ExportYUVA(const VP8LDecoder* const dec, int y_pos) { - WebPRescaler* const rescaler = dec->rescaler; - uint32_t* const src = (uint32_t*)rescaler->dst; - const int dst_width = rescaler->dst_width; - int num_lines_out = 0; - while (WebPRescalerHasPendingOutput(rescaler)) { - WebPRescalerExportRow(rescaler); - WebPMultARGBRow(src, dst_width, 1); - ConvertToYUVA(src, dst_width, y_pos, dec->output_); - ++y_pos; - ++num_lines_out; - } - return num_lines_out; -} - -static int EmitRescaledRowsYUVA(const VP8LDecoder* const dec, - uint8_t* in, int in_stride, int mb_h) { - int num_lines_in = 0; - int y_pos = dec->last_out_row_; - while (num_lines_in < mb_h) { - const int lines_left = mb_h - num_lines_in; - const int needed_lines = WebPRescaleNeededLines(dec->rescaler, lines_left); - WebPMultARGBRows(in, in_stride, dec->rescaler->src_width, needed_lines, 0); - WebPRescalerImport(dec->rescaler, lines_left, in, in_stride); - num_lines_in += needed_lines; - in += needed_lines * in_stride; - y_pos += ExportYUVA(dec, y_pos); - } - return y_pos; -} - -static int EmitRowsYUVA(const VP8LDecoder* const dec, - const uint8_t* in, int in_stride, - int mb_w, int num_rows) { - int y_pos = dec->last_out_row_; - while (num_rows-- > 0) { - ConvertToYUVA((const uint32_t*)in, mb_w, y_pos, dec->output_); - in += in_stride; - ++y_pos; - } - return y_pos; -} - -//------------------------------------------------------------------------------ -// Cropping. - -// Sets io->mb_y, io->mb_h & io->mb_w according to start row, end row and -// crop options. Also updates the input data pointer, so that it points to the -// start of the cropped window. Note that pixels are in ARGB format even if -// 'in_data' is uint8_t*. -// Returns true if the crop window is not empty. -static int SetCropWindow(VP8Io* const io, int y_start, int y_end, - uint8_t** const in_data, int pixel_stride) { - assert(y_start < y_end); - assert(io->crop_left < io->crop_right); - if (y_end > io->crop_bottom) { - y_end = io->crop_bottom; // make sure we don't overflow on last row. - } - if (y_start < io->crop_top) { - const int delta = io->crop_top - y_start; - y_start = io->crop_top; - *in_data += delta * pixel_stride; - } - if (y_start >= y_end) return 0; // Crop window is empty. - - *in_data += io->crop_left * sizeof(uint32_t); - - io->mb_y = y_start - io->crop_top; - io->mb_w = io->crop_right - io->crop_left; - io->mb_h = y_end - y_start; - return 1; // Non-empty crop window. -} - -//------------------------------------------------------------------------------ - -static WEBP_INLINE int GetMetaIndex( - const uint32_t* const image, int xsize, int bits, int x, int y) { - if (bits == 0) return 0; - return image[xsize * (y >> bits) + (x >> bits)]; -} - -static WEBP_INLINE HTreeGroup* GetHtreeGroupForPos(VP8LMetadata* const hdr, - int x, int y) { - const int meta_index = GetMetaIndex(hdr->huffman_image_, hdr->huffman_xsize_, - hdr->huffman_subsample_bits_, x, y); - assert(meta_index < hdr->num_htree_groups_); - return hdr->htree_groups_ + meta_index; -} - -//------------------------------------------------------------------------------ -// Main loop, with custom row-processing function - -typedef void (*ProcessRowsFunc)(VP8LDecoder* const dec, int row); - -static void ApplyInverseTransforms(VP8LDecoder* const dec, int num_rows, - const uint32_t* const rows) { - int n = dec->next_transform_; - const int cache_pixs = dec->width_ * num_rows; - const int start_row = dec->last_row_; - const int end_row = start_row + num_rows; - const uint32_t* rows_in = rows; - uint32_t* const rows_out = dec->argb_cache_; - - // Inverse transforms. - // TODO: most transforms only need to operate on the cropped region only. - memcpy(rows_out, rows_in, cache_pixs * sizeof(*rows_out)); - while (n-- > 0) { - VP8LTransform* const transform = &dec->transforms_[n]; - VP8LInverseTransform(transform, start_row, end_row, rows_in, rows_out); - rows_in = rows_out; - } -} - -// Processes (transforms, scales & color-converts) the rows decoded after the -// last call. -static void ProcessRows(VP8LDecoder* const dec, int row) { - const uint32_t* const rows = dec->pixels_ + dec->width_ * dec->last_row_; - const int num_rows = row - dec->last_row_; - - assert(row <= dec->io_->crop_bottom); - // We can't process more than NUM_ARGB_CACHE_ROWS at a time (that's the size - // of argb_cache_), but we currently don't need more than that. - assert(num_rows <= NUM_ARGB_CACHE_ROWS); - if (num_rows > 0) { // Emit output. - VP8Io* const io = dec->io_; - uint8_t* rows_data = (uint8_t*)dec->argb_cache_; - const int in_stride = io->width * sizeof(uint32_t); // in unit of RGBA - - ApplyInverseTransforms(dec, num_rows, rows); - if (!SetCropWindow(io, dec->last_row_, row, &rows_data, in_stride)) { - // Nothing to output (this time). - } else { - const WebPDecBuffer* const output = dec->output_; - if (WebPIsRGBMode(output->colorspace)) { // convert to RGBA - const WebPRGBABuffer* const buf = &output->u.RGBA; - uint8_t* const rgba = buf->rgba + dec->last_out_row_ * buf->stride; - const int num_rows_out = io->use_scaling ? - EmitRescaledRowsRGBA(dec, rows_data, in_stride, io->mb_h, - rgba, buf->stride) : - EmitRows(output->colorspace, rows_data, in_stride, - io->mb_w, io->mb_h, rgba, buf->stride); - // Update 'last_out_row_'. - dec->last_out_row_ += num_rows_out; - } else { // convert to YUVA - dec->last_out_row_ = io->use_scaling ? - EmitRescaledRowsYUVA(dec, rows_data, in_stride, io->mb_h) : - EmitRowsYUVA(dec, rows_data, in_stride, io->mb_w, io->mb_h); - } - assert(dec->last_out_row_ <= output->height); - } - } - - // Update 'last_row_'. - dec->last_row_ = row; - assert(dec->last_row_ <= dec->height_); -} - -// Row-processing for the special case when alpha data contains only one -// transform (color indexing), and trivial non-green literals. -static int Is8bOptimizable(const VP8LMetadata* const hdr) { - int i; - if (hdr->color_cache_size_ > 0) return 0; - // When the Huffman tree contains only one symbol, we can skip the - // call to ReadSymbol() for red/blue/alpha channels. - for (i = 0; i < hdr->num_htree_groups_; ++i) { - HuffmanCode** const htrees = hdr->htree_groups_[i].htrees; - if (htrees[RED][0].bits > 0) return 0; - if (htrees[BLUE][0].bits > 0) return 0; - if (htrees[ALPHA][0].bits > 0) return 0; - } - return 1; -} - -static void AlphaApplyFilter(ALPHDecoder* const alph_dec, - int first_row, int last_row, - uint8_t* out, int stride) { - if (alph_dec->filter_ != WEBP_FILTER_NONE) { - int y; - const uint8_t* prev_line = alph_dec->prev_line_; - assert(WebPUnfilters[alph_dec->filter_] != NULL); - for (y = first_row; y < last_row; ++y) { - WebPUnfilters[alph_dec->filter_](prev_line, out, out, stride); - prev_line = out; - out += stride; - } - alph_dec->prev_line_ = prev_line; - } -} - -static void ExtractPalettedAlphaRows(VP8LDecoder* const dec, int last_row) { - // For vertical and gradient filtering, we need to decode the part above the - // crop_top row, in order to have the correct spatial predictors. - ALPHDecoder* const alph_dec = (ALPHDecoder*)dec->io_->opaque; - const int top_row = - (alph_dec->filter_ == WEBP_FILTER_NONE || - alph_dec->filter_ == WEBP_FILTER_HORIZONTAL) ? dec->io_->crop_top - : dec->last_row_; - const int first_row = (dec->last_row_ < top_row) ? top_row : dec->last_row_; - assert(last_row <= dec->io_->crop_bottom); - if (last_row > first_row) { - // Special method for paletted alpha data. We only process the cropped area. - const int width = dec->io_->width; - uint8_t* out = alph_dec->output_ + width * first_row; - const uint8_t* const in = - (uint8_t*)dec->pixels_ + dec->width_ * first_row; - VP8LTransform* const transform = &dec->transforms_[0]; - assert(dec->next_transform_ == 1); - assert(transform->type_ == COLOR_INDEXING_TRANSFORM); - VP8LColorIndexInverseTransformAlpha(transform, first_row, last_row, - in, out); - AlphaApplyFilter(alph_dec, first_row, last_row, out, width); - } - dec->last_row_ = dec->last_out_row_ = last_row; -} - -//------------------------------------------------------------------------------ -// Helper functions for fast pattern copy (8b and 32b) - -// cyclic rotation of pattern word -static WEBP_INLINE uint32_t Rotate8b(uint32_t V) { -#if defined(WORDS_BIGENDIAN) - return ((V & 0xff000000u) >> 24) | (V << 8); -#else - return ((V & 0xffu) << 24) | (V >> 8); -#endif -} - -// copy 1, 2 or 4-bytes pattern -static WEBP_INLINE void CopySmallPattern8b(const uint8_t* src, uint8_t* dst, - int length, uint32_t pattern) { - int i; - // align 'dst' to 4-bytes boundary. Adjust the pattern along the way. - while ((uintptr_t)dst & 3) { - *dst++ = *src++; - pattern = Rotate8b(pattern); - --length; - } - // Copy the pattern 4 bytes at a time. - for (i = 0; i < (length >> 2); ++i) { - ((uint32_t*)dst)[i] = pattern; - } - // Finish with left-overs. 'pattern' is still correctly positioned, - // so no Rotate8b() call is needed. - for (i <<= 2; i < length; ++i) { - dst[i] = src[i]; - } -} - -static WEBP_INLINE void CopyBlock8b(uint8_t* const dst, int dist, int length) { - const uint8_t* src = dst - dist; - if (length >= 8) { - uint32_t pattern = 0; - switch (dist) { - case 1: - pattern = src[0]; -#if defined(__arm__) || defined(_M_ARM) // arm doesn't like multiply that much - pattern |= pattern << 8; - pattern |= pattern << 16; -#elif defined(WEBP_USE_MIPS_DSP_R2) - __asm__ volatile ("replv.qb %0, %0" : "+r"(pattern)); -#else - pattern = 0x01010101u * pattern; -#endif - break; - case 2: - memcpy(&pattern, src, sizeof(uint16_t)); -#if defined(__arm__) || defined(_M_ARM) - pattern |= pattern << 16; -#elif defined(WEBP_USE_MIPS_DSP_R2) - __asm__ volatile ("replv.ph %0, %0" : "+r"(pattern)); -#else - pattern = 0x00010001u * pattern; -#endif - break; - case 4: - memcpy(&pattern, src, sizeof(uint32_t)); - break; - default: - goto Copy; - break; - } - CopySmallPattern8b(src, dst, length, pattern); - return; - } - Copy: - if (dist >= length) { // no overlap -> use memcpy() - memcpy(dst, src, length * sizeof(*dst)); - } else { - int i; - for (i = 0; i < length; ++i) dst[i] = src[i]; - } -} - -// copy pattern of 1 or 2 uint32_t's -static WEBP_INLINE void CopySmallPattern32b(const uint32_t* src, - uint32_t* dst, - int length, uint64_t pattern) { - int i; - if ((uintptr_t)dst & 4) { // Align 'dst' to 8-bytes boundary. - *dst++ = *src++; - pattern = (pattern >> 32) | (pattern << 32); - --length; - } - assert(0 == ((uintptr_t)dst & 7)); - for (i = 0; i < (length >> 1); ++i) { - ((uint64_t*)dst)[i] = pattern; // Copy the pattern 8 bytes at a time. - } - if (length & 1) { // Finish with left-over. - dst[i << 1] = src[i << 1]; - } -} - -static WEBP_INLINE void CopyBlock32b(uint32_t* const dst, - int dist, int length) { - const uint32_t* const src = dst - dist; - if (dist <= 2 && length >= 4 && ((uintptr_t)dst & 3) == 0) { - uint64_t pattern; - if (dist == 1) { - pattern = (uint64_t)src[0]; - pattern |= pattern << 32; - } else { - memcpy(&pattern, src, sizeof(pattern)); - } - CopySmallPattern32b(src, dst, length, pattern); - } else if (dist >= length) { // no overlap - memcpy(dst, src, length * sizeof(*dst)); - } else { - int i; - for (i = 0; i < length; ++i) dst[i] = src[i]; - } -} - -//------------------------------------------------------------------------------ - -static int DecodeAlphaData(VP8LDecoder* const dec, uint8_t* const data, - int width, int height, int last_row) { - int ok = 1; - int row = dec->last_pixel_ / width; - int col = dec->last_pixel_ % width; - VP8LBitReader* const br = &dec->br_; - VP8LMetadata* const hdr = &dec->hdr_; - int pos = dec->last_pixel_; // current position - const int end = width * height; // End of data - const int last = width * last_row; // Last pixel to decode - const int len_code_limit = NUM_LITERAL_CODES + NUM_LENGTH_CODES; - const int mask = hdr->huffman_mask_; - const HTreeGroup* htree_group = - (pos < last) ? GetHtreeGroupForPos(hdr, col, row) : NULL; - assert(pos <= end); - assert(last_row <= height); - assert(Is8bOptimizable(hdr)); - - while (!br->eos_ && pos < last) { - int code; - // Only update when changing tile. - if ((col & mask) == 0) { - htree_group = GetHtreeGroupForPos(hdr, col, row); - } - assert(htree_group != NULL); - VP8LFillBitWindow(br); - code = ReadSymbol(htree_group->htrees[GREEN], br); - if (code < NUM_LITERAL_CODES) { // Literal - data[pos] = code; - ++pos; - ++col; - if (col >= width) { - col = 0; - ++row; - if (row <= last_row && (row % NUM_ARGB_CACHE_ROWS == 0)) { - ExtractPalettedAlphaRows(dec, row); - } - } - } else if (code < len_code_limit) { // Backward reference - int dist_code, dist; - const int length_sym = code - NUM_LITERAL_CODES; - const int length = GetCopyLength(length_sym, br); - const int dist_symbol = ReadSymbol(htree_group->htrees[DIST], br); - VP8LFillBitWindow(br); - dist_code = GetCopyDistance(dist_symbol, br); - dist = PlaneCodeToDistance(width, dist_code); - if (pos >= dist && end - pos >= length) { - CopyBlock8b(data + pos, dist, length); - } else { - ok = 0; - goto End; - } - pos += length; - col += length; - while (col >= width) { - col -= width; - ++row; - if (row <= last_row && (row % NUM_ARGB_CACHE_ROWS == 0)) { - ExtractPalettedAlphaRows(dec, row); - } - } - if (pos < last && (col & mask)) { - htree_group = GetHtreeGroupForPos(hdr, col, row); - } - } else { // Not reached - ok = 0; - goto End; - } - assert(br->eos_ == VP8LIsEndOfStream(br)); - } - // Process the remaining rows corresponding to last row-block. - ExtractPalettedAlphaRows(dec, row > last_row ? last_row : row); - - End: - if (!ok || (br->eos_ && pos < end)) { - ok = 0; - dec->status_ = br->eos_ ? VP8_STATUS_SUSPENDED - : VP8_STATUS_BITSTREAM_ERROR; - } else { - dec->last_pixel_ = pos; - } - return ok; -} - -static void SaveState(VP8LDecoder* const dec, int last_pixel) { - assert(dec->incremental_); - dec->saved_br_ = dec->br_; - dec->saved_last_pixel_ = last_pixel; - if (dec->hdr_.color_cache_size_ > 0) { - VP8LColorCacheCopy(&dec->hdr_.color_cache_, &dec->hdr_.saved_color_cache_); - } -} - -static void RestoreState(VP8LDecoder* const dec) { - assert(dec->br_.eos_); - dec->status_ = VP8_STATUS_SUSPENDED; - dec->br_ = dec->saved_br_; - dec->last_pixel_ = dec->saved_last_pixel_; - if (dec->hdr_.color_cache_size_ > 0) { - VP8LColorCacheCopy(&dec->hdr_.saved_color_cache_, &dec->hdr_.color_cache_); - } -} - -#define SYNC_EVERY_N_ROWS 8 // minimum number of rows between check-points -static int DecodeImageData(VP8LDecoder* const dec, uint32_t* const data, - int width, int height, int last_row, - ProcessRowsFunc process_func) { - int row = dec->last_pixel_ / width; - int col = dec->last_pixel_ % width; - VP8LBitReader* const br = &dec->br_; - VP8LMetadata* const hdr = &dec->hdr_; - uint32_t* src = data + dec->last_pixel_; - uint32_t* last_cached = src; - uint32_t* const src_end = data + width * height; // End of data - uint32_t* const src_last = data + width * last_row; // Last pixel to decode - const int len_code_limit = NUM_LITERAL_CODES + NUM_LENGTH_CODES; - const int color_cache_limit = len_code_limit + hdr->color_cache_size_; - int next_sync_row = dec->incremental_ ? row : 1 << 24; - VP8LColorCache* const color_cache = - (hdr->color_cache_size_ > 0) ? &hdr->color_cache_ : NULL; - const int mask = hdr->huffman_mask_; - const HTreeGroup* htree_group = - (src < src_last) ? GetHtreeGroupForPos(hdr, col, row) : NULL; - assert(dec->last_row_ < last_row); - assert(src_last <= src_end); - - while (src < src_last) { - int code; - if (row >= next_sync_row) { - SaveState(dec, (int)(src - data)); - next_sync_row = row + SYNC_EVERY_N_ROWS; - } - // Only update when changing tile. Note we could use this test: - // if "((((prev_col ^ col) | prev_row ^ row)) > mask)" -> tile changed - // but that's actually slower and needs storing the previous col/row. - if ((col & mask) == 0) { - htree_group = GetHtreeGroupForPos(hdr, col, row); - } - assert(htree_group != NULL); - if (htree_group->is_trivial_code) { - *src = htree_group->literal_arb; - goto AdvanceByOne; - } - VP8LFillBitWindow(br); - if (htree_group->use_packed_table) { - code = ReadPackedSymbols(htree_group, br, src); - if (code == PACKED_NON_LITERAL_CODE) goto AdvanceByOne; - } else { - code = ReadSymbol(htree_group->htrees[GREEN], br); - } - if (br->eos_) break; // early out - if (code < NUM_LITERAL_CODES) { // Literal - if (htree_group->is_trivial_literal) { - *src = htree_group->literal_arb | (code << 8); - } else { - int red, blue, alpha; - red = ReadSymbol(htree_group->htrees[RED], br); - VP8LFillBitWindow(br); - blue = ReadSymbol(htree_group->htrees[BLUE], br); - alpha = ReadSymbol(htree_group->htrees[ALPHA], br); - if (br->eos_) break; - *src = ((uint32_t)alpha << 24) | (red << 16) | (code << 8) | blue; - } - AdvanceByOne: - ++src; - ++col; - if (col >= width) { - col = 0; - ++row; - if (process_func != NULL) { - if (row <= last_row && (row % NUM_ARGB_CACHE_ROWS == 0)) { - process_func(dec, row); - } - } - if (color_cache != NULL) { - while (last_cached < src) { - VP8LColorCacheInsert(color_cache, *last_cached++); - } - } - } - } else if (code < len_code_limit) { // Backward reference - int dist_code, dist; - const int length_sym = code - NUM_LITERAL_CODES; - const int length = GetCopyLength(length_sym, br); - const int dist_symbol = ReadSymbol(htree_group->htrees[DIST], br); - VP8LFillBitWindow(br); - dist_code = GetCopyDistance(dist_symbol, br); - dist = PlaneCodeToDistance(width, dist_code); - if (br->eos_) break; - if (src - data < (ptrdiff_t)dist || src_end - src < (ptrdiff_t)length) { - goto Error; - } else { - CopyBlock32b(src, dist, length); - } - src += length; - col += length; - while (col >= width) { - col -= width; - ++row; - if (process_func != NULL) { - if (row <= last_row && (row % NUM_ARGB_CACHE_ROWS == 0)) { - process_func(dec, row); - } - } - } - // Because of the check done above (before 'src' was incremented by - // 'length'), the following holds true. - assert(src <= src_end); - if (col & mask) htree_group = GetHtreeGroupForPos(hdr, col, row); - if (color_cache != NULL) { - while (last_cached < src) { - VP8LColorCacheInsert(color_cache, *last_cached++); - } - } - } else if (code < color_cache_limit) { // Color cache - const int key = code - len_code_limit; - assert(color_cache != NULL); - while (last_cached < src) { - VP8LColorCacheInsert(color_cache, *last_cached++); - } - *src = VP8LColorCacheLookup(color_cache, key); - goto AdvanceByOne; - } else { // Not reached - goto Error; - } - assert(br->eos_ == VP8LIsEndOfStream(br)); - } - - if (dec->incremental_ && br->eos_ && src < src_end) { - RestoreState(dec); - } else if (!br->eos_) { - // Process the remaining rows corresponding to last row-block. - if (process_func != NULL) { - process_func(dec, row > last_row ? last_row : row); - } - dec->status_ = VP8_STATUS_OK; - dec->last_pixel_ = (int)(src - data); // end-of-scan marker - } else { - // if not incremental, and we are past the end of buffer (eos_=1), then this - // is a real bitstream error. - goto Error; - } - return 1; - - Error: - dec->status_ = VP8_STATUS_BITSTREAM_ERROR; - return 0; -} - -// ----------------------------------------------------------------------------- -// VP8LTransform - -static void ClearTransform(VP8LTransform* const transform) { - WebPSafeFree(transform->data_); - transform->data_ = NULL; -} - -// For security reason, we need to remap the color map to span -// the total possible bundled values, and not just the num_colors. -static int ExpandColorMap(int num_colors, VP8LTransform* const transform) { - int i; - const int final_num_colors = 1 << (8 >> transform->bits_); - uint32_t* const new_color_map = - (uint32_t*)WebPSafeMalloc((uint64_t)final_num_colors, - sizeof(*new_color_map)); - if (new_color_map == NULL) { - return 0; - } else { - uint8_t* const data = (uint8_t*)transform->data_; - uint8_t* const new_data = (uint8_t*)new_color_map; - new_color_map[0] = transform->data_[0]; - for (i = 4; i < 4 * num_colors; ++i) { - // Equivalent to AddPixelEq(), on a byte-basis. - new_data[i] = (data[i] + new_data[i - 4]) & 0xff; - } - for (; i < 4 * final_num_colors; ++i) - new_data[i] = 0; // black tail. - WebPSafeFree(transform->data_); - transform->data_ = new_color_map; - } - return 1; -} - -static int ReadTransform(int* const xsize, int const* ysize, - VP8LDecoder* const dec) { - int ok = 1; - VP8LBitReader* const br = &dec->br_; - VP8LTransform* transform = &dec->transforms_[dec->next_transform_]; - const VP8LImageTransformType type = - (VP8LImageTransformType)VP8LReadBits(br, 2); - - // Each transform type can only be present once in the stream. - if (dec->transforms_seen_ & (1U << type)) { - return 0; // Already there, let's not accept the second same transform. - } - dec->transforms_seen_ |= (1U << type); - - transform->type_ = type; - transform->xsize_ = *xsize; - transform->ysize_ = *ysize; - transform->data_ = NULL; - ++dec->next_transform_; - assert(dec->next_transform_ <= NUM_TRANSFORMS); - - switch (type) { - case PREDICTOR_TRANSFORM: - case CROSS_COLOR_TRANSFORM: - transform->bits_ = VP8LReadBits(br, 3) + 2; - ok = DecodeImageStream(VP8LSubSampleSize(transform->xsize_, - transform->bits_), - VP8LSubSampleSize(transform->ysize_, - transform->bits_), - 0, dec, &transform->data_); - break; - case COLOR_INDEXING_TRANSFORM: { - const int num_colors = VP8LReadBits(br, 8) + 1; - const int bits = (num_colors > 16) ? 0 - : (num_colors > 4) ? 1 - : (num_colors > 2) ? 2 - : 3; - *xsize = VP8LSubSampleSize(transform->xsize_, bits); - transform->bits_ = bits; - ok = DecodeImageStream(num_colors, 1, 0, dec, &transform->data_); - ok = ok && ExpandColorMap(num_colors, transform); - break; - } - case SUBTRACT_GREEN: - break; - default: - assert(0); // can't happen - break; - } - - return ok; -} - -// ----------------------------------------------------------------------------- -// VP8LMetadata - -static void InitMetadata(VP8LMetadata* const hdr) { - assert(hdr != NULL); - memset(hdr, 0, sizeof(*hdr)); -} - -static void ClearMetadata(VP8LMetadata* const hdr) { - assert(hdr != NULL); - - WebPSafeFree(hdr->huffman_image_); - WebPSafeFree(hdr->huffman_tables_); - VP8LHtreeGroupsFree(hdr->htree_groups_); - VP8LColorCacheClear(&hdr->color_cache_); - VP8LColorCacheClear(&hdr->saved_color_cache_); - InitMetadata(hdr); -} - -// ----------------------------------------------------------------------------- -// VP8LDecoder - -VP8LDecoder* VP8LNew(void) { - VP8LDecoder* const dec = (VP8LDecoder*)WebPSafeCalloc(1ULL, sizeof(*dec)); - if (dec == NULL) return NULL; - dec->status_ = VP8_STATUS_OK; - dec->state_ = READ_DIM; - - VP8LDspInit(); // Init critical function pointers. - - return dec; -} - -void VP8LClear(VP8LDecoder* const dec) { - int i; - if (dec == NULL) return; - ClearMetadata(&dec->hdr_); - - WebPSafeFree(dec->pixels_); - dec->pixels_ = NULL; - for (i = 0; i < dec->next_transform_; ++i) { - ClearTransform(&dec->transforms_[i]); - } - dec->next_transform_ = 0; - dec->transforms_seen_ = 0; - - WebPSafeFree(dec->rescaler_memory); - dec->rescaler_memory = NULL; - - dec->output_ = NULL; // leave no trace behind -} - -void VP8LDelete(VP8LDecoder* const dec) { - if (dec != NULL) { - VP8LClear(dec); - WebPSafeFree(dec); - } -} - -static void UpdateDecoder(VP8LDecoder* const dec, int width, int height) { - VP8LMetadata* const hdr = &dec->hdr_; - const int num_bits = hdr->huffman_subsample_bits_; - dec->width_ = width; - dec->height_ = height; - - hdr->huffman_xsize_ = VP8LSubSampleSize(width, num_bits); - hdr->huffman_mask_ = (num_bits == 0) ? ~0 : (1 << num_bits) - 1; -} - -static int DecodeImageStream(int xsize, int ysize, - int is_level0, - VP8LDecoder* const dec, - uint32_t** const decoded_data) { - int ok = 1; - int transform_xsize = xsize; - int transform_ysize = ysize; - VP8LBitReader* const br = &dec->br_; - VP8LMetadata* const hdr = &dec->hdr_; - uint32_t* data = NULL; - int color_cache_bits = 0; - - // Read the transforms (may recurse). - if (is_level0) { - while (ok && VP8LReadBits(br, 1)) { - ok = ReadTransform(&transform_xsize, &transform_ysize, dec); - } - } - - // Color cache - if (ok && VP8LReadBits(br, 1)) { - color_cache_bits = VP8LReadBits(br, 4); - ok = (color_cache_bits >= 1 && color_cache_bits <= MAX_CACHE_BITS); - if (!ok) { - dec->status_ = VP8_STATUS_BITSTREAM_ERROR; - goto End; - } - } - - // Read the Huffman codes (may recurse). - ok = ok && ReadHuffmanCodes(dec, transform_xsize, transform_ysize, - color_cache_bits, is_level0); - if (!ok) { - dec->status_ = VP8_STATUS_BITSTREAM_ERROR; - goto End; - } - - // Finish setting up the color-cache - if (color_cache_bits > 0) { - hdr->color_cache_size_ = 1 << color_cache_bits; - if (!VP8LColorCacheInit(&hdr->color_cache_, color_cache_bits)) { - dec->status_ = VP8_STATUS_OUT_OF_MEMORY; - ok = 0; - goto End; - } - } else { - hdr->color_cache_size_ = 0; - } - UpdateDecoder(dec, transform_xsize, transform_ysize); - - if (is_level0) { // level 0 complete - dec->state_ = READ_HDR; - goto End; - } - - { - const uint64_t total_size = (uint64_t)transform_xsize * transform_ysize; - data = (uint32_t*)WebPSafeMalloc(total_size, sizeof(*data)); - if (data == NULL) { - dec->status_ = VP8_STATUS_OUT_OF_MEMORY; - ok = 0; - goto End; - } - } - - // Use the Huffman trees to decode the LZ77 encoded data. - ok = DecodeImageData(dec, data, transform_xsize, transform_ysize, - transform_ysize, NULL); - ok = ok && !br->eos_; - - End: - if (!ok) { - WebPSafeFree(data); - ClearMetadata(hdr); - } else { - if (decoded_data != NULL) { - *decoded_data = data; - } else { - // We allocate image data in this function only for transforms. At level 0 - // (that is: not the transforms), we shouldn't have allocated anything. - assert(data == NULL); - assert(is_level0); - } - dec->last_pixel_ = 0; // Reset for future DECODE_DATA_FUNC() calls. - if (!is_level0) ClearMetadata(hdr); // Clean up temporary data behind. - } - return ok; -} - -//------------------------------------------------------------------------------ -// Allocate internal buffers dec->pixels_ and dec->argb_cache_. -static int AllocateInternalBuffers32b(VP8LDecoder* const dec, int final_width) { - const uint64_t num_pixels = (uint64_t)dec->width_ * dec->height_; - // Scratch buffer corresponding to top-prediction row for transforming the - // first row in the row-blocks. Not needed for paletted alpha. - const uint64_t cache_top_pixels = (uint16_t)final_width; - // Scratch buffer for temporary BGRA storage. Not needed for paletted alpha. - const uint64_t cache_pixels = (uint64_t)final_width * NUM_ARGB_CACHE_ROWS; - const uint64_t total_num_pixels = - num_pixels + cache_top_pixels + cache_pixels; - - assert(dec->width_ <= final_width); - dec->pixels_ = (uint32_t*)WebPSafeMalloc(total_num_pixels, sizeof(uint32_t)); - if (dec->pixels_ == NULL) { - dec->argb_cache_ = NULL; // for sanity check - dec->status_ = VP8_STATUS_OUT_OF_MEMORY; - return 0; - } - dec->argb_cache_ = dec->pixels_ + num_pixels + cache_top_pixels; - return 1; -} - -static int AllocateInternalBuffers8b(VP8LDecoder* const dec) { - const uint64_t total_num_pixels = (uint64_t)dec->width_ * dec->height_; - dec->argb_cache_ = NULL; // for sanity check - dec->pixels_ = (uint32_t*)WebPSafeMalloc(total_num_pixels, sizeof(uint8_t)); - if (dec->pixels_ == NULL) { - dec->status_ = VP8_STATUS_OUT_OF_MEMORY; - return 0; - } - return 1; -} - -//------------------------------------------------------------------------------ - -// Special row-processing that only stores the alpha data. -static void ExtractAlphaRows(VP8LDecoder* const dec, int last_row) { - int cur_row = dec->last_row_; - int num_rows = last_row - cur_row; - const uint32_t* in = dec->pixels_ + dec->width_ * cur_row; - - assert(last_row <= dec->io_->crop_bottom); - while (num_rows > 0) { - const int num_rows_to_process = - (num_rows > NUM_ARGB_CACHE_ROWS) ? NUM_ARGB_CACHE_ROWS : num_rows; - // Extract alpha (which is stored in the green plane). - ALPHDecoder* const alph_dec = (ALPHDecoder*)dec->io_->opaque; - uint8_t* const output = alph_dec->output_; - const int width = dec->io_->width; // the final width (!= dec->width_) - const int cache_pixs = width * num_rows_to_process; - uint8_t* const dst = output + width * cur_row; - const uint32_t* const src = dec->argb_cache_; - int i; - ApplyInverseTransforms(dec, num_rows_to_process, in); - for (i = 0; i < cache_pixs; ++i) dst[i] = (src[i] >> 8) & 0xff; - AlphaApplyFilter(alph_dec, - cur_row, cur_row + num_rows_to_process, dst, width); - num_rows -= num_rows_to_process; - in += num_rows_to_process * dec->width_; - cur_row += num_rows_to_process; - } - assert(cur_row == last_row); - dec->last_row_ = dec->last_out_row_ = last_row; -} - -int VP8LDecodeAlphaHeader(ALPHDecoder* const alph_dec, - const uint8_t* const data, size_t data_size) { - int ok = 0; - VP8LDecoder* dec = VP8LNew(); - - if (dec == NULL) return 0; - - assert(alph_dec != NULL); - alph_dec->vp8l_dec_ = dec; - - dec->width_ = alph_dec->width_; - dec->height_ = alph_dec->height_; - dec->io_ = &alph_dec->io_; - dec->io_->opaque = alph_dec; - dec->io_->width = alph_dec->width_; - dec->io_->height = alph_dec->height_; - - dec->status_ = VP8_STATUS_OK; - VP8LInitBitReader(&dec->br_, data, data_size); - - if (!DecodeImageStream(alph_dec->width_, alph_dec->height_, 1, dec, NULL)) { - goto Err; - } - - // Special case: if alpha data uses only the color indexing transform and - // doesn't use color cache (a frequent case), we will use DecodeAlphaData() - // method that only needs allocation of 1 byte per pixel (alpha channel). - if (dec->next_transform_ == 1 && - dec->transforms_[0].type_ == COLOR_INDEXING_TRANSFORM && - Is8bOptimizable(&dec->hdr_)) { - alph_dec->use_8b_decode_ = 1; - ok = AllocateInternalBuffers8b(dec); - } else { - // Allocate internal buffers (note that dec->width_ may have changed here). - alph_dec->use_8b_decode_ = 0; - ok = AllocateInternalBuffers32b(dec, alph_dec->width_); - } - - if (!ok) goto Err; - - return 1; - - Err: - VP8LDelete(alph_dec->vp8l_dec_); - alph_dec->vp8l_dec_ = NULL; - return 0; -} - -int VP8LDecodeAlphaImageStream(ALPHDecoder* const alph_dec, int last_row) { - VP8LDecoder* const dec = alph_dec->vp8l_dec_; - assert(dec != NULL); - assert(last_row <= dec->height_); - - if (dec->last_row_ >= last_row) { - return 1; // done - } - - // Decode (with special row processing). - return alph_dec->use_8b_decode_ ? - DecodeAlphaData(dec, (uint8_t*)dec->pixels_, dec->width_, dec->height_, - last_row) : - DecodeImageData(dec, dec->pixels_, dec->width_, dec->height_, - last_row, ExtractAlphaRows); -} - -//------------------------------------------------------------------------------ - -int VP8LDecodeHeader(VP8LDecoder* const dec, VP8Io* const io) { - int width, height, has_alpha; - - if (dec == NULL) return 0; - if (io == NULL) { - dec->status_ = VP8_STATUS_INVALID_PARAM; - return 0; - } - - dec->io_ = io; - dec->status_ = VP8_STATUS_OK; - VP8LInitBitReader(&dec->br_, io->data, io->data_size); - if (!ReadImageInfo(&dec->br_, &width, &height, &has_alpha)) { - dec->status_ = VP8_STATUS_BITSTREAM_ERROR; - goto Error; - } - dec->state_ = READ_DIM; - io->width = width; - io->height = height; - - if (!DecodeImageStream(width, height, 1, dec, NULL)) goto Error; - return 1; - - Error: - VP8LClear(dec); - assert(dec->status_ != VP8_STATUS_OK); - return 0; -} - -int VP8LDecodeImage(VP8LDecoder* const dec) { - VP8Io* io = NULL; - WebPDecParams* params = NULL; - - // Sanity checks. - if (dec == NULL) return 0; - - assert(dec->hdr_.huffman_tables_ != NULL); - assert(dec->hdr_.htree_groups_ != NULL); - assert(dec->hdr_.num_htree_groups_ > 0); - - io = dec->io_; - assert(io != NULL); - params = (WebPDecParams*)io->opaque; - assert(params != NULL); - - // Initialization. - if (dec->state_ != READ_DATA) { - dec->output_ = params->output; - assert(dec->output_ != NULL); - - if (!WebPIoInitFromOptions(params->options, io, MODE_BGRA)) { - dec->status_ = VP8_STATUS_INVALID_PARAM; - goto Err; - } - - if (!AllocateInternalBuffers32b(dec, io->width)) goto Err; - - if (io->use_scaling && !AllocateAndInitRescaler(dec, io)) goto Err; - - if (io->use_scaling || WebPIsPremultipliedMode(dec->output_->colorspace)) { - // need the alpha-multiply functions for premultiplied output or rescaling - WebPInitAlphaProcessing(); - } - if (!WebPIsRGBMode(dec->output_->colorspace)) { - WebPInitConvertARGBToYUV(); - if (dec->output_->u.YUVA.a != NULL) WebPInitAlphaProcessing(); - } - if (dec->incremental_) { - if (dec->hdr_.color_cache_size_ > 0 && - dec->hdr_.saved_color_cache_.colors_ == NULL) { - if (!VP8LColorCacheInit(&dec->hdr_.saved_color_cache_, - dec->hdr_.color_cache_.hash_bits_)) { - dec->status_ = VP8_STATUS_OUT_OF_MEMORY; - goto Err; - } - } - } - dec->state_ = READ_DATA; - } - - // Decode. - if (!DecodeImageData(dec, dec->pixels_, dec->width_, dec->height_, - io->crop_bottom, ProcessRows)) { - goto Err; - } - - params->last_y = dec->last_out_row_; - return 1; - - Err: - VP8LClear(dec); - assert(dec->status_ != VP8_STATUS_OK); - return 0; -} - -//------------------------------------------------------------------------------ diff --git a/thirdparty/libwebp/dec/vp8l_dec.c b/thirdparty/libwebp/dec/vp8l_dec.c new file mode 100644 index 0000000000..ef359a91f0 --- /dev/null +++ b/thirdparty/libwebp/dec/vp8l_dec.c @@ -0,0 +1,1671 @@ +// Copyright 2012 Google Inc. All Rights Reserved. +// +// Use of this source code is governed by a BSD-style license +// that can be found in the COPYING file in the root of the source +// tree. An additional intellectual property rights grant can be found +// in the file PATENTS. All contributing project authors may +// be found in the AUTHORS file in the root of the source tree. +// ----------------------------------------------------------------------------- +// +// main entry for the decoder +// +// Authors: Vikas Arora (vikaas.arora@gmail.com) +// Jyrki Alakuijala (jyrki@google.com) + +#include + +#include "./alphai_dec.h" +#include "./vp8li_dec.h" +#include "../dsp/dsp.h" +#include "../dsp/lossless.h" +#include "../dsp/lossless_common.h" +#include "../dsp/yuv.h" +#include "../utils/endian_inl_utils.h" +#include "../utils/huffman_utils.h" +#include "../utils/utils.h" + +#define NUM_ARGB_CACHE_ROWS 16 + +static const int kCodeLengthLiterals = 16; +static const int kCodeLengthRepeatCode = 16; +static const int kCodeLengthExtraBits[3] = { 2, 3, 7 }; +static const int kCodeLengthRepeatOffsets[3] = { 3, 3, 11 }; + +// ----------------------------------------------------------------------------- +// Five Huffman codes are used at each meta code: +// 1. green + length prefix codes + color cache codes, +// 2. alpha, +// 3. red, +// 4. blue, and, +// 5. distance prefix codes. +typedef enum { + GREEN = 0, + RED = 1, + BLUE = 2, + ALPHA = 3, + DIST = 4 +} HuffIndex; + +static const uint16_t kAlphabetSize[HUFFMAN_CODES_PER_META_CODE] = { + NUM_LITERAL_CODES + NUM_LENGTH_CODES, + NUM_LITERAL_CODES, NUM_LITERAL_CODES, NUM_LITERAL_CODES, + NUM_DISTANCE_CODES +}; + +static const uint8_t kLiteralMap[HUFFMAN_CODES_PER_META_CODE] = { + 0, 1, 1, 1, 0 +}; + +#define NUM_CODE_LENGTH_CODES 19 +static const uint8_t kCodeLengthCodeOrder[NUM_CODE_LENGTH_CODES] = { + 17, 18, 0, 1, 2, 3, 4, 5, 16, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 +}; + +#define CODE_TO_PLANE_CODES 120 +static const uint8_t kCodeToPlane[CODE_TO_PLANE_CODES] = { + 0x18, 0x07, 0x17, 0x19, 0x28, 0x06, 0x27, 0x29, 0x16, 0x1a, + 0x26, 0x2a, 0x38, 0x05, 0x37, 0x39, 0x15, 0x1b, 0x36, 0x3a, + 0x25, 0x2b, 0x48, 0x04, 0x47, 0x49, 0x14, 0x1c, 0x35, 0x3b, + 0x46, 0x4a, 0x24, 0x2c, 0x58, 0x45, 0x4b, 0x34, 0x3c, 0x03, + 0x57, 0x59, 0x13, 0x1d, 0x56, 0x5a, 0x23, 0x2d, 0x44, 0x4c, + 0x55, 0x5b, 0x33, 0x3d, 0x68, 0x02, 0x67, 0x69, 0x12, 0x1e, + 0x66, 0x6a, 0x22, 0x2e, 0x54, 0x5c, 0x43, 0x4d, 0x65, 0x6b, + 0x32, 0x3e, 0x78, 0x01, 0x77, 0x79, 0x53, 0x5d, 0x11, 0x1f, + 0x64, 0x6c, 0x42, 0x4e, 0x76, 0x7a, 0x21, 0x2f, 0x75, 0x7b, + 0x31, 0x3f, 0x63, 0x6d, 0x52, 0x5e, 0x00, 0x74, 0x7c, 0x41, + 0x4f, 0x10, 0x20, 0x62, 0x6e, 0x30, 0x73, 0x7d, 0x51, 0x5f, + 0x40, 0x72, 0x7e, 0x61, 0x6f, 0x50, 0x71, 0x7f, 0x60, 0x70 +}; + +// Memory needed for lookup tables of one Huffman tree group. Red, blue, alpha +// and distance alphabets are constant (256 for red, blue and alpha, 40 for +// distance) and lookup table sizes for them in worst case are 630 and 410 +// respectively. Size of green alphabet depends on color cache size and is equal +// to 256 (green component values) + 24 (length prefix values) +// + color_cache_size (between 0 and 2048). +// All values computed for 8-bit first level lookup with Mark Adler's tool: +// http://www.hdfgroup.org/ftp/lib-external/zlib/zlib-1.2.5/examples/enough.c +#define FIXED_TABLE_SIZE (630 * 3 + 410) +static const int kTableSize[12] = { + FIXED_TABLE_SIZE + 654, + FIXED_TABLE_SIZE + 656, + FIXED_TABLE_SIZE + 658, + FIXED_TABLE_SIZE + 662, + FIXED_TABLE_SIZE + 670, + FIXED_TABLE_SIZE + 686, + FIXED_TABLE_SIZE + 718, + FIXED_TABLE_SIZE + 782, + FIXED_TABLE_SIZE + 912, + FIXED_TABLE_SIZE + 1168, + FIXED_TABLE_SIZE + 1680, + FIXED_TABLE_SIZE + 2704 +}; + +static int DecodeImageStream(int xsize, int ysize, + int is_level0, + VP8LDecoder* const dec, + uint32_t** const decoded_data); + +//------------------------------------------------------------------------------ + +int VP8LCheckSignature(const uint8_t* const data, size_t size) { + return (size >= VP8L_FRAME_HEADER_SIZE && + data[0] == VP8L_MAGIC_BYTE && + (data[4] >> 5) == 0); // version +} + +static int ReadImageInfo(VP8LBitReader* const br, + int* const width, int* const height, + int* const has_alpha) { + if (VP8LReadBits(br, 8) != VP8L_MAGIC_BYTE) return 0; + *width = VP8LReadBits(br, VP8L_IMAGE_SIZE_BITS) + 1; + *height = VP8LReadBits(br, VP8L_IMAGE_SIZE_BITS) + 1; + *has_alpha = VP8LReadBits(br, 1); + if (VP8LReadBits(br, VP8L_VERSION_BITS) != 0) return 0; + return !br->eos_; +} + +int VP8LGetInfo(const uint8_t* data, size_t data_size, + int* const width, int* const height, int* const has_alpha) { + if (data == NULL || data_size < VP8L_FRAME_HEADER_SIZE) { + return 0; // not enough data + } else if (!VP8LCheckSignature(data, data_size)) { + return 0; // bad signature + } else { + int w, h, a; + VP8LBitReader br; + VP8LInitBitReader(&br, data, data_size); + if (!ReadImageInfo(&br, &w, &h, &a)) { + return 0; + } + if (width != NULL) *width = w; + if (height != NULL) *height = h; + if (has_alpha != NULL) *has_alpha = a; + return 1; + } +} + +//------------------------------------------------------------------------------ + +static WEBP_INLINE int GetCopyDistance(int distance_symbol, + VP8LBitReader* const br) { + int extra_bits, offset; + if (distance_symbol < 4) { + return distance_symbol + 1; + } + extra_bits = (distance_symbol - 2) >> 1; + offset = (2 + (distance_symbol & 1)) << extra_bits; + return offset + VP8LReadBits(br, extra_bits) + 1; +} + +static WEBP_INLINE int GetCopyLength(int length_symbol, + VP8LBitReader* const br) { + // Length and distance prefixes are encoded the same way. + return GetCopyDistance(length_symbol, br); +} + +static WEBP_INLINE int PlaneCodeToDistance(int xsize, int plane_code) { + if (plane_code > CODE_TO_PLANE_CODES) { + return plane_code - CODE_TO_PLANE_CODES; + } else { + const int dist_code = kCodeToPlane[plane_code - 1]; + const int yoffset = dist_code >> 4; + const int xoffset = 8 - (dist_code & 0xf); + const int dist = yoffset * xsize + xoffset; + return (dist >= 1) ? dist : 1; // dist<1 can happen if xsize is very small + } +} + +//------------------------------------------------------------------------------ +// Decodes the next Huffman code from bit-stream. +// FillBitWindow(br) needs to be called at minimum every second call +// to ReadSymbol, in order to pre-fetch enough bits. +static WEBP_INLINE int ReadSymbol(const HuffmanCode* table, + VP8LBitReader* const br) { + int nbits; + uint32_t val = VP8LPrefetchBits(br); + table += val & HUFFMAN_TABLE_MASK; + nbits = table->bits - HUFFMAN_TABLE_BITS; + if (nbits > 0) { + VP8LSetBitPos(br, br->bit_pos_ + HUFFMAN_TABLE_BITS); + val = VP8LPrefetchBits(br); + table += table->value; + table += val & ((1 << nbits) - 1); + } + VP8LSetBitPos(br, br->bit_pos_ + table->bits); + return table->value; +} + +// Reads packed symbol depending on GREEN channel +#define BITS_SPECIAL_MARKER 0x100 // something large enough (and a bit-mask) +#define PACKED_NON_LITERAL_CODE 0 // must be < NUM_LITERAL_CODES +static WEBP_INLINE int ReadPackedSymbols(const HTreeGroup* group, + VP8LBitReader* const br, + uint32_t* const dst) { + const uint32_t val = VP8LPrefetchBits(br) & (HUFFMAN_PACKED_TABLE_SIZE - 1); + const HuffmanCode32 code = group->packed_table[val]; + assert(group->use_packed_table); + if (code.bits < BITS_SPECIAL_MARKER) { + VP8LSetBitPos(br, br->bit_pos_ + code.bits); + *dst = code.value; + return PACKED_NON_LITERAL_CODE; + } else { + VP8LSetBitPos(br, br->bit_pos_ + code.bits - BITS_SPECIAL_MARKER); + assert(code.value >= NUM_LITERAL_CODES); + return code.value; + } +} + +static int AccumulateHCode(HuffmanCode hcode, int shift, + HuffmanCode32* const huff) { + huff->bits += hcode.bits; + huff->value |= (uint32_t)hcode.value << shift; + assert(huff->bits <= HUFFMAN_TABLE_BITS); + return hcode.bits; +} + +static void BuildPackedTable(HTreeGroup* const htree_group) { + uint32_t code; + for (code = 0; code < HUFFMAN_PACKED_TABLE_SIZE; ++code) { + uint32_t bits = code; + HuffmanCode32* const huff = &htree_group->packed_table[bits]; + HuffmanCode hcode = htree_group->htrees[GREEN][bits]; + if (hcode.value >= NUM_LITERAL_CODES) { + huff->bits = hcode.bits + BITS_SPECIAL_MARKER; + huff->value = hcode.value; + } else { + huff->bits = 0; + huff->value = 0; + bits >>= AccumulateHCode(hcode, 8, huff); + bits >>= AccumulateHCode(htree_group->htrees[RED][bits], 16, huff); + bits >>= AccumulateHCode(htree_group->htrees[BLUE][bits], 0, huff); + bits >>= AccumulateHCode(htree_group->htrees[ALPHA][bits], 24, huff); + (void)bits; + } + } +} + +static int ReadHuffmanCodeLengths( + VP8LDecoder* const dec, const int* const code_length_code_lengths, + int num_symbols, int* const code_lengths) { + int ok = 0; + VP8LBitReader* const br = &dec->br_; + int symbol; + int max_symbol; + int prev_code_len = DEFAULT_CODE_LENGTH; + HuffmanCode table[1 << LENGTHS_TABLE_BITS]; + + if (!VP8LBuildHuffmanTable(table, LENGTHS_TABLE_BITS, + code_length_code_lengths, + NUM_CODE_LENGTH_CODES)) { + goto End; + } + + if (VP8LReadBits(br, 1)) { // use length + const int length_nbits = 2 + 2 * VP8LReadBits(br, 3); + max_symbol = 2 + VP8LReadBits(br, length_nbits); + if (max_symbol > num_symbols) { + goto End; + } + } else { + max_symbol = num_symbols; + } + + symbol = 0; + while (symbol < num_symbols) { + const HuffmanCode* p; + int code_len; + if (max_symbol-- == 0) break; + VP8LFillBitWindow(br); + p = &table[VP8LPrefetchBits(br) & LENGTHS_TABLE_MASK]; + VP8LSetBitPos(br, br->bit_pos_ + p->bits); + code_len = p->value; + if (code_len < kCodeLengthLiterals) { + code_lengths[symbol++] = code_len; + if (code_len != 0) prev_code_len = code_len; + } else { + const int use_prev = (code_len == kCodeLengthRepeatCode); + const int slot = code_len - kCodeLengthLiterals; + const int extra_bits = kCodeLengthExtraBits[slot]; + const int repeat_offset = kCodeLengthRepeatOffsets[slot]; + int repeat = VP8LReadBits(br, extra_bits) + repeat_offset; + if (symbol + repeat > num_symbols) { + goto End; + } else { + const int length = use_prev ? prev_code_len : 0; + while (repeat-- > 0) code_lengths[symbol++] = length; + } + } + } + ok = 1; + + End: + if (!ok) dec->status_ = VP8_STATUS_BITSTREAM_ERROR; + return ok; +} + +// 'code_lengths' is pre-allocated temporary buffer, used for creating Huffman +// tree. +static int ReadHuffmanCode(int alphabet_size, VP8LDecoder* const dec, + int* const code_lengths, HuffmanCode* const table) { + int ok = 0; + int size = 0; + VP8LBitReader* const br = &dec->br_; + const int simple_code = VP8LReadBits(br, 1); + + memset(code_lengths, 0, alphabet_size * sizeof(*code_lengths)); + + if (simple_code) { // Read symbols, codes & code lengths directly. + const int num_symbols = VP8LReadBits(br, 1) + 1; + const int first_symbol_len_code = VP8LReadBits(br, 1); + // The first code is either 1 bit or 8 bit code. + int symbol = VP8LReadBits(br, (first_symbol_len_code == 0) ? 1 : 8); + code_lengths[symbol] = 1; + // The second code (if present), is always 8 bit long. + if (num_symbols == 2) { + symbol = VP8LReadBits(br, 8); + code_lengths[symbol] = 1; + } + ok = 1; + } else { // Decode Huffman-coded code lengths. + int i; + int code_length_code_lengths[NUM_CODE_LENGTH_CODES] = { 0 }; + const int num_codes = VP8LReadBits(br, 4) + 4; + if (num_codes > NUM_CODE_LENGTH_CODES) { + dec->status_ = VP8_STATUS_BITSTREAM_ERROR; + return 0; + } + + for (i = 0; i < num_codes; ++i) { + code_length_code_lengths[kCodeLengthCodeOrder[i]] = VP8LReadBits(br, 3); + } + ok = ReadHuffmanCodeLengths(dec, code_length_code_lengths, alphabet_size, + code_lengths); + } + + ok = ok && !br->eos_; + if (ok) { + size = VP8LBuildHuffmanTable(table, HUFFMAN_TABLE_BITS, + code_lengths, alphabet_size); + } + if (!ok || size == 0) { + dec->status_ = VP8_STATUS_BITSTREAM_ERROR; + return 0; + } + return size; +} + +static int ReadHuffmanCodes(VP8LDecoder* const dec, int xsize, int ysize, + int color_cache_bits, int allow_recursion) { + int i, j; + VP8LBitReader* const br = &dec->br_; + VP8LMetadata* const hdr = &dec->hdr_; + uint32_t* huffman_image = NULL; + HTreeGroup* htree_groups = NULL; + HuffmanCode* huffman_tables = NULL; + HuffmanCode* next = NULL; + int num_htree_groups = 1; + int max_alphabet_size = 0; + int* code_lengths = NULL; + const int table_size = kTableSize[color_cache_bits]; + + if (allow_recursion && VP8LReadBits(br, 1)) { + // use meta Huffman codes. + const int huffman_precision = VP8LReadBits(br, 3) + 2; + const int huffman_xsize = VP8LSubSampleSize(xsize, huffman_precision); + const int huffman_ysize = VP8LSubSampleSize(ysize, huffman_precision); + const int huffman_pixs = huffman_xsize * huffman_ysize; + if (!DecodeImageStream(huffman_xsize, huffman_ysize, 0, dec, + &huffman_image)) { + goto Error; + } + hdr->huffman_subsample_bits_ = huffman_precision; + for (i = 0; i < huffman_pixs; ++i) { + // The huffman data is stored in red and green bytes. + const int group = (huffman_image[i] >> 8) & 0xffff; + huffman_image[i] = group; + if (group >= num_htree_groups) { + num_htree_groups = group + 1; + } + } + } + + if (br->eos_) goto Error; + + // Find maximum alphabet size for the htree group. + for (j = 0; j < HUFFMAN_CODES_PER_META_CODE; ++j) { + int alphabet_size = kAlphabetSize[j]; + if (j == 0 && color_cache_bits > 0) { + alphabet_size += 1 << color_cache_bits; + } + if (max_alphabet_size < alphabet_size) { + max_alphabet_size = alphabet_size; + } + } + + huffman_tables = (HuffmanCode*)WebPSafeMalloc(num_htree_groups * table_size, + sizeof(*huffman_tables)); + htree_groups = VP8LHtreeGroupsNew(num_htree_groups); + code_lengths = (int*)WebPSafeCalloc((uint64_t)max_alphabet_size, + sizeof(*code_lengths)); + + if (htree_groups == NULL || code_lengths == NULL || huffman_tables == NULL) { + dec->status_ = VP8_STATUS_OUT_OF_MEMORY; + goto Error; + } + + next = huffman_tables; + for (i = 0; i < num_htree_groups; ++i) { + HTreeGroup* const htree_group = &htree_groups[i]; + HuffmanCode** const htrees = htree_group->htrees; + int size; + int total_size = 0; + int is_trivial_literal = 1; + int max_bits = 0; + for (j = 0; j < HUFFMAN_CODES_PER_META_CODE; ++j) { + int alphabet_size = kAlphabetSize[j]; + htrees[j] = next; + if (j == 0 && color_cache_bits > 0) { + alphabet_size += 1 << color_cache_bits; + } + size = ReadHuffmanCode(alphabet_size, dec, code_lengths, next); + if (size == 0) { + goto Error; + } + if (is_trivial_literal && kLiteralMap[j] == 1) { + is_trivial_literal = (next->bits == 0); + } + total_size += next->bits; + next += size; + if (j <= ALPHA) { + int local_max_bits = code_lengths[0]; + int k; + for (k = 1; k < alphabet_size; ++k) { + if (code_lengths[k] > local_max_bits) { + local_max_bits = code_lengths[k]; + } + } + max_bits += local_max_bits; + } + } + htree_group->is_trivial_literal = is_trivial_literal; + htree_group->is_trivial_code = 0; + if (is_trivial_literal) { + const int red = htrees[RED][0].value; + const int blue = htrees[BLUE][0].value; + const int alpha = htrees[ALPHA][0].value; + htree_group->literal_arb = + ((uint32_t)alpha << 24) | (red << 16) | blue; + if (total_size == 0 && htrees[GREEN][0].value < NUM_LITERAL_CODES) { + htree_group->is_trivial_code = 1; + htree_group->literal_arb |= htrees[GREEN][0].value << 8; + } + } + htree_group->use_packed_table = !htree_group->is_trivial_code && + (max_bits < HUFFMAN_PACKED_BITS); + if (htree_group->use_packed_table) BuildPackedTable(htree_group); + } + WebPSafeFree(code_lengths); + + // All OK. Finalize pointers and return. + hdr->huffman_image_ = huffman_image; + hdr->num_htree_groups_ = num_htree_groups; + hdr->htree_groups_ = htree_groups; + hdr->huffman_tables_ = huffman_tables; + return 1; + + Error: + WebPSafeFree(code_lengths); + WebPSafeFree(huffman_image); + WebPSafeFree(huffman_tables); + VP8LHtreeGroupsFree(htree_groups); + return 0; +} + +//------------------------------------------------------------------------------ +// Scaling. + +static int AllocateAndInitRescaler(VP8LDecoder* const dec, VP8Io* const io) { + const int num_channels = 4; + const int in_width = io->mb_w; + const int out_width = io->scaled_width; + const int in_height = io->mb_h; + const int out_height = io->scaled_height; + const uint64_t work_size = 2 * num_channels * (uint64_t)out_width; + rescaler_t* work; // Rescaler work area. + const uint64_t scaled_data_size = (uint64_t)out_width; + uint32_t* scaled_data; // Temporary storage for scaled BGRA data. + const uint64_t memory_size = sizeof(*dec->rescaler) + + work_size * sizeof(*work) + + scaled_data_size * sizeof(*scaled_data); + uint8_t* memory = (uint8_t*)WebPSafeMalloc(memory_size, sizeof(*memory)); + if (memory == NULL) { + dec->status_ = VP8_STATUS_OUT_OF_MEMORY; + return 0; + } + assert(dec->rescaler_memory == NULL); + dec->rescaler_memory = memory; + + dec->rescaler = (WebPRescaler*)memory; + memory += sizeof(*dec->rescaler); + work = (rescaler_t*)memory; + memory += work_size * sizeof(*work); + scaled_data = (uint32_t*)memory; + + WebPRescalerInit(dec->rescaler, in_width, in_height, (uint8_t*)scaled_data, + out_width, out_height, 0, num_channels, work); + return 1; +} + +//------------------------------------------------------------------------------ +// Export to ARGB + +// We have special "export" function since we need to convert from BGRA +static int Export(WebPRescaler* const rescaler, WEBP_CSP_MODE colorspace, + int rgba_stride, uint8_t* const rgba) { + uint32_t* const src = (uint32_t*)rescaler->dst; + const int dst_width = rescaler->dst_width; + int num_lines_out = 0; + while (WebPRescalerHasPendingOutput(rescaler)) { + uint8_t* const dst = rgba + num_lines_out * rgba_stride; + WebPRescalerExportRow(rescaler); + WebPMultARGBRow(src, dst_width, 1); + VP8LConvertFromBGRA(src, dst_width, colorspace, dst); + ++num_lines_out; + } + return num_lines_out; +} + +// Emit scaled rows. +static int EmitRescaledRowsRGBA(const VP8LDecoder* const dec, + uint8_t* in, int in_stride, int mb_h, + uint8_t* const out, int out_stride) { + const WEBP_CSP_MODE colorspace = dec->output_->colorspace; + int num_lines_in = 0; + int num_lines_out = 0; + while (num_lines_in < mb_h) { + uint8_t* const row_in = in + num_lines_in * in_stride; + uint8_t* const row_out = out + num_lines_out * out_stride; + const int lines_left = mb_h - num_lines_in; + const int needed_lines = WebPRescaleNeededLines(dec->rescaler, lines_left); + int lines_imported; + assert(needed_lines > 0 && needed_lines <= lines_left); + WebPMultARGBRows(row_in, in_stride, + dec->rescaler->src_width, needed_lines, 0); + lines_imported = + WebPRescalerImport(dec->rescaler, lines_left, row_in, in_stride); + assert(lines_imported == needed_lines); + num_lines_in += lines_imported; + num_lines_out += Export(dec->rescaler, colorspace, out_stride, row_out); + } + return num_lines_out; +} + +// Emit rows without any scaling. +static int EmitRows(WEBP_CSP_MODE colorspace, + const uint8_t* row_in, int in_stride, + int mb_w, int mb_h, + uint8_t* const out, int out_stride) { + int lines = mb_h; + uint8_t* row_out = out; + while (lines-- > 0) { + VP8LConvertFromBGRA((const uint32_t*)row_in, mb_w, colorspace, row_out); + row_in += in_stride; + row_out += out_stride; + } + return mb_h; // Num rows out == num rows in. +} + +//------------------------------------------------------------------------------ +// Export to YUVA + +static void ConvertToYUVA(const uint32_t* const src, int width, int y_pos, + const WebPDecBuffer* const output) { + const WebPYUVABuffer* const buf = &output->u.YUVA; + + // first, the luma plane + WebPConvertARGBToY(src, buf->y + y_pos * buf->y_stride, width); + + // then U/V planes + { + uint8_t* const u = buf->u + (y_pos >> 1) * buf->u_stride; + uint8_t* const v = buf->v + (y_pos >> 1) * buf->v_stride; + // even lines: store values + // odd lines: average with previous values + WebPConvertARGBToUV(src, u, v, width, !(y_pos & 1)); + } + // Lastly, store alpha if needed. + if (buf->a != NULL) { + uint8_t* const a = buf->a + y_pos * buf->a_stride; +#if defined(WORDS_BIGENDIAN) + WebPExtractAlpha((uint8_t*)src + 0, 0, width, 1, a, 0); +#else + WebPExtractAlpha((uint8_t*)src + 3, 0, width, 1, a, 0); +#endif + } +} + +static int ExportYUVA(const VP8LDecoder* const dec, int y_pos) { + WebPRescaler* const rescaler = dec->rescaler; + uint32_t* const src = (uint32_t*)rescaler->dst; + const int dst_width = rescaler->dst_width; + int num_lines_out = 0; + while (WebPRescalerHasPendingOutput(rescaler)) { + WebPRescalerExportRow(rescaler); + WebPMultARGBRow(src, dst_width, 1); + ConvertToYUVA(src, dst_width, y_pos, dec->output_); + ++y_pos; + ++num_lines_out; + } + return num_lines_out; +} + +static int EmitRescaledRowsYUVA(const VP8LDecoder* const dec, + uint8_t* in, int in_stride, int mb_h) { + int num_lines_in = 0; + int y_pos = dec->last_out_row_; + while (num_lines_in < mb_h) { + const int lines_left = mb_h - num_lines_in; + const int needed_lines = WebPRescaleNeededLines(dec->rescaler, lines_left); + int lines_imported; + WebPMultARGBRows(in, in_stride, dec->rescaler->src_width, needed_lines, 0); + lines_imported = + WebPRescalerImport(dec->rescaler, lines_left, in, in_stride); + assert(lines_imported == needed_lines); + num_lines_in += lines_imported; + in += needed_lines * in_stride; + y_pos += ExportYUVA(dec, y_pos); + } + return y_pos; +} + +static int EmitRowsYUVA(const VP8LDecoder* const dec, + const uint8_t* in, int in_stride, + int mb_w, int num_rows) { + int y_pos = dec->last_out_row_; + while (num_rows-- > 0) { + ConvertToYUVA((const uint32_t*)in, mb_w, y_pos, dec->output_); + in += in_stride; + ++y_pos; + } + return y_pos; +} + +//------------------------------------------------------------------------------ +// Cropping. + +// Sets io->mb_y, io->mb_h & io->mb_w according to start row, end row and +// crop options. Also updates the input data pointer, so that it points to the +// start of the cropped window. Note that pixels are in ARGB format even if +// 'in_data' is uint8_t*. +// Returns true if the crop window is not empty. +static int SetCropWindow(VP8Io* const io, int y_start, int y_end, + uint8_t** const in_data, int pixel_stride) { + assert(y_start < y_end); + assert(io->crop_left < io->crop_right); + if (y_end > io->crop_bottom) { + y_end = io->crop_bottom; // make sure we don't overflow on last row. + } + if (y_start < io->crop_top) { + const int delta = io->crop_top - y_start; + y_start = io->crop_top; + *in_data += delta * pixel_stride; + } + if (y_start >= y_end) return 0; // Crop window is empty. + + *in_data += io->crop_left * sizeof(uint32_t); + + io->mb_y = y_start - io->crop_top; + io->mb_w = io->crop_right - io->crop_left; + io->mb_h = y_end - y_start; + return 1; // Non-empty crop window. +} + +//------------------------------------------------------------------------------ + +static WEBP_INLINE int GetMetaIndex( + const uint32_t* const image, int xsize, int bits, int x, int y) { + if (bits == 0) return 0; + return image[xsize * (y >> bits) + (x >> bits)]; +} + +static WEBP_INLINE HTreeGroup* GetHtreeGroupForPos(VP8LMetadata* const hdr, + int x, int y) { + const int meta_index = GetMetaIndex(hdr->huffman_image_, hdr->huffman_xsize_, + hdr->huffman_subsample_bits_, x, y); + assert(meta_index < hdr->num_htree_groups_); + return hdr->htree_groups_ + meta_index; +} + +//------------------------------------------------------------------------------ +// Main loop, with custom row-processing function + +typedef void (*ProcessRowsFunc)(VP8LDecoder* const dec, int row); + +static void ApplyInverseTransforms(VP8LDecoder* const dec, int num_rows, + const uint32_t* const rows) { + int n = dec->next_transform_; + const int cache_pixs = dec->width_ * num_rows; + const int start_row = dec->last_row_; + const int end_row = start_row + num_rows; + const uint32_t* rows_in = rows; + uint32_t* const rows_out = dec->argb_cache_; + + // Inverse transforms. + while (n-- > 0) { + VP8LTransform* const transform = &dec->transforms_[n]; + VP8LInverseTransform(transform, start_row, end_row, rows_in, rows_out); + rows_in = rows_out; + } + if (rows_in != rows_out) { + // No transform called, hence just copy. + memcpy(rows_out, rows_in, cache_pixs * sizeof(*rows_out)); + } +} + +// Processes (transforms, scales & color-converts) the rows decoded after the +// last call. +static void ProcessRows(VP8LDecoder* const dec, int row) { + const uint32_t* const rows = dec->pixels_ + dec->width_ * dec->last_row_; + const int num_rows = row - dec->last_row_; + + assert(row <= dec->io_->crop_bottom); + // We can't process more than NUM_ARGB_CACHE_ROWS at a time (that's the size + // of argb_cache_), but we currently don't need more than that. + assert(num_rows <= NUM_ARGB_CACHE_ROWS); + if (num_rows > 0) { // Emit output. + VP8Io* const io = dec->io_; + uint8_t* rows_data = (uint8_t*)dec->argb_cache_; + const int in_stride = io->width * sizeof(uint32_t); // in unit of RGBA + + ApplyInverseTransforms(dec, num_rows, rows); + if (!SetCropWindow(io, dec->last_row_, row, &rows_data, in_stride)) { + // Nothing to output (this time). + } else { + const WebPDecBuffer* const output = dec->output_; + if (WebPIsRGBMode(output->colorspace)) { // convert to RGBA + const WebPRGBABuffer* const buf = &output->u.RGBA; + uint8_t* const rgba = buf->rgba + dec->last_out_row_ * buf->stride; + const int num_rows_out = io->use_scaling ? + EmitRescaledRowsRGBA(dec, rows_data, in_stride, io->mb_h, + rgba, buf->stride) : + EmitRows(output->colorspace, rows_data, in_stride, + io->mb_w, io->mb_h, rgba, buf->stride); + // Update 'last_out_row_'. + dec->last_out_row_ += num_rows_out; + } else { // convert to YUVA + dec->last_out_row_ = io->use_scaling ? + EmitRescaledRowsYUVA(dec, rows_data, in_stride, io->mb_h) : + EmitRowsYUVA(dec, rows_data, in_stride, io->mb_w, io->mb_h); + } + assert(dec->last_out_row_ <= output->height); + } + } + + // Update 'last_row_'. + dec->last_row_ = row; + assert(dec->last_row_ <= dec->height_); +} + +// Row-processing for the special case when alpha data contains only one +// transform (color indexing), and trivial non-green literals. +static int Is8bOptimizable(const VP8LMetadata* const hdr) { + int i; + if (hdr->color_cache_size_ > 0) return 0; + // When the Huffman tree contains only one symbol, we can skip the + // call to ReadSymbol() for red/blue/alpha channels. + for (i = 0; i < hdr->num_htree_groups_; ++i) { + HuffmanCode** const htrees = hdr->htree_groups_[i].htrees; + if (htrees[RED][0].bits > 0) return 0; + if (htrees[BLUE][0].bits > 0) return 0; + if (htrees[ALPHA][0].bits > 0) return 0; + } + return 1; +} + +static void AlphaApplyFilter(ALPHDecoder* const alph_dec, + int first_row, int last_row, + uint8_t* out, int stride) { + if (alph_dec->filter_ != WEBP_FILTER_NONE) { + int y; + const uint8_t* prev_line = alph_dec->prev_line_; + assert(WebPUnfilters[alph_dec->filter_] != NULL); + for (y = first_row; y < last_row; ++y) { + WebPUnfilters[alph_dec->filter_](prev_line, out, out, stride); + prev_line = out; + out += stride; + } + alph_dec->prev_line_ = prev_line; + } +} + +static void ExtractPalettedAlphaRows(VP8LDecoder* const dec, int last_row) { + // For vertical and gradient filtering, we need to decode the part above the + // crop_top row, in order to have the correct spatial predictors. + ALPHDecoder* const alph_dec = (ALPHDecoder*)dec->io_->opaque; + const int top_row = + (alph_dec->filter_ == WEBP_FILTER_NONE || + alph_dec->filter_ == WEBP_FILTER_HORIZONTAL) ? dec->io_->crop_top + : dec->last_row_; + const int first_row = (dec->last_row_ < top_row) ? top_row : dec->last_row_; + assert(last_row <= dec->io_->crop_bottom); + if (last_row > first_row) { + // Special method for paletted alpha data. We only process the cropped area. + const int width = dec->io_->width; + uint8_t* out = alph_dec->output_ + width * first_row; + const uint8_t* const in = + (uint8_t*)dec->pixels_ + dec->width_ * first_row; + VP8LTransform* const transform = &dec->transforms_[0]; + assert(dec->next_transform_ == 1); + assert(transform->type_ == COLOR_INDEXING_TRANSFORM); + VP8LColorIndexInverseTransformAlpha(transform, first_row, last_row, + in, out); + AlphaApplyFilter(alph_dec, first_row, last_row, out, width); + } + dec->last_row_ = dec->last_out_row_ = last_row; +} + +//------------------------------------------------------------------------------ +// Helper functions for fast pattern copy (8b and 32b) + +// cyclic rotation of pattern word +static WEBP_INLINE uint32_t Rotate8b(uint32_t V) { +#if defined(WORDS_BIGENDIAN) + return ((V & 0xff000000u) >> 24) | (V << 8); +#else + return ((V & 0xffu) << 24) | (V >> 8); +#endif +} + +// copy 1, 2 or 4-bytes pattern +static WEBP_INLINE void CopySmallPattern8b(const uint8_t* src, uint8_t* dst, + int length, uint32_t pattern) { + int i; + // align 'dst' to 4-bytes boundary. Adjust the pattern along the way. + while ((uintptr_t)dst & 3) { + *dst++ = *src++; + pattern = Rotate8b(pattern); + --length; + } + // Copy the pattern 4 bytes at a time. + for (i = 0; i < (length >> 2); ++i) { + ((uint32_t*)dst)[i] = pattern; + } + // Finish with left-overs. 'pattern' is still correctly positioned, + // so no Rotate8b() call is needed. + for (i <<= 2; i < length; ++i) { + dst[i] = src[i]; + } +} + +static WEBP_INLINE void CopyBlock8b(uint8_t* const dst, int dist, int length) { + const uint8_t* src = dst - dist; + if (length >= 8) { + uint32_t pattern = 0; + switch (dist) { + case 1: + pattern = src[0]; +#if defined(__arm__) || defined(_M_ARM) // arm doesn't like multiply that much + pattern |= pattern << 8; + pattern |= pattern << 16; +#elif defined(WEBP_USE_MIPS_DSP_R2) + __asm__ volatile ("replv.qb %0, %0" : "+r"(pattern)); +#else + pattern = 0x01010101u * pattern; +#endif + break; + case 2: + memcpy(&pattern, src, sizeof(uint16_t)); +#if defined(__arm__) || defined(_M_ARM) + pattern |= pattern << 16; +#elif defined(WEBP_USE_MIPS_DSP_R2) + __asm__ volatile ("replv.ph %0, %0" : "+r"(pattern)); +#else + pattern = 0x00010001u * pattern; +#endif + break; + case 4: + memcpy(&pattern, src, sizeof(uint32_t)); + break; + default: + goto Copy; + break; + } + CopySmallPattern8b(src, dst, length, pattern); + return; + } + Copy: + if (dist >= length) { // no overlap -> use memcpy() + memcpy(dst, src, length * sizeof(*dst)); + } else { + int i; + for (i = 0; i < length; ++i) dst[i] = src[i]; + } +} + +// copy pattern of 1 or 2 uint32_t's +static WEBP_INLINE void CopySmallPattern32b(const uint32_t* src, + uint32_t* dst, + int length, uint64_t pattern) { + int i; + if ((uintptr_t)dst & 4) { // Align 'dst' to 8-bytes boundary. + *dst++ = *src++; + pattern = (pattern >> 32) | (pattern << 32); + --length; + } + assert(0 == ((uintptr_t)dst & 7)); + for (i = 0; i < (length >> 1); ++i) { + ((uint64_t*)dst)[i] = pattern; // Copy the pattern 8 bytes at a time. + } + if (length & 1) { // Finish with left-over. + dst[i << 1] = src[i << 1]; + } +} + +static WEBP_INLINE void CopyBlock32b(uint32_t* const dst, + int dist, int length) { + const uint32_t* const src = dst - dist; + if (dist <= 2 && length >= 4 && ((uintptr_t)dst & 3) == 0) { + uint64_t pattern; + if (dist == 1) { + pattern = (uint64_t)src[0]; + pattern |= pattern << 32; + } else { + memcpy(&pattern, src, sizeof(pattern)); + } + CopySmallPattern32b(src, dst, length, pattern); + } else if (dist >= length) { // no overlap + memcpy(dst, src, length * sizeof(*dst)); + } else { + int i; + for (i = 0; i < length; ++i) dst[i] = src[i]; + } +} + +//------------------------------------------------------------------------------ + +static int DecodeAlphaData(VP8LDecoder* const dec, uint8_t* const data, + int width, int height, int last_row) { + int ok = 1; + int row = dec->last_pixel_ / width; + int col = dec->last_pixel_ % width; + VP8LBitReader* const br = &dec->br_; + VP8LMetadata* const hdr = &dec->hdr_; + int pos = dec->last_pixel_; // current position + const int end = width * height; // End of data + const int last = width * last_row; // Last pixel to decode + const int len_code_limit = NUM_LITERAL_CODES + NUM_LENGTH_CODES; + const int mask = hdr->huffman_mask_; + const HTreeGroup* htree_group = + (pos < last) ? GetHtreeGroupForPos(hdr, col, row) : NULL; + assert(pos <= end); + assert(last_row <= height); + assert(Is8bOptimizable(hdr)); + + while (!br->eos_ && pos < last) { + int code; + // Only update when changing tile. + if ((col & mask) == 0) { + htree_group = GetHtreeGroupForPos(hdr, col, row); + } + assert(htree_group != NULL); + VP8LFillBitWindow(br); + code = ReadSymbol(htree_group->htrees[GREEN], br); + if (code < NUM_LITERAL_CODES) { // Literal + data[pos] = code; + ++pos; + ++col; + if (col >= width) { + col = 0; + ++row; + if (row <= last_row && (row % NUM_ARGB_CACHE_ROWS == 0)) { + ExtractPalettedAlphaRows(dec, row); + } + } + } else if (code < len_code_limit) { // Backward reference + int dist_code, dist; + const int length_sym = code - NUM_LITERAL_CODES; + const int length = GetCopyLength(length_sym, br); + const int dist_symbol = ReadSymbol(htree_group->htrees[DIST], br); + VP8LFillBitWindow(br); + dist_code = GetCopyDistance(dist_symbol, br); + dist = PlaneCodeToDistance(width, dist_code); + if (pos >= dist && end - pos >= length) { + CopyBlock8b(data + pos, dist, length); + } else { + ok = 0; + goto End; + } + pos += length; + col += length; + while (col >= width) { + col -= width; + ++row; + if (row <= last_row && (row % NUM_ARGB_CACHE_ROWS == 0)) { + ExtractPalettedAlphaRows(dec, row); + } + } + if (pos < last && (col & mask)) { + htree_group = GetHtreeGroupForPos(hdr, col, row); + } + } else { // Not reached + ok = 0; + goto End; + } + assert(br->eos_ == VP8LIsEndOfStream(br)); + } + // Process the remaining rows corresponding to last row-block. + ExtractPalettedAlphaRows(dec, row > last_row ? last_row : row); + + End: + if (!ok || (br->eos_ && pos < end)) { + ok = 0; + dec->status_ = br->eos_ ? VP8_STATUS_SUSPENDED + : VP8_STATUS_BITSTREAM_ERROR; + } else { + dec->last_pixel_ = pos; + } + return ok; +} + +static void SaveState(VP8LDecoder* const dec, int last_pixel) { + assert(dec->incremental_); + dec->saved_br_ = dec->br_; + dec->saved_last_pixel_ = last_pixel; + if (dec->hdr_.color_cache_size_ > 0) { + VP8LColorCacheCopy(&dec->hdr_.color_cache_, &dec->hdr_.saved_color_cache_); + } +} + +static void RestoreState(VP8LDecoder* const dec) { + assert(dec->br_.eos_); + dec->status_ = VP8_STATUS_SUSPENDED; + dec->br_ = dec->saved_br_; + dec->last_pixel_ = dec->saved_last_pixel_; + if (dec->hdr_.color_cache_size_ > 0) { + VP8LColorCacheCopy(&dec->hdr_.saved_color_cache_, &dec->hdr_.color_cache_); + } +} + +#define SYNC_EVERY_N_ROWS 8 // minimum number of rows between check-points +static int DecodeImageData(VP8LDecoder* const dec, uint32_t* const data, + int width, int height, int last_row, + ProcessRowsFunc process_func) { + int row = dec->last_pixel_ / width; + int col = dec->last_pixel_ % width; + VP8LBitReader* const br = &dec->br_; + VP8LMetadata* const hdr = &dec->hdr_; + uint32_t* src = data + dec->last_pixel_; + uint32_t* last_cached = src; + uint32_t* const src_end = data + width * height; // End of data + uint32_t* const src_last = data + width * last_row; // Last pixel to decode + const int len_code_limit = NUM_LITERAL_CODES + NUM_LENGTH_CODES; + const int color_cache_limit = len_code_limit + hdr->color_cache_size_; + int next_sync_row = dec->incremental_ ? row : 1 << 24; + VP8LColorCache* const color_cache = + (hdr->color_cache_size_ > 0) ? &hdr->color_cache_ : NULL; + const int mask = hdr->huffman_mask_; + const HTreeGroup* htree_group = + (src < src_last) ? GetHtreeGroupForPos(hdr, col, row) : NULL; + assert(dec->last_row_ < last_row); + assert(src_last <= src_end); + + while (src < src_last) { + int code; + if (row >= next_sync_row) { + SaveState(dec, (int)(src - data)); + next_sync_row = row + SYNC_EVERY_N_ROWS; + } + // Only update when changing tile. Note we could use this test: + // if "((((prev_col ^ col) | prev_row ^ row)) > mask)" -> tile changed + // but that's actually slower and needs storing the previous col/row. + if ((col & mask) == 0) { + htree_group = GetHtreeGroupForPos(hdr, col, row); + } + assert(htree_group != NULL); + if (htree_group->is_trivial_code) { + *src = htree_group->literal_arb; + goto AdvanceByOne; + } + VP8LFillBitWindow(br); + if (htree_group->use_packed_table) { + code = ReadPackedSymbols(htree_group, br, src); + if (code == PACKED_NON_LITERAL_CODE) goto AdvanceByOne; + } else { + code = ReadSymbol(htree_group->htrees[GREEN], br); + } + if (br->eos_) break; // early out + if (code < NUM_LITERAL_CODES) { // Literal + if (htree_group->is_trivial_literal) { + *src = htree_group->literal_arb | (code << 8); + } else { + int red, blue, alpha; + red = ReadSymbol(htree_group->htrees[RED], br); + VP8LFillBitWindow(br); + blue = ReadSymbol(htree_group->htrees[BLUE], br); + alpha = ReadSymbol(htree_group->htrees[ALPHA], br); + if (br->eos_) break; + *src = ((uint32_t)alpha << 24) | (red << 16) | (code << 8) | blue; + } + AdvanceByOne: + ++src; + ++col; + if (col >= width) { + col = 0; + ++row; + if (process_func != NULL) { + if (row <= last_row && (row % NUM_ARGB_CACHE_ROWS == 0)) { + process_func(dec, row); + } + } + if (color_cache != NULL) { + while (last_cached < src) { + VP8LColorCacheInsert(color_cache, *last_cached++); + } + } + } + } else if (code < len_code_limit) { // Backward reference + int dist_code, dist; + const int length_sym = code - NUM_LITERAL_CODES; + const int length = GetCopyLength(length_sym, br); + const int dist_symbol = ReadSymbol(htree_group->htrees[DIST], br); + VP8LFillBitWindow(br); + dist_code = GetCopyDistance(dist_symbol, br); + dist = PlaneCodeToDistance(width, dist_code); + if (br->eos_) break; + if (src - data < (ptrdiff_t)dist || src_end - src < (ptrdiff_t)length) { + goto Error; + } else { + CopyBlock32b(src, dist, length); + } + src += length; + col += length; + while (col >= width) { + col -= width; + ++row; + if (process_func != NULL) { + if (row <= last_row && (row % NUM_ARGB_CACHE_ROWS == 0)) { + process_func(dec, row); + } + } + } + // Because of the check done above (before 'src' was incremented by + // 'length'), the following holds true. + assert(src <= src_end); + if (col & mask) htree_group = GetHtreeGroupForPos(hdr, col, row); + if (color_cache != NULL) { + while (last_cached < src) { + VP8LColorCacheInsert(color_cache, *last_cached++); + } + } + } else if (code < color_cache_limit) { // Color cache + const int key = code - len_code_limit; + assert(color_cache != NULL); + while (last_cached < src) { + VP8LColorCacheInsert(color_cache, *last_cached++); + } + *src = VP8LColorCacheLookup(color_cache, key); + goto AdvanceByOne; + } else { // Not reached + goto Error; + } + assert(br->eos_ == VP8LIsEndOfStream(br)); + } + + if (dec->incremental_ && br->eos_ && src < src_end) { + RestoreState(dec); + } else if (!br->eos_) { + // Process the remaining rows corresponding to last row-block. + if (process_func != NULL) { + process_func(dec, row > last_row ? last_row : row); + } + dec->status_ = VP8_STATUS_OK; + dec->last_pixel_ = (int)(src - data); // end-of-scan marker + } else { + // if not incremental, and we are past the end of buffer (eos_=1), then this + // is a real bitstream error. + goto Error; + } + return 1; + + Error: + dec->status_ = VP8_STATUS_BITSTREAM_ERROR; + return 0; +} + +// ----------------------------------------------------------------------------- +// VP8LTransform + +static void ClearTransform(VP8LTransform* const transform) { + WebPSafeFree(transform->data_); + transform->data_ = NULL; +} + +// For security reason, we need to remap the color map to span +// the total possible bundled values, and not just the num_colors. +static int ExpandColorMap(int num_colors, VP8LTransform* const transform) { + int i; + const int final_num_colors = 1 << (8 >> transform->bits_); + uint32_t* const new_color_map = + (uint32_t*)WebPSafeMalloc((uint64_t)final_num_colors, + sizeof(*new_color_map)); + if (new_color_map == NULL) { + return 0; + } else { + uint8_t* const data = (uint8_t*)transform->data_; + uint8_t* const new_data = (uint8_t*)new_color_map; + new_color_map[0] = transform->data_[0]; + for (i = 4; i < 4 * num_colors; ++i) { + // Equivalent to AddPixelEq(), on a byte-basis. + new_data[i] = (data[i] + new_data[i - 4]) & 0xff; + } + for (; i < 4 * final_num_colors; ++i) { + new_data[i] = 0; // black tail. + } + WebPSafeFree(transform->data_); + transform->data_ = new_color_map; + } + return 1; +} + +static int ReadTransform(int* const xsize, int const* ysize, + VP8LDecoder* const dec) { + int ok = 1; + VP8LBitReader* const br = &dec->br_; + VP8LTransform* transform = &dec->transforms_[dec->next_transform_]; + const VP8LImageTransformType type = + (VP8LImageTransformType)VP8LReadBits(br, 2); + + // Each transform type can only be present once in the stream. + if (dec->transforms_seen_ & (1U << type)) { + return 0; // Already there, let's not accept the second same transform. + } + dec->transforms_seen_ |= (1U << type); + + transform->type_ = type; + transform->xsize_ = *xsize; + transform->ysize_ = *ysize; + transform->data_ = NULL; + ++dec->next_transform_; + assert(dec->next_transform_ <= NUM_TRANSFORMS); + + switch (type) { + case PREDICTOR_TRANSFORM: + case CROSS_COLOR_TRANSFORM: + transform->bits_ = VP8LReadBits(br, 3) + 2; + ok = DecodeImageStream(VP8LSubSampleSize(transform->xsize_, + transform->bits_), + VP8LSubSampleSize(transform->ysize_, + transform->bits_), + 0, dec, &transform->data_); + break; + case COLOR_INDEXING_TRANSFORM: { + const int num_colors = VP8LReadBits(br, 8) + 1; + const int bits = (num_colors > 16) ? 0 + : (num_colors > 4) ? 1 + : (num_colors > 2) ? 2 + : 3; + *xsize = VP8LSubSampleSize(transform->xsize_, bits); + transform->bits_ = bits; + ok = DecodeImageStream(num_colors, 1, 0, dec, &transform->data_); + ok = ok && ExpandColorMap(num_colors, transform); + break; + } + case SUBTRACT_GREEN: + break; + default: + assert(0); // can't happen + break; + } + + return ok; +} + +// ----------------------------------------------------------------------------- +// VP8LMetadata + +static void InitMetadata(VP8LMetadata* const hdr) { + assert(hdr != NULL); + memset(hdr, 0, sizeof(*hdr)); +} + +static void ClearMetadata(VP8LMetadata* const hdr) { + assert(hdr != NULL); + + WebPSafeFree(hdr->huffman_image_); + WebPSafeFree(hdr->huffman_tables_); + VP8LHtreeGroupsFree(hdr->htree_groups_); + VP8LColorCacheClear(&hdr->color_cache_); + VP8LColorCacheClear(&hdr->saved_color_cache_); + InitMetadata(hdr); +} + +// ----------------------------------------------------------------------------- +// VP8LDecoder + +VP8LDecoder* VP8LNew(void) { + VP8LDecoder* const dec = (VP8LDecoder*)WebPSafeCalloc(1ULL, sizeof(*dec)); + if (dec == NULL) return NULL; + dec->status_ = VP8_STATUS_OK; + dec->state_ = READ_DIM; + + VP8LDspInit(); // Init critical function pointers. + + return dec; +} + +void VP8LClear(VP8LDecoder* const dec) { + int i; + if (dec == NULL) return; + ClearMetadata(&dec->hdr_); + + WebPSafeFree(dec->pixels_); + dec->pixels_ = NULL; + for (i = 0; i < dec->next_transform_; ++i) { + ClearTransform(&dec->transforms_[i]); + } + dec->next_transform_ = 0; + dec->transforms_seen_ = 0; + + WebPSafeFree(dec->rescaler_memory); + dec->rescaler_memory = NULL; + + dec->output_ = NULL; // leave no trace behind +} + +void VP8LDelete(VP8LDecoder* const dec) { + if (dec != NULL) { + VP8LClear(dec); + WebPSafeFree(dec); + } +} + +static void UpdateDecoder(VP8LDecoder* const dec, int width, int height) { + VP8LMetadata* const hdr = &dec->hdr_; + const int num_bits = hdr->huffman_subsample_bits_; + dec->width_ = width; + dec->height_ = height; + + hdr->huffman_xsize_ = VP8LSubSampleSize(width, num_bits); + hdr->huffman_mask_ = (num_bits == 0) ? ~0 : (1 << num_bits) - 1; +} + +static int DecodeImageStream(int xsize, int ysize, + int is_level0, + VP8LDecoder* const dec, + uint32_t** const decoded_data) { + int ok = 1; + int transform_xsize = xsize; + int transform_ysize = ysize; + VP8LBitReader* const br = &dec->br_; + VP8LMetadata* const hdr = &dec->hdr_; + uint32_t* data = NULL; + int color_cache_bits = 0; + + // Read the transforms (may recurse). + if (is_level0) { + while (ok && VP8LReadBits(br, 1)) { + ok = ReadTransform(&transform_xsize, &transform_ysize, dec); + } + } + + // Color cache + if (ok && VP8LReadBits(br, 1)) { + color_cache_bits = VP8LReadBits(br, 4); + ok = (color_cache_bits >= 1 && color_cache_bits <= MAX_CACHE_BITS); + if (!ok) { + dec->status_ = VP8_STATUS_BITSTREAM_ERROR; + goto End; + } + } + + // Read the Huffman codes (may recurse). + ok = ok && ReadHuffmanCodes(dec, transform_xsize, transform_ysize, + color_cache_bits, is_level0); + if (!ok) { + dec->status_ = VP8_STATUS_BITSTREAM_ERROR; + goto End; + } + + // Finish setting up the color-cache + if (color_cache_bits > 0) { + hdr->color_cache_size_ = 1 << color_cache_bits; + if (!VP8LColorCacheInit(&hdr->color_cache_, color_cache_bits)) { + dec->status_ = VP8_STATUS_OUT_OF_MEMORY; + ok = 0; + goto End; + } + } else { + hdr->color_cache_size_ = 0; + } + UpdateDecoder(dec, transform_xsize, transform_ysize); + + if (is_level0) { // level 0 complete + dec->state_ = READ_HDR; + goto End; + } + + { + const uint64_t total_size = (uint64_t)transform_xsize * transform_ysize; + data = (uint32_t*)WebPSafeMalloc(total_size, sizeof(*data)); + if (data == NULL) { + dec->status_ = VP8_STATUS_OUT_OF_MEMORY; + ok = 0; + goto End; + } + } + + // Use the Huffman trees to decode the LZ77 encoded data. + ok = DecodeImageData(dec, data, transform_xsize, transform_ysize, + transform_ysize, NULL); + ok = ok && !br->eos_; + + End: + if (!ok) { + WebPSafeFree(data); + ClearMetadata(hdr); + } else { + if (decoded_data != NULL) { + *decoded_data = data; + } else { + // We allocate image data in this function only for transforms. At level 0 + // (that is: not the transforms), we shouldn't have allocated anything. + assert(data == NULL); + assert(is_level0); + } + dec->last_pixel_ = 0; // Reset for future DECODE_DATA_FUNC() calls. + if (!is_level0) ClearMetadata(hdr); // Clean up temporary data behind. + } + return ok; +} + +//------------------------------------------------------------------------------ +// Allocate internal buffers dec->pixels_ and dec->argb_cache_. +static int AllocateInternalBuffers32b(VP8LDecoder* const dec, int final_width) { + const uint64_t num_pixels = (uint64_t)dec->width_ * dec->height_; + // Scratch buffer corresponding to top-prediction row for transforming the + // first row in the row-blocks. Not needed for paletted alpha. + const uint64_t cache_top_pixels = (uint16_t)final_width; + // Scratch buffer for temporary BGRA storage. Not needed for paletted alpha. + const uint64_t cache_pixels = (uint64_t)final_width * NUM_ARGB_CACHE_ROWS; + const uint64_t total_num_pixels = + num_pixels + cache_top_pixels + cache_pixels; + + assert(dec->width_ <= final_width); + dec->pixels_ = (uint32_t*)WebPSafeMalloc(total_num_pixels, sizeof(uint32_t)); + if (dec->pixels_ == NULL) { + dec->argb_cache_ = NULL; // for sanity check + dec->status_ = VP8_STATUS_OUT_OF_MEMORY; + return 0; + } + dec->argb_cache_ = dec->pixels_ + num_pixels + cache_top_pixels; + return 1; +} + +static int AllocateInternalBuffers8b(VP8LDecoder* const dec) { + const uint64_t total_num_pixels = (uint64_t)dec->width_ * dec->height_; + dec->argb_cache_ = NULL; // for sanity check + dec->pixels_ = (uint32_t*)WebPSafeMalloc(total_num_pixels, sizeof(uint8_t)); + if (dec->pixels_ == NULL) { + dec->status_ = VP8_STATUS_OUT_OF_MEMORY; + return 0; + } + return 1; +} + +//------------------------------------------------------------------------------ + +// Special row-processing that only stores the alpha data. +static void ExtractAlphaRows(VP8LDecoder* const dec, int last_row) { + int cur_row = dec->last_row_; + int num_rows = last_row - cur_row; + const uint32_t* in = dec->pixels_ + dec->width_ * cur_row; + + assert(last_row <= dec->io_->crop_bottom); + while (num_rows > 0) { + const int num_rows_to_process = + (num_rows > NUM_ARGB_CACHE_ROWS) ? NUM_ARGB_CACHE_ROWS : num_rows; + // Extract alpha (which is stored in the green plane). + ALPHDecoder* const alph_dec = (ALPHDecoder*)dec->io_->opaque; + uint8_t* const output = alph_dec->output_; + const int width = dec->io_->width; // the final width (!= dec->width_) + const int cache_pixs = width * num_rows_to_process; + uint8_t* const dst = output + width * cur_row; + const uint32_t* const src = dec->argb_cache_; + ApplyInverseTransforms(dec, num_rows_to_process, in); + WebPExtractGreen(src, dst, cache_pixs); + AlphaApplyFilter(alph_dec, + cur_row, cur_row + num_rows_to_process, dst, width); + num_rows -= num_rows_to_process; + in += num_rows_to_process * dec->width_; + cur_row += num_rows_to_process; + } + assert(cur_row == last_row); + dec->last_row_ = dec->last_out_row_ = last_row; +} + +int VP8LDecodeAlphaHeader(ALPHDecoder* const alph_dec, + const uint8_t* const data, size_t data_size) { + int ok = 0; + VP8LDecoder* dec = VP8LNew(); + + if (dec == NULL) return 0; + + assert(alph_dec != NULL); + alph_dec->vp8l_dec_ = dec; + + dec->width_ = alph_dec->width_; + dec->height_ = alph_dec->height_; + dec->io_ = &alph_dec->io_; + dec->io_->opaque = alph_dec; + dec->io_->width = alph_dec->width_; + dec->io_->height = alph_dec->height_; + + dec->status_ = VP8_STATUS_OK; + VP8LInitBitReader(&dec->br_, data, data_size); + + if (!DecodeImageStream(alph_dec->width_, alph_dec->height_, 1, dec, NULL)) { + goto Err; + } + + // Special case: if alpha data uses only the color indexing transform and + // doesn't use color cache (a frequent case), we will use DecodeAlphaData() + // method that only needs allocation of 1 byte per pixel (alpha channel). + if (dec->next_transform_ == 1 && + dec->transforms_[0].type_ == COLOR_INDEXING_TRANSFORM && + Is8bOptimizable(&dec->hdr_)) { + alph_dec->use_8b_decode_ = 1; + ok = AllocateInternalBuffers8b(dec); + } else { + // Allocate internal buffers (note that dec->width_ may have changed here). + alph_dec->use_8b_decode_ = 0; + ok = AllocateInternalBuffers32b(dec, alph_dec->width_); + } + + if (!ok) goto Err; + + return 1; + + Err: + VP8LDelete(alph_dec->vp8l_dec_); + alph_dec->vp8l_dec_ = NULL; + return 0; +} + +int VP8LDecodeAlphaImageStream(ALPHDecoder* const alph_dec, int last_row) { + VP8LDecoder* const dec = alph_dec->vp8l_dec_; + assert(dec != NULL); + assert(last_row <= dec->height_); + + if (dec->last_row_ >= last_row) { + return 1; // done + } + + if (!alph_dec->use_8b_decode_) WebPInitAlphaProcessing(); + + // Decode (with special row processing). + return alph_dec->use_8b_decode_ ? + DecodeAlphaData(dec, (uint8_t*)dec->pixels_, dec->width_, dec->height_, + last_row) : + DecodeImageData(dec, dec->pixels_, dec->width_, dec->height_, + last_row, ExtractAlphaRows); +} + +//------------------------------------------------------------------------------ + +int VP8LDecodeHeader(VP8LDecoder* const dec, VP8Io* const io) { + int width, height, has_alpha; + + if (dec == NULL) return 0; + if (io == NULL) { + dec->status_ = VP8_STATUS_INVALID_PARAM; + return 0; + } + + dec->io_ = io; + dec->status_ = VP8_STATUS_OK; + VP8LInitBitReader(&dec->br_, io->data, io->data_size); + if (!ReadImageInfo(&dec->br_, &width, &height, &has_alpha)) { + dec->status_ = VP8_STATUS_BITSTREAM_ERROR; + goto Error; + } + dec->state_ = READ_DIM; + io->width = width; + io->height = height; + + if (!DecodeImageStream(width, height, 1, dec, NULL)) goto Error; + return 1; + + Error: + VP8LClear(dec); + assert(dec->status_ != VP8_STATUS_OK); + return 0; +} + +int VP8LDecodeImage(VP8LDecoder* const dec) { + VP8Io* io = NULL; + WebPDecParams* params = NULL; + + // Sanity checks. + if (dec == NULL) return 0; + + assert(dec->hdr_.huffman_tables_ != NULL); + assert(dec->hdr_.htree_groups_ != NULL); + assert(dec->hdr_.num_htree_groups_ > 0); + + io = dec->io_; + assert(io != NULL); + params = (WebPDecParams*)io->opaque; + assert(params != NULL); + + // Initialization. + if (dec->state_ != READ_DATA) { + dec->output_ = params->output; + assert(dec->output_ != NULL); + + if (!WebPIoInitFromOptions(params->options, io, MODE_BGRA)) { + dec->status_ = VP8_STATUS_INVALID_PARAM; + goto Err; + } + + if (!AllocateInternalBuffers32b(dec, io->width)) goto Err; + + if (io->use_scaling && !AllocateAndInitRescaler(dec, io)) goto Err; + + if (io->use_scaling || WebPIsPremultipliedMode(dec->output_->colorspace)) { + // need the alpha-multiply functions for premultiplied output or rescaling + WebPInitAlphaProcessing(); + } + if (!WebPIsRGBMode(dec->output_->colorspace)) { + WebPInitConvertARGBToYUV(); + if (dec->output_->u.YUVA.a != NULL) WebPInitAlphaProcessing(); + } + if (dec->incremental_) { + if (dec->hdr_.color_cache_size_ > 0 && + dec->hdr_.saved_color_cache_.colors_ == NULL) { + if (!VP8LColorCacheInit(&dec->hdr_.saved_color_cache_, + dec->hdr_.color_cache_.hash_bits_)) { + dec->status_ = VP8_STATUS_OUT_OF_MEMORY; + goto Err; + } + } + } + dec->state_ = READ_DATA; + } + + // Decode. + if (!DecodeImageData(dec, dec->pixels_, dec->width_, dec->height_, + io->crop_bottom, ProcessRows)) { + goto Err; + } + + params->last_y = dec->last_out_row_; + return 1; + + Err: + VP8LClear(dec); + assert(dec->status_ != VP8_STATUS_OK); + return 0; +} + +//------------------------------------------------------------------------------ diff --git a/thirdparty/libwebp/dec/vp8li.h b/thirdparty/libwebp/dec/vp8li.h deleted file mode 100644 index 9313bdc0af..0000000000 --- a/thirdparty/libwebp/dec/vp8li.h +++ /dev/null @@ -1,135 +0,0 @@ -// Copyright 2012 Google Inc. All Rights Reserved. -// -// Use of this source code is governed by a BSD-style license -// that can be found in the COPYING file in the root of the source -// tree. An additional intellectual property rights grant can be found -// in the file PATENTS. All contributing project authors may -// be found in the AUTHORS file in the root of the source tree. -// ----------------------------------------------------------------------------- -// -// Lossless decoder: internal header. -// -// Author: Skal (pascal.massimino@gmail.com) -// Vikas Arora(vikaas.arora@gmail.com) - -#ifndef WEBP_DEC_VP8LI_H_ -#define WEBP_DEC_VP8LI_H_ - -#include // for memcpy() -#include "./webpi.h" -#include "../utils/bit_reader.h" -#include "../utils/color_cache.h" -#include "../utils/huffman.h" - -#ifdef __cplusplus -extern "C" { -#endif - -typedef enum { - READ_DATA = 0, - READ_HDR = 1, - READ_DIM = 2 -} VP8LDecodeState; - -typedef struct VP8LTransform VP8LTransform; -struct VP8LTransform { - VP8LImageTransformType type_; // transform type. - int bits_; // subsampling bits defining transform window. - int xsize_; // transform window X index. - int ysize_; // transform window Y index. - uint32_t *data_; // transform data. -}; - -typedef struct { - int color_cache_size_; - VP8LColorCache color_cache_; - VP8LColorCache saved_color_cache_; // for incremental - - int huffman_mask_; - int huffman_subsample_bits_; - int huffman_xsize_; - uint32_t *huffman_image_; - int num_htree_groups_; - HTreeGroup *htree_groups_; - HuffmanCode *huffman_tables_; -} VP8LMetadata; - -typedef struct VP8LDecoder VP8LDecoder; -struct VP8LDecoder { - VP8StatusCode status_; - VP8LDecodeState state_; - VP8Io *io_; - - const WebPDecBuffer *output_; // shortcut to io->opaque->output - - uint32_t *pixels_; // Internal data: either uint8_t* for alpha - // or uint32_t* for BGRA. - uint32_t *argb_cache_; // Scratch buffer for temporary BGRA storage. - - VP8LBitReader br_; - int incremental_; // if true, incremental decoding is expected - VP8LBitReader saved_br_; // note: could be local variables too - int saved_last_pixel_; - - int width_; - int height_; - int last_row_; // last input row decoded so far. - int last_pixel_; // last pixel decoded so far. However, it may - // not be transformed, scaled and - // color-converted yet. - int last_out_row_; // last row output so far. - - VP8LMetadata hdr_; - - int next_transform_; - VP8LTransform transforms_[NUM_TRANSFORMS]; - // or'd bitset storing the transforms types. - uint32_t transforms_seen_; - - uint8_t *rescaler_memory; // Working memory for rescaling work. - WebPRescaler *rescaler; // Common rescaler for all channels. -}; - -//------------------------------------------------------------------------------ -// internal functions. Not public. - -struct ALPHDecoder; // Defined in dec/alphai.h. - -// in vp8l.c - -// Decodes image header for alpha data stored using lossless compression. -// Returns false in case of error. -int VP8LDecodeAlphaHeader(struct ALPHDecoder* const alph_dec, - const uint8_t* const data, size_t data_size); - -// Decodes *at least* 'last_row' rows of alpha. If some of the initial rows are -// already decoded in previous call(s), it will resume decoding from where it -// was paused. -// Returns false in case of bitstream error. -int VP8LDecodeAlphaImageStream(struct ALPHDecoder* const alph_dec, - int last_row); - -// Allocates and initialize a new lossless decoder instance. -VP8LDecoder* VP8LNew(void); - -// Decodes the image header. Returns false in case of error. -int VP8LDecodeHeader(VP8LDecoder* const dec, VP8Io* const io); - -// Decodes an image. It's required to decode the lossless header before calling -// this function. Returns false in case of error, with updated dec->status_. -int VP8LDecodeImage(VP8LDecoder* const dec); - -// Resets the decoder in its initial state, reclaiming memory. -// Preserves the dec->status_ value. -void VP8LClear(VP8LDecoder* const dec); - -// Clears and deallocate a lossless decoder instance. -void VP8LDelete(VP8LDecoder* const dec); - -//------------------------------------------------------------------------------ - -#ifdef __cplusplus -} // extern "C" -#endif - -#endif /* WEBP_DEC_VP8LI_H_ */ diff --git a/thirdparty/libwebp/dec/vp8li_dec.h b/thirdparty/libwebp/dec/vp8li_dec.h new file mode 100644 index 0000000000..097a9d0589 --- /dev/null +++ b/thirdparty/libwebp/dec/vp8li_dec.h @@ -0,0 +1,135 @@ +// Copyright 2012 Google Inc. All Rights Reserved. +// +// Use of this source code is governed by a BSD-style license +// that can be found in the COPYING file in the root of the source +// tree. An additional intellectual property rights grant can be found +// in the file PATENTS. All contributing project authors may +// be found in the AUTHORS file in the root of the source tree. +// ----------------------------------------------------------------------------- +// +// Lossless decoder: internal header. +// +// Author: Skal (pascal.massimino@gmail.com) +// Vikas Arora(vikaas.arora@gmail.com) + +#ifndef WEBP_DEC_VP8LI_H_ +#define WEBP_DEC_VP8LI_H_ + +#include // for memcpy() +#include "./webpi_dec.h" +#include "../utils/bit_reader_utils.h" +#include "../utils/color_cache_utils.h" +#include "../utils/huffman_utils.h" + +#ifdef __cplusplus +extern "C" { +#endif + +typedef enum { + READ_DATA = 0, + READ_HDR = 1, + READ_DIM = 2 +} VP8LDecodeState; + +typedef struct VP8LTransform VP8LTransform; +struct VP8LTransform { + VP8LImageTransformType type_; // transform type. + int bits_; // subsampling bits defining transform window. + int xsize_; // transform window X index. + int ysize_; // transform window Y index. + uint32_t *data_; // transform data. +}; + +typedef struct { + int color_cache_size_; + VP8LColorCache color_cache_; + VP8LColorCache saved_color_cache_; // for incremental + + int huffman_mask_; + int huffman_subsample_bits_; + int huffman_xsize_; + uint32_t *huffman_image_; + int num_htree_groups_; + HTreeGroup *htree_groups_; + HuffmanCode *huffman_tables_; +} VP8LMetadata; + +typedef struct VP8LDecoder VP8LDecoder; +struct VP8LDecoder { + VP8StatusCode status_; + VP8LDecodeState state_; + VP8Io *io_; + + const WebPDecBuffer *output_; // shortcut to io->opaque->output + + uint32_t *pixels_; // Internal data: either uint8_t* for alpha + // or uint32_t* for BGRA. + uint32_t *argb_cache_; // Scratch buffer for temporary BGRA storage. + + VP8LBitReader br_; + int incremental_; // if true, incremental decoding is expected + VP8LBitReader saved_br_; // note: could be local variables too + int saved_last_pixel_; + + int width_; + int height_; + int last_row_; // last input row decoded so far. + int last_pixel_; // last pixel decoded so far. However, it may + // not be transformed, scaled and + // color-converted yet. + int last_out_row_; // last row output so far. + + VP8LMetadata hdr_; + + int next_transform_; + VP8LTransform transforms_[NUM_TRANSFORMS]; + // or'd bitset storing the transforms types. + uint32_t transforms_seen_; + + uint8_t *rescaler_memory; // Working memory for rescaling work. + WebPRescaler *rescaler; // Common rescaler for all channels. +}; + +//------------------------------------------------------------------------------ +// internal functions. Not public. + +struct ALPHDecoder; // Defined in dec/alphai.h. + +// in vp8l.c + +// Decodes image header for alpha data stored using lossless compression. +// Returns false in case of error. +int VP8LDecodeAlphaHeader(struct ALPHDecoder* const alph_dec, + const uint8_t* const data, size_t data_size); + +// Decodes *at least* 'last_row' rows of alpha. If some of the initial rows are +// already decoded in previous call(s), it will resume decoding from where it +// was paused. +// Returns false in case of bitstream error. +int VP8LDecodeAlphaImageStream(struct ALPHDecoder* const alph_dec, + int last_row); + +// Allocates and initialize a new lossless decoder instance. +VP8LDecoder* VP8LNew(void); + +// Decodes the image header. Returns false in case of error. +int VP8LDecodeHeader(VP8LDecoder* const dec, VP8Io* const io); + +// Decodes an image. It's required to decode the lossless header before calling +// this function. Returns false in case of error, with updated dec->status_. +int VP8LDecodeImage(VP8LDecoder* const dec); + +// Resets the decoder in its initial state, reclaiming memory. +// Preserves the dec->status_ value. +void VP8LClear(VP8LDecoder* const dec); + +// Clears and deallocate a lossless decoder instance. +void VP8LDelete(VP8LDecoder* const dec); + +//------------------------------------------------------------------------------ + +#ifdef __cplusplus +} // extern "C" +#endif + +#endif /* WEBP_DEC_VP8LI_H_ */ diff --git a/thirdparty/libwebp/dec/webp.c b/thirdparty/libwebp/dec/webp.c deleted file mode 100644 index d0b912f02f..0000000000 --- a/thirdparty/libwebp/dec/webp.c +++ /dev/null @@ -1,846 +0,0 @@ -// Copyright 2010 Google Inc. All Rights Reserved. -// -// Use of this source code is governed by a BSD-style license -// that can be found in the COPYING file in the root of the source -// tree. An additional intellectual property rights grant can be found -// in the file PATENTS. All contributing project authors may -// be found in the AUTHORS file in the root of the source tree. -// ----------------------------------------------------------------------------- -// -// Main decoding functions for WEBP images. -// -// Author: Skal (pascal.massimino@gmail.com) - -#include - -#include "./vp8i.h" -#include "./vp8li.h" -#include "./webpi.h" -#include "../utils/utils.h" -#include "../webp/mux_types.h" // ALPHA_FLAG - -//------------------------------------------------------------------------------ -// RIFF layout is: -// Offset tag -// 0...3 "RIFF" 4-byte tag -// 4...7 size of image data (including metadata) starting at offset 8 -// 8...11 "WEBP" our form-type signature -// The RIFF container (12 bytes) is followed by appropriate chunks: -// 12..15 "VP8 ": 4-bytes tags, signaling the use of VP8 video format -// 16..19 size of the raw VP8 image data, starting at offset 20 -// 20.... the VP8 bytes -// Or, -// 12..15 "VP8L": 4-bytes tags, signaling the use of VP8L lossless format -// 16..19 size of the raw VP8L image data, starting at offset 20 -// 20.... the VP8L bytes -// Or, -// 12..15 "VP8X": 4-bytes tags, describing the extended-VP8 chunk. -// 16..19 size of the VP8X chunk starting at offset 20. -// 20..23 VP8X flags bit-map corresponding to the chunk-types present. -// 24..26 Width of the Canvas Image. -// 27..29 Height of the Canvas Image. -// There can be extra chunks after the "VP8X" chunk (ICCP, FRGM, ANMF, VP8, -// VP8L, XMP, EXIF ...) -// All sizes are in little-endian order. -// Note: chunk data size must be padded to multiple of 2 when written. - -// Validates the RIFF container (if detected) and skips over it. -// If a RIFF container is detected, returns: -// VP8_STATUS_BITSTREAM_ERROR for invalid header, -// VP8_STATUS_NOT_ENOUGH_DATA for truncated data if have_all_data is true, -// and VP8_STATUS_OK otherwise. -// In case there are not enough bytes (partial RIFF container), return 0 for -// *riff_size. Else return the RIFF size extracted from the header. -static VP8StatusCode ParseRIFF(const uint8_t** const data, - size_t* const data_size, int have_all_data, - size_t* const riff_size) { - assert(data != NULL); - assert(data_size != NULL); - assert(riff_size != NULL); - - *riff_size = 0; // Default: no RIFF present. - if (*data_size >= RIFF_HEADER_SIZE && !memcmp(*data, "RIFF", TAG_SIZE)) { - if (memcmp(*data + 8, "WEBP", TAG_SIZE)) { - return VP8_STATUS_BITSTREAM_ERROR; // Wrong image file signature. - } else { - const uint32_t size = GetLE32(*data + TAG_SIZE); - // Check that we have at least one chunk (i.e "WEBP" + "VP8?nnnn"). - if (size < TAG_SIZE + CHUNK_HEADER_SIZE) { - return VP8_STATUS_BITSTREAM_ERROR; - } - if (size > MAX_CHUNK_PAYLOAD) { - return VP8_STATUS_BITSTREAM_ERROR; - } - if (have_all_data && (size > *data_size - CHUNK_HEADER_SIZE)) { - return VP8_STATUS_NOT_ENOUGH_DATA; // Truncated bitstream. - } - // We have a RIFF container. Skip it. - *riff_size = size; - *data += RIFF_HEADER_SIZE; - *data_size -= RIFF_HEADER_SIZE; - } - } - return VP8_STATUS_OK; -} - -// Validates the VP8X header and skips over it. -// Returns VP8_STATUS_BITSTREAM_ERROR for invalid VP8X header, -// VP8_STATUS_NOT_ENOUGH_DATA in case of insufficient data, and -// VP8_STATUS_OK otherwise. -// If a VP8X chunk is found, found_vp8x is set to true and *width_ptr, -// *height_ptr and *flags_ptr are set to the corresponding values extracted -// from the VP8X chunk. -static VP8StatusCode ParseVP8X(const uint8_t** const data, - size_t* const data_size, - int* const found_vp8x, - int* const width_ptr, int* const height_ptr, - uint32_t* const flags_ptr) { - const uint32_t vp8x_size = CHUNK_HEADER_SIZE + VP8X_CHUNK_SIZE; - assert(data != NULL); - assert(data_size != NULL); - assert(found_vp8x != NULL); - - *found_vp8x = 0; - - if (*data_size < CHUNK_HEADER_SIZE) { - return VP8_STATUS_NOT_ENOUGH_DATA; // Insufficient data. - } - - if (!memcmp(*data, "VP8X", TAG_SIZE)) { - int width, height; - uint32_t flags; - const uint32_t chunk_size = GetLE32(*data + TAG_SIZE); - if (chunk_size != VP8X_CHUNK_SIZE) { - return VP8_STATUS_BITSTREAM_ERROR; // Wrong chunk size. - } - - // Verify if enough data is available to validate the VP8X chunk. - if (*data_size < vp8x_size) { - return VP8_STATUS_NOT_ENOUGH_DATA; // Insufficient data. - } - flags = GetLE32(*data + 8); - width = 1 + GetLE24(*data + 12); - height = 1 + GetLE24(*data + 15); - if (width * (uint64_t)height >= MAX_IMAGE_AREA) { - return VP8_STATUS_BITSTREAM_ERROR; // image is too large - } - - if (flags_ptr != NULL) *flags_ptr = flags; - if (width_ptr != NULL) *width_ptr = width; - if (height_ptr != NULL) *height_ptr = height; - // Skip over VP8X header bytes. - *data += vp8x_size; - *data_size -= vp8x_size; - *found_vp8x = 1; - } - return VP8_STATUS_OK; -} - -// Skips to the next VP8/VP8L chunk header in the data given the size of the -// RIFF chunk 'riff_size'. -// Returns VP8_STATUS_BITSTREAM_ERROR if any invalid chunk size is encountered, -// VP8_STATUS_NOT_ENOUGH_DATA in case of insufficient data, and -// VP8_STATUS_OK otherwise. -// If an alpha chunk is found, *alpha_data and *alpha_size are set -// appropriately. -static VP8StatusCode ParseOptionalChunks(const uint8_t** const data, - size_t* const data_size, - size_t const riff_size, - const uint8_t** const alpha_data, - size_t* const alpha_size) { - const uint8_t* buf; - size_t buf_size; - uint32_t total_size = TAG_SIZE + // "WEBP". - CHUNK_HEADER_SIZE + // "VP8Xnnnn". - VP8X_CHUNK_SIZE; // data. - assert(data != NULL); - assert(data_size != NULL); - buf = *data; - buf_size = *data_size; - - assert(alpha_data != NULL); - assert(alpha_size != NULL); - *alpha_data = NULL; - *alpha_size = 0; - - while (1) { - uint32_t chunk_size; - uint32_t disk_chunk_size; // chunk_size with padding - - *data = buf; - *data_size = buf_size; - - if (buf_size < CHUNK_HEADER_SIZE) { // Insufficient data. - return VP8_STATUS_NOT_ENOUGH_DATA; - } - - chunk_size = GetLE32(buf + TAG_SIZE); - if (chunk_size > MAX_CHUNK_PAYLOAD) { - return VP8_STATUS_BITSTREAM_ERROR; // Not a valid chunk size. - } - // For odd-sized chunk-payload, there's one byte padding at the end. - disk_chunk_size = (CHUNK_HEADER_SIZE + chunk_size + 1) & ~1; - total_size += disk_chunk_size; - - // Check that total bytes skipped so far does not exceed riff_size. - if (riff_size > 0 && (total_size > riff_size)) { - return VP8_STATUS_BITSTREAM_ERROR; // Not a valid chunk size. - } - - // Start of a (possibly incomplete) VP8/VP8L chunk implies that we have - // parsed all the optional chunks. - // Note: This check must occur before the check 'buf_size < disk_chunk_size' - // below to allow incomplete VP8/VP8L chunks. - if (!memcmp(buf, "VP8 ", TAG_SIZE) || - !memcmp(buf, "VP8L", TAG_SIZE)) { - return VP8_STATUS_OK; - } - - if (buf_size < disk_chunk_size) { // Insufficient data. - return VP8_STATUS_NOT_ENOUGH_DATA; - } - - if (!memcmp(buf, "ALPH", TAG_SIZE)) { // A valid ALPH header. - *alpha_data = buf + CHUNK_HEADER_SIZE; - *alpha_size = chunk_size; - } - - // We have a full and valid chunk; skip it. - buf += disk_chunk_size; - buf_size -= disk_chunk_size; - } -} - -// Validates the VP8/VP8L Header ("VP8 nnnn" or "VP8L nnnn") and skips over it. -// Returns VP8_STATUS_BITSTREAM_ERROR for invalid (chunk larger than -// riff_size) VP8/VP8L header, -// VP8_STATUS_NOT_ENOUGH_DATA in case of insufficient data, and -// VP8_STATUS_OK otherwise. -// If a VP8/VP8L chunk is found, *chunk_size is set to the total number of bytes -// extracted from the VP8/VP8L chunk header. -// The flag '*is_lossless' is set to 1 in case of VP8L chunk / raw VP8L data. -static VP8StatusCode ParseVP8Header(const uint8_t** const data_ptr, - size_t* const data_size, int have_all_data, - size_t riff_size, size_t* const chunk_size, - int* const is_lossless) { - const uint8_t* const data = *data_ptr; - const int is_vp8 = !memcmp(data, "VP8 ", TAG_SIZE); - const int is_vp8l = !memcmp(data, "VP8L", TAG_SIZE); - const uint32_t minimal_size = - TAG_SIZE + CHUNK_HEADER_SIZE; // "WEBP" + "VP8 nnnn" OR - // "WEBP" + "VP8Lnnnn" - assert(data != NULL); - assert(data_size != NULL); - assert(chunk_size != NULL); - assert(is_lossless != NULL); - - if (*data_size < CHUNK_HEADER_SIZE) { - return VP8_STATUS_NOT_ENOUGH_DATA; // Insufficient data. - } - - if (is_vp8 || is_vp8l) { - // Bitstream contains VP8/VP8L header. - const uint32_t size = GetLE32(data + TAG_SIZE); - if ((riff_size >= minimal_size) && (size > riff_size - minimal_size)) { - return VP8_STATUS_BITSTREAM_ERROR; // Inconsistent size information. - } - if (have_all_data && (size > *data_size - CHUNK_HEADER_SIZE)) { - return VP8_STATUS_NOT_ENOUGH_DATA; // Truncated bitstream. - } - // Skip over CHUNK_HEADER_SIZE bytes from VP8/VP8L Header. - *chunk_size = size; - *data_ptr += CHUNK_HEADER_SIZE; - *data_size -= CHUNK_HEADER_SIZE; - *is_lossless = is_vp8l; - } else { - // Raw VP8/VP8L bitstream (no header). - *is_lossless = VP8LCheckSignature(data, *data_size); - *chunk_size = *data_size; - } - - return VP8_STATUS_OK; -} - -//------------------------------------------------------------------------------ - -// Fetch '*width', '*height', '*has_alpha' and fill out 'headers' based on -// 'data'. All the output parameters may be NULL. If 'headers' is NULL only the -// minimal amount will be read to fetch the remaining parameters. -// If 'headers' is non-NULL this function will attempt to locate both alpha -// data (with or without a VP8X chunk) and the bitstream chunk (VP8/VP8L). -// Note: The following chunk sequences (before the raw VP8/VP8L data) are -// considered valid by this function: -// RIFF + VP8(L) -// RIFF + VP8X + (optional chunks) + VP8(L) -// ALPH + VP8 <-- Not a valid WebP format: only allowed for internal purpose. -// VP8(L) <-- Not a valid WebP format: only allowed for internal purpose. -static VP8StatusCode ParseHeadersInternal(const uint8_t* data, - size_t data_size, - int* const width, - int* const height, - int* const has_alpha, - int* const has_animation, - int* const format, - WebPHeaderStructure* const headers) { - int canvas_width = 0; - int canvas_height = 0; - int image_width = 0; - int image_height = 0; - int found_riff = 0; - int found_vp8x = 0; - int animation_present = 0; - int fragments_present = 0; - const int have_all_data = (headers != NULL) ? headers->have_all_data : 0; - - VP8StatusCode status; - WebPHeaderStructure hdrs; - - if (data == NULL || data_size < RIFF_HEADER_SIZE) { - return VP8_STATUS_NOT_ENOUGH_DATA; - } - memset(&hdrs, 0, sizeof(hdrs)); - hdrs.data = data; - hdrs.data_size = data_size; - - // Skip over RIFF header. - status = ParseRIFF(&data, &data_size, have_all_data, &hdrs.riff_size); - if (status != VP8_STATUS_OK) { - return status; // Wrong RIFF header / insufficient data. - } - found_riff = (hdrs.riff_size > 0); - - // Skip over VP8X. - { - uint32_t flags = 0; - status = ParseVP8X(&data, &data_size, &found_vp8x, - &canvas_width, &canvas_height, &flags); - if (status != VP8_STATUS_OK) { - return status; // Wrong VP8X / insufficient data. - } - animation_present = !!(flags & ANIMATION_FLAG); - fragments_present = !!(flags & FRAGMENTS_FLAG); - if (!found_riff && found_vp8x) { - // Note: This restriction may be removed in the future, if it becomes - // necessary to send VP8X chunk to the decoder. - return VP8_STATUS_BITSTREAM_ERROR; - } - if (has_alpha != NULL) *has_alpha = !!(flags & ALPHA_FLAG); - if (has_animation != NULL) *has_animation = animation_present; - if (format != NULL) *format = 0; // default = undefined - - image_width = canvas_width; - image_height = canvas_height; - if (found_vp8x && (animation_present || fragments_present) && - headers == NULL) { - status = VP8_STATUS_OK; - goto ReturnWidthHeight; // Just return features from VP8X header. - } - } - - if (data_size < TAG_SIZE) { - status = VP8_STATUS_NOT_ENOUGH_DATA; - goto ReturnWidthHeight; - } - - // Skip over optional chunks if data started with "RIFF + VP8X" or "ALPH". - if ((found_riff && found_vp8x) || - (!found_riff && !found_vp8x && !memcmp(data, "ALPH", TAG_SIZE))) { - status = ParseOptionalChunks(&data, &data_size, hdrs.riff_size, - &hdrs.alpha_data, &hdrs.alpha_data_size); - if (status != VP8_STATUS_OK) { - goto ReturnWidthHeight; // Invalid chunk size / insufficient data. - } - } - - // Skip over VP8/VP8L header. - status = ParseVP8Header(&data, &data_size, have_all_data, hdrs.riff_size, - &hdrs.compressed_size, &hdrs.is_lossless); - if (status != VP8_STATUS_OK) { - goto ReturnWidthHeight; // Wrong VP8/VP8L chunk-header / insufficient data. - } - if (hdrs.compressed_size > MAX_CHUNK_PAYLOAD) { - return VP8_STATUS_BITSTREAM_ERROR; - } - - if (format != NULL && !(animation_present || fragments_present)) { - *format = hdrs.is_lossless ? 2 : 1; - } - - if (!hdrs.is_lossless) { - if (data_size < VP8_FRAME_HEADER_SIZE) { - status = VP8_STATUS_NOT_ENOUGH_DATA; - goto ReturnWidthHeight; - } - // Validates raw VP8 data. - if (!VP8GetInfo(data, data_size, (uint32_t)hdrs.compressed_size, - &image_width, &image_height)) { - return VP8_STATUS_BITSTREAM_ERROR; - } - } else { - if (data_size < VP8L_FRAME_HEADER_SIZE) { - status = VP8_STATUS_NOT_ENOUGH_DATA; - goto ReturnWidthHeight; - } - // Validates raw VP8L data. - if (!VP8LGetInfo(data, data_size, &image_width, &image_height, has_alpha)) { - return VP8_STATUS_BITSTREAM_ERROR; - } - } - // Validates image size coherency. - if (found_vp8x) { - if (canvas_width != image_width || canvas_height != image_height) { - return VP8_STATUS_BITSTREAM_ERROR; - } - } - if (headers != NULL) { - *headers = hdrs; - headers->offset = data - headers->data; - assert((uint64_t)(data - headers->data) < MAX_CHUNK_PAYLOAD); - assert(headers->offset == headers->data_size - data_size); - } - ReturnWidthHeight: - if (status == VP8_STATUS_OK || - (status == VP8_STATUS_NOT_ENOUGH_DATA && found_vp8x && headers == NULL)) { - if (has_alpha != NULL) { - // If the data did not contain a VP8X/VP8L chunk the only definitive way - // to set this is by looking for alpha data (from an ALPH chunk). - *has_alpha |= (hdrs.alpha_data != NULL); - } - if (width != NULL) *width = image_width; - if (height != NULL) *height = image_height; - return VP8_STATUS_OK; - } else { - return status; - } -} - -VP8StatusCode WebPParseHeaders(WebPHeaderStructure* const headers) { - // status is marked volatile as a workaround for a clang-3.8 (aarch64) bug - volatile VP8StatusCode status; - int has_animation = 0; - assert(headers != NULL); - // fill out headers, ignore width/height/has_alpha. - status = ParseHeadersInternal(headers->data, headers->data_size, - NULL, NULL, NULL, &has_animation, - NULL, headers); - if (status == VP8_STATUS_OK || status == VP8_STATUS_NOT_ENOUGH_DATA) { - // TODO(jzern): full support of animation frames will require API additions. - if (has_animation) { - status = VP8_STATUS_UNSUPPORTED_FEATURE; - } - } - return status; -} - -//------------------------------------------------------------------------------ -// WebPDecParams - -void WebPResetDecParams(WebPDecParams* const params) { - if (params != NULL) { - memset(params, 0, sizeof(*params)); - } -} - -//------------------------------------------------------------------------------ -// "Into" decoding variants - -// Main flow -static VP8StatusCode DecodeInto(const uint8_t* const data, size_t data_size, - WebPDecParams* const params) { - VP8StatusCode status; - VP8Io io; - WebPHeaderStructure headers; - - headers.data = data; - headers.data_size = data_size; - headers.have_all_data = 1; - status = WebPParseHeaders(&headers); // Process Pre-VP8 chunks. - if (status != VP8_STATUS_OK) { - return status; - } - - assert(params != NULL); - VP8InitIo(&io); - io.data = headers.data + headers.offset; - io.data_size = headers.data_size - headers.offset; - WebPInitCustomIo(params, &io); // Plug the I/O functions. - - if (!headers.is_lossless) { - VP8Decoder* const dec = VP8New(); - if (dec == NULL) { - return VP8_STATUS_OUT_OF_MEMORY; - } - dec->alpha_data_ = headers.alpha_data; - dec->alpha_data_size_ = headers.alpha_data_size; - - // Decode bitstream header, update io->width/io->height. - if (!VP8GetHeaders(dec, &io)) { - status = dec->status_; // An error occurred. Grab error status. - } else { - // Allocate/check output buffers. - status = WebPAllocateDecBuffer(io.width, io.height, params->options, - params->output); - if (status == VP8_STATUS_OK) { // Decode - // This change must be done before calling VP8Decode() - dec->mt_method_ = VP8GetThreadMethod(params->options, &headers, - io.width, io.height); - VP8InitDithering(params->options, dec); - if (!VP8Decode(dec, &io)) { - status = dec->status_; - } - } - } - VP8Delete(dec); - } else { - VP8LDecoder* const dec = VP8LNew(); - if (dec == NULL) { - return VP8_STATUS_OUT_OF_MEMORY; - } - if (!VP8LDecodeHeader(dec, &io)) { - status = dec->status_; // An error occurred. Grab error status. - } else { - // Allocate/check output buffers. - status = WebPAllocateDecBuffer(io.width, io.height, params->options, - params->output); - if (status == VP8_STATUS_OK) { // Decode - if (!VP8LDecodeImage(dec)) { - status = dec->status_; - } - } - } - VP8LDelete(dec); - } - - if (status != VP8_STATUS_OK) { - WebPFreeDecBuffer(params->output); - } else { - if (params->options != NULL && params->options->flip) { - // This restores the original stride values if options->flip was used - // during the call to WebPAllocateDecBuffer above. - status = WebPFlipBuffer(params->output); - } - } - return status; -} - -// Helpers -static uint8_t* DecodeIntoRGBABuffer(WEBP_CSP_MODE colorspace, - const uint8_t* const data, - size_t data_size, - uint8_t* const rgba, - int stride, size_t size) { - WebPDecParams params; - WebPDecBuffer buf; - if (rgba == NULL) { - return NULL; - } - WebPInitDecBuffer(&buf); - WebPResetDecParams(¶ms); - params.output = &buf; - buf.colorspace = colorspace; - buf.u.RGBA.rgba = rgba; - buf.u.RGBA.stride = stride; - buf.u.RGBA.size = size; - buf.is_external_memory = 1; - if (DecodeInto(data, data_size, ¶ms) != VP8_STATUS_OK) { - return NULL; - } - return rgba; -} - -uint8_t* WebPDecodeRGBInto(const uint8_t* data, size_t data_size, - uint8_t* output, size_t size, int stride) { - return DecodeIntoRGBABuffer(MODE_RGB, data, data_size, output, stride, size); -} - -uint8_t* WebPDecodeRGBAInto(const uint8_t* data, size_t data_size, - uint8_t* output, size_t size, int stride) { - return DecodeIntoRGBABuffer(MODE_RGBA, data, data_size, output, stride, size); -} - -uint8_t* WebPDecodeARGBInto(const uint8_t* data, size_t data_size, - uint8_t* output, size_t size, int stride) { - return DecodeIntoRGBABuffer(MODE_ARGB, data, data_size, output, stride, size); -} - -uint8_t* WebPDecodeBGRInto(const uint8_t* data, size_t data_size, - uint8_t* output, size_t size, int stride) { - return DecodeIntoRGBABuffer(MODE_BGR, data, data_size, output, stride, size); -} - -uint8_t* WebPDecodeBGRAInto(const uint8_t* data, size_t data_size, - uint8_t* output, size_t size, int stride) { - return DecodeIntoRGBABuffer(MODE_BGRA, data, data_size, output, stride, size); -} - -uint8_t* WebPDecodeYUVInto(const uint8_t* data, size_t data_size, - uint8_t* luma, size_t luma_size, int luma_stride, - uint8_t* u, size_t u_size, int u_stride, - uint8_t* v, size_t v_size, int v_stride) { - WebPDecParams params; - WebPDecBuffer output; - if (luma == NULL) return NULL; - WebPInitDecBuffer(&output); - WebPResetDecParams(¶ms); - params.output = &output; - output.colorspace = MODE_YUV; - output.u.YUVA.y = luma; - output.u.YUVA.y_stride = luma_stride; - output.u.YUVA.y_size = luma_size; - output.u.YUVA.u = u; - output.u.YUVA.u_stride = u_stride; - output.u.YUVA.u_size = u_size; - output.u.YUVA.v = v; - output.u.YUVA.v_stride = v_stride; - output.u.YUVA.v_size = v_size; - output.is_external_memory = 1; - if (DecodeInto(data, data_size, ¶ms) != VP8_STATUS_OK) { - return NULL; - } - return luma; -} - -//------------------------------------------------------------------------------ - -static uint8_t* Decode(WEBP_CSP_MODE mode, const uint8_t* const data, - size_t data_size, int* const width, int* const height, - WebPDecBuffer* const keep_info) { - WebPDecParams params; - WebPDecBuffer output; - - WebPInitDecBuffer(&output); - WebPResetDecParams(¶ms); - params.output = &output; - output.colorspace = mode; - - // Retrieve (and report back) the required dimensions from bitstream. - if (!WebPGetInfo(data, data_size, &output.width, &output.height)) { - return NULL; - } - if (width != NULL) *width = output.width; - if (height != NULL) *height = output.height; - - // Decode - if (DecodeInto(data, data_size, ¶ms) != VP8_STATUS_OK) { - return NULL; - } - if (keep_info != NULL) { // keep track of the side-info - WebPCopyDecBuffer(&output, keep_info); - } - // return decoded samples (don't clear 'output'!) - return WebPIsRGBMode(mode) ? output.u.RGBA.rgba : output.u.YUVA.y; -} - -uint8_t* WebPDecodeRGB(const uint8_t* data, size_t data_size, - int* width, int* height) { - return Decode(MODE_RGB, data, data_size, width, height, NULL); -} - -uint8_t* WebPDecodeRGBA(const uint8_t* data, size_t data_size, - int* width, int* height) { - return Decode(MODE_RGBA, data, data_size, width, height, NULL); -} - -uint8_t* WebPDecodeARGB(const uint8_t* data, size_t data_size, - int* width, int* height) { - return Decode(MODE_ARGB, data, data_size, width, height, NULL); -} - -uint8_t* WebPDecodeBGR(const uint8_t* data, size_t data_size, - int* width, int* height) { - return Decode(MODE_BGR, data, data_size, width, height, NULL); -} - -uint8_t* WebPDecodeBGRA(const uint8_t* data, size_t data_size, - int* width, int* height) { - return Decode(MODE_BGRA, data, data_size, width, height, NULL); -} - -uint8_t* WebPDecodeYUV(const uint8_t* data, size_t data_size, - int* width, int* height, uint8_t** u, uint8_t** v, - int* stride, int* uv_stride) { - WebPDecBuffer output; // only to preserve the side-infos - uint8_t* const out = Decode(MODE_YUV, data, data_size, - width, height, &output); - - if (out != NULL) { - const WebPYUVABuffer* const buf = &output.u.YUVA; - *u = buf->u; - *v = buf->v; - *stride = buf->y_stride; - *uv_stride = buf->u_stride; - assert(buf->u_stride == buf->v_stride); - } - return out; -} - -static void DefaultFeatures(WebPBitstreamFeatures* const features) { - assert(features != NULL); - memset(features, 0, sizeof(*features)); -} - -static VP8StatusCode GetFeatures(const uint8_t* const data, size_t data_size, - WebPBitstreamFeatures* const features) { - if (features == NULL || data == NULL) { - return VP8_STATUS_INVALID_PARAM; - } - DefaultFeatures(features); - - // Only parse enough of the data to retrieve the features. - return ParseHeadersInternal(data, data_size, - &features->width, &features->height, - &features->has_alpha, &features->has_animation, - &features->format, NULL); -} - -//------------------------------------------------------------------------------ -// WebPGetInfo() - -int WebPGetInfo(const uint8_t* data, size_t data_size, - int* width, int* height) { - WebPBitstreamFeatures features; - - if (GetFeatures(data, data_size, &features) != VP8_STATUS_OK) { - return 0; - } - - if (width != NULL) { - *width = features.width; - } - if (height != NULL) { - *height = features.height; - } - - return 1; -} - -//------------------------------------------------------------------------------ -// Advance decoding API - -int WebPInitDecoderConfigInternal(WebPDecoderConfig* config, - int version) { - if (WEBP_ABI_IS_INCOMPATIBLE(version, WEBP_DECODER_ABI_VERSION)) { - return 0; // version mismatch - } - if (config == NULL) { - return 0; - } - memset(config, 0, sizeof(*config)); - DefaultFeatures(&config->input); - WebPInitDecBuffer(&config->output); - return 1; -} - -VP8StatusCode WebPGetFeaturesInternal(const uint8_t* data, size_t data_size, - WebPBitstreamFeatures* features, - int version) { - if (WEBP_ABI_IS_INCOMPATIBLE(version, WEBP_DECODER_ABI_VERSION)) { - return VP8_STATUS_INVALID_PARAM; // version mismatch - } - if (features == NULL) { - return VP8_STATUS_INVALID_PARAM; - } - return GetFeatures(data, data_size, features); -} - -VP8StatusCode WebPDecode(const uint8_t* data, size_t data_size, - WebPDecoderConfig* config) { - WebPDecParams params; - VP8StatusCode status; - - if (config == NULL) { - return VP8_STATUS_INVALID_PARAM; - } - - status = GetFeatures(data, data_size, &config->input); - if (status != VP8_STATUS_OK) { - if (status == VP8_STATUS_NOT_ENOUGH_DATA) { - return VP8_STATUS_BITSTREAM_ERROR; // Not-enough-data treated as error. - } - return status; - } - - WebPResetDecParams(¶ms); - params.options = &config->options; - params.output = &config->output; - if (WebPAvoidSlowMemory(params.output, &config->input)) { - // decoding to slow memory: use a temporary in-mem buffer to decode into. - WebPDecBuffer in_mem_buffer; - WebPInitDecBuffer(&in_mem_buffer); - in_mem_buffer.colorspace = config->output.colorspace; - in_mem_buffer.width = config->input.width; - in_mem_buffer.height = config->input.height; - params.output = &in_mem_buffer; - status = DecodeInto(data, data_size, ¶ms); - if (status == VP8_STATUS_OK) { // do the slow-copy - status = WebPCopyDecBufferPixels(&in_mem_buffer, &config->output); - } - WebPFreeDecBuffer(&in_mem_buffer); - } else { - status = DecodeInto(data, data_size, ¶ms); - } - - return status; -} - -//------------------------------------------------------------------------------ -// Cropping and rescaling. - -int WebPIoInitFromOptions(const WebPDecoderOptions* const options, - VP8Io* const io, WEBP_CSP_MODE src_colorspace) { - const int W = io->width; - const int H = io->height; - int x = 0, y = 0, w = W, h = H; - - // Cropping - io->use_cropping = (options != NULL) && (options->use_cropping > 0); - if (io->use_cropping) { - w = options->crop_width; - h = options->crop_height; - x = options->crop_left; - y = options->crop_top; - if (!WebPIsRGBMode(src_colorspace)) { // only snap for YUV420 - x &= ~1; - y &= ~1; - } - if (x < 0 || y < 0 || w <= 0 || h <= 0 || x + w > W || y + h > H) { - return 0; // out of frame boundary error - } - } - io->crop_left = x; - io->crop_top = y; - io->crop_right = x + w; - io->crop_bottom = y + h; - io->mb_w = w; - io->mb_h = h; - - // Scaling - io->use_scaling = (options != NULL) && (options->use_scaling > 0); - if (io->use_scaling) { - int scaled_width = options->scaled_width; - int scaled_height = options->scaled_height; - if (!WebPRescalerGetScaledDimensions(w, h, &scaled_width, &scaled_height)) { - return 0; - } - io->scaled_width = scaled_width; - io->scaled_height = scaled_height; - } - - // Filter - io->bypass_filtering = (options != NULL) && options->bypass_filtering; - - // Fancy upsampler -#ifdef FANCY_UPSAMPLING - io->fancy_upsampling = (options == NULL) || (!options->no_fancy_upsampling); -#endif - - if (io->use_scaling) { - // disable filter (only for large downscaling ratio). - io->bypass_filtering = (io->scaled_width < W * 3 / 4) && - (io->scaled_height < H * 3 / 4); - io->fancy_upsampling = 0; - } - return 1; -} - -//------------------------------------------------------------------------------ diff --git a/thirdparty/libwebp/dec/webp_dec.c b/thirdparty/libwebp/dec/webp_dec.c new file mode 100644 index 0000000000..a8e9c2c510 --- /dev/null +++ b/thirdparty/libwebp/dec/webp_dec.c @@ -0,0 +1,843 @@ +// Copyright 2010 Google Inc. All Rights Reserved. +// +// Use of this source code is governed by a BSD-style license +// that can be found in the COPYING file in the root of the source +// tree. An additional intellectual property rights grant can be found +// in the file PATENTS. All contributing project authors may +// be found in the AUTHORS file in the root of the source tree. +// ----------------------------------------------------------------------------- +// +// Main decoding functions for WEBP images. +// +// Author: Skal (pascal.massimino@gmail.com) + +#include + +#include "./vp8i_dec.h" +#include "./vp8li_dec.h" +#include "./webpi_dec.h" +#include "../utils/utils.h" +#include "../webp/mux_types.h" // ALPHA_FLAG + +//------------------------------------------------------------------------------ +// RIFF layout is: +// Offset tag +// 0...3 "RIFF" 4-byte tag +// 4...7 size of image data (including metadata) starting at offset 8 +// 8...11 "WEBP" our form-type signature +// The RIFF container (12 bytes) is followed by appropriate chunks: +// 12..15 "VP8 ": 4-bytes tags, signaling the use of VP8 video format +// 16..19 size of the raw VP8 image data, starting at offset 20 +// 20.... the VP8 bytes +// Or, +// 12..15 "VP8L": 4-bytes tags, signaling the use of VP8L lossless format +// 16..19 size of the raw VP8L image data, starting at offset 20 +// 20.... the VP8L bytes +// Or, +// 12..15 "VP8X": 4-bytes tags, describing the extended-VP8 chunk. +// 16..19 size of the VP8X chunk starting at offset 20. +// 20..23 VP8X flags bit-map corresponding to the chunk-types present. +// 24..26 Width of the Canvas Image. +// 27..29 Height of the Canvas Image. +// There can be extra chunks after the "VP8X" chunk (ICCP, ANMF, VP8, VP8L, +// XMP, EXIF ...) +// All sizes are in little-endian order. +// Note: chunk data size must be padded to multiple of 2 when written. + +// Validates the RIFF container (if detected) and skips over it. +// If a RIFF container is detected, returns: +// VP8_STATUS_BITSTREAM_ERROR for invalid header, +// VP8_STATUS_NOT_ENOUGH_DATA for truncated data if have_all_data is true, +// and VP8_STATUS_OK otherwise. +// In case there are not enough bytes (partial RIFF container), return 0 for +// *riff_size. Else return the RIFF size extracted from the header. +static VP8StatusCode ParseRIFF(const uint8_t** const data, + size_t* const data_size, int have_all_data, + size_t* const riff_size) { + assert(data != NULL); + assert(data_size != NULL); + assert(riff_size != NULL); + + *riff_size = 0; // Default: no RIFF present. + if (*data_size >= RIFF_HEADER_SIZE && !memcmp(*data, "RIFF", TAG_SIZE)) { + if (memcmp(*data + 8, "WEBP", TAG_SIZE)) { + return VP8_STATUS_BITSTREAM_ERROR; // Wrong image file signature. + } else { + const uint32_t size = GetLE32(*data + TAG_SIZE); + // Check that we have at least one chunk (i.e "WEBP" + "VP8?nnnn"). + if (size < TAG_SIZE + CHUNK_HEADER_SIZE) { + return VP8_STATUS_BITSTREAM_ERROR; + } + if (size > MAX_CHUNK_PAYLOAD) { + return VP8_STATUS_BITSTREAM_ERROR; + } + if (have_all_data && (size > *data_size - CHUNK_HEADER_SIZE)) { + return VP8_STATUS_NOT_ENOUGH_DATA; // Truncated bitstream. + } + // We have a RIFF container. Skip it. + *riff_size = size; + *data += RIFF_HEADER_SIZE; + *data_size -= RIFF_HEADER_SIZE; + } + } + return VP8_STATUS_OK; +} + +// Validates the VP8X header and skips over it. +// Returns VP8_STATUS_BITSTREAM_ERROR for invalid VP8X header, +// VP8_STATUS_NOT_ENOUGH_DATA in case of insufficient data, and +// VP8_STATUS_OK otherwise. +// If a VP8X chunk is found, found_vp8x is set to true and *width_ptr, +// *height_ptr and *flags_ptr are set to the corresponding values extracted +// from the VP8X chunk. +static VP8StatusCode ParseVP8X(const uint8_t** const data, + size_t* const data_size, + int* const found_vp8x, + int* const width_ptr, int* const height_ptr, + uint32_t* const flags_ptr) { + const uint32_t vp8x_size = CHUNK_HEADER_SIZE + VP8X_CHUNK_SIZE; + assert(data != NULL); + assert(data_size != NULL); + assert(found_vp8x != NULL); + + *found_vp8x = 0; + + if (*data_size < CHUNK_HEADER_SIZE) { + return VP8_STATUS_NOT_ENOUGH_DATA; // Insufficient data. + } + + if (!memcmp(*data, "VP8X", TAG_SIZE)) { + int width, height; + uint32_t flags; + const uint32_t chunk_size = GetLE32(*data + TAG_SIZE); + if (chunk_size != VP8X_CHUNK_SIZE) { + return VP8_STATUS_BITSTREAM_ERROR; // Wrong chunk size. + } + + // Verify if enough data is available to validate the VP8X chunk. + if (*data_size < vp8x_size) { + return VP8_STATUS_NOT_ENOUGH_DATA; // Insufficient data. + } + flags = GetLE32(*data + 8); + width = 1 + GetLE24(*data + 12); + height = 1 + GetLE24(*data + 15); + if (width * (uint64_t)height >= MAX_IMAGE_AREA) { + return VP8_STATUS_BITSTREAM_ERROR; // image is too large + } + + if (flags_ptr != NULL) *flags_ptr = flags; + if (width_ptr != NULL) *width_ptr = width; + if (height_ptr != NULL) *height_ptr = height; + // Skip over VP8X header bytes. + *data += vp8x_size; + *data_size -= vp8x_size; + *found_vp8x = 1; + } + return VP8_STATUS_OK; +} + +// Skips to the next VP8/VP8L chunk header in the data given the size of the +// RIFF chunk 'riff_size'. +// Returns VP8_STATUS_BITSTREAM_ERROR if any invalid chunk size is encountered, +// VP8_STATUS_NOT_ENOUGH_DATA in case of insufficient data, and +// VP8_STATUS_OK otherwise. +// If an alpha chunk is found, *alpha_data and *alpha_size are set +// appropriately. +static VP8StatusCode ParseOptionalChunks(const uint8_t** const data, + size_t* const data_size, + size_t const riff_size, + const uint8_t** const alpha_data, + size_t* const alpha_size) { + const uint8_t* buf; + size_t buf_size; + uint32_t total_size = TAG_SIZE + // "WEBP". + CHUNK_HEADER_SIZE + // "VP8Xnnnn". + VP8X_CHUNK_SIZE; // data. + assert(data != NULL); + assert(data_size != NULL); + buf = *data; + buf_size = *data_size; + + assert(alpha_data != NULL); + assert(alpha_size != NULL); + *alpha_data = NULL; + *alpha_size = 0; + + while (1) { + uint32_t chunk_size; + uint32_t disk_chunk_size; // chunk_size with padding + + *data = buf; + *data_size = buf_size; + + if (buf_size < CHUNK_HEADER_SIZE) { // Insufficient data. + return VP8_STATUS_NOT_ENOUGH_DATA; + } + + chunk_size = GetLE32(buf + TAG_SIZE); + if (chunk_size > MAX_CHUNK_PAYLOAD) { + return VP8_STATUS_BITSTREAM_ERROR; // Not a valid chunk size. + } + // For odd-sized chunk-payload, there's one byte padding at the end. + disk_chunk_size = (CHUNK_HEADER_SIZE + chunk_size + 1) & ~1; + total_size += disk_chunk_size; + + // Check that total bytes skipped so far does not exceed riff_size. + if (riff_size > 0 && (total_size > riff_size)) { + return VP8_STATUS_BITSTREAM_ERROR; // Not a valid chunk size. + } + + // Start of a (possibly incomplete) VP8/VP8L chunk implies that we have + // parsed all the optional chunks. + // Note: This check must occur before the check 'buf_size < disk_chunk_size' + // below to allow incomplete VP8/VP8L chunks. + if (!memcmp(buf, "VP8 ", TAG_SIZE) || + !memcmp(buf, "VP8L", TAG_SIZE)) { + return VP8_STATUS_OK; + } + + if (buf_size < disk_chunk_size) { // Insufficient data. + return VP8_STATUS_NOT_ENOUGH_DATA; + } + + if (!memcmp(buf, "ALPH", TAG_SIZE)) { // A valid ALPH header. + *alpha_data = buf + CHUNK_HEADER_SIZE; + *alpha_size = chunk_size; + } + + // We have a full and valid chunk; skip it. + buf += disk_chunk_size; + buf_size -= disk_chunk_size; + } +} + +// Validates the VP8/VP8L Header ("VP8 nnnn" or "VP8L nnnn") and skips over it. +// Returns VP8_STATUS_BITSTREAM_ERROR for invalid (chunk larger than +// riff_size) VP8/VP8L header, +// VP8_STATUS_NOT_ENOUGH_DATA in case of insufficient data, and +// VP8_STATUS_OK otherwise. +// If a VP8/VP8L chunk is found, *chunk_size is set to the total number of bytes +// extracted from the VP8/VP8L chunk header. +// The flag '*is_lossless' is set to 1 in case of VP8L chunk / raw VP8L data. +static VP8StatusCode ParseVP8Header(const uint8_t** const data_ptr, + size_t* const data_size, int have_all_data, + size_t riff_size, size_t* const chunk_size, + int* const is_lossless) { + const uint8_t* const data = *data_ptr; + const int is_vp8 = !memcmp(data, "VP8 ", TAG_SIZE); + const int is_vp8l = !memcmp(data, "VP8L", TAG_SIZE); + const uint32_t minimal_size = + TAG_SIZE + CHUNK_HEADER_SIZE; // "WEBP" + "VP8 nnnn" OR + // "WEBP" + "VP8Lnnnn" + assert(data != NULL); + assert(data_size != NULL); + assert(chunk_size != NULL); + assert(is_lossless != NULL); + + if (*data_size < CHUNK_HEADER_SIZE) { + return VP8_STATUS_NOT_ENOUGH_DATA; // Insufficient data. + } + + if (is_vp8 || is_vp8l) { + // Bitstream contains VP8/VP8L header. + const uint32_t size = GetLE32(data + TAG_SIZE); + if ((riff_size >= minimal_size) && (size > riff_size - minimal_size)) { + return VP8_STATUS_BITSTREAM_ERROR; // Inconsistent size information. + } + if (have_all_data && (size > *data_size - CHUNK_HEADER_SIZE)) { + return VP8_STATUS_NOT_ENOUGH_DATA; // Truncated bitstream. + } + // Skip over CHUNK_HEADER_SIZE bytes from VP8/VP8L Header. + *chunk_size = size; + *data_ptr += CHUNK_HEADER_SIZE; + *data_size -= CHUNK_HEADER_SIZE; + *is_lossless = is_vp8l; + } else { + // Raw VP8/VP8L bitstream (no header). + *is_lossless = VP8LCheckSignature(data, *data_size); + *chunk_size = *data_size; + } + + return VP8_STATUS_OK; +} + +//------------------------------------------------------------------------------ + +// Fetch '*width', '*height', '*has_alpha' and fill out 'headers' based on +// 'data'. All the output parameters may be NULL. If 'headers' is NULL only the +// minimal amount will be read to fetch the remaining parameters. +// If 'headers' is non-NULL this function will attempt to locate both alpha +// data (with or without a VP8X chunk) and the bitstream chunk (VP8/VP8L). +// Note: The following chunk sequences (before the raw VP8/VP8L data) are +// considered valid by this function: +// RIFF + VP8(L) +// RIFF + VP8X + (optional chunks) + VP8(L) +// ALPH + VP8 <-- Not a valid WebP format: only allowed for internal purpose. +// VP8(L) <-- Not a valid WebP format: only allowed for internal purpose. +static VP8StatusCode ParseHeadersInternal(const uint8_t* data, + size_t data_size, + int* const width, + int* const height, + int* const has_alpha, + int* const has_animation, + int* const format, + WebPHeaderStructure* const headers) { + int canvas_width = 0; + int canvas_height = 0; + int image_width = 0; + int image_height = 0; + int found_riff = 0; + int found_vp8x = 0; + int animation_present = 0; + const int have_all_data = (headers != NULL) ? headers->have_all_data : 0; + + VP8StatusCode status; + WebPHeaderStructure hdrs; + + if (data == NULL || data_size < RIFF_HEADER_SIZE) { + return VP8_STATUS_NOT_ENOUGH_DATA; + } + memset(&hdrs, 0, sizeof(hdrs)); + hdrs.data = data; + hdrs.data_size = data_size; + + // Skip over RIFF header. + status = ParseRIFF(&data, &data_size, have_all_data, &hdrs.riff_size); + if (status != VP8_STATUS_OK) { + return status; // Wrong RIFF header / insufficient data. + } + found_riff = (hdrs.riff_size > 0); + + // Skip over VP8X. + { + uint32_t flags = 0; + status = ParseVP8X(&data, &data_size, &found_vp8x, + &canvas_width, &canvas_height, &flags); + if (status != VP8_STATUS_OK) { + return status; // Wrong VP8X / insufficient data. + } + animation_present = !!(flags & ANIMATION_FLAG); + if (!found_riff && found_vp8x) { + // Note: This restriction may be removed in the future, if it becomes + // necessary to send VP8X chunk to the decoder. + return VP8_STATUS_BITSTREAM_ERROR; + } + if (has_alpha != NULL) *has_alpha = !!(flags & ALPHA_FLAG); + if (has_animation != NULL) *has_animation = animation_present; + if (format != NULL) *format = 0; // default = undefined + + image_width = canvas_width; + image_height = canvas_height; + if (found_vp8x && animation_present && headers == NULL) { + status = VP8_STATUS_OK; + goto ReturnWidthHeight; // Just return features from VP8X header. + } + } + + if (data_size < TAG_SIZE) { + status = VP8_STATUS_NOT_ENOUGH_DATA; + goto ReturnWidthHeight; + } + + // Skip over optional chunks if data started with "RIFF + VP8X" or "ALPH". + if ((found_riff && found_vp8x) || + (!found_riff && !found_vp8x && !memcmp(data, "ALPH", TAG_SIZE))) { + status = ParseOptionalChunks(&data, &data_size, hdrs.riff_size, + &hdrs.alpha_data, &hdrs.alpha_data_size); + if (status != VP8_STATUS_OK) { + goto ReturnWidthHeight; // Invalid chunk size / insufficient data. + } + } + + // Skip over VP8/VP8L header. + status = ParseVP8Header(&data, &data_size, have_all_data, hdrs.riff_size, + &hdrs.compressed_size, &hdrs.is_lossless); + if (status != VP8_STATUS_OK) { + goto ReturnWidthHeight; // Wrong VP8/VP8L chunk-header / insufficient data. + } + if (hdrs.compressed_size > MAX_CHUNK_PAYLOAD) { + return VP8_STATUS_BITSTREAM_ERROR; + } + + if (format != NULL && !animation_present) { + *format = hdrs.is_lossless ? 2 : 1; + } + + if (!hdrs.is_lossless) { + if (data_size < VP8_FRAME_HEADER_SIZE) { + status = VP8_STATUS_NOT_ENOUGH_DATA; + goto ReturnWidthHeight; + } + // Validates raw VP8 data. + if (!VP8GetInfo(data, data_size, (uint32_t)hdrs.compressed_size, + &image_width, &image_height)) { + return VP8_STATUS_BITSTREAM_ERROR; + } + } else { + if (data_size < VP8L_FRAME_HEADER_SIZE) { + status = VP8_STATUS_NOT_ENOUGH_DATA; + goto ReturnWidthHeight; + } + // Validates raw VP8L data. + if (!VP8LGetInfo(data, data_size, &image_width, &image_height, has_alpha)) { + return VP8_STATUS_BITSTREAM_ERROR; + } + } + // Validates image size coherency. + if (found_vp8x) { + if (canvas_width != image_width || canvas_height != image_height) { + return VP8_STATUS_BITSTREAM_ERROR; + } + } + if (headers != NULL) { + *headers = hdrs; + headers->offset = data - headers->data; + assert((uint64_t)(data - headers->data) < MAX_CHUNK_PAYLOAD); + assert(headers->offset == headers->data_size - data_size); + } + ReturnWidthHeight: + if (status == VP8_STATUS_OK || + (status == VP8_STATUS_NOT_ENOUGH_DATA && found_vp8x && headers == NULL)) { + if (has_alpha != NULL) { + // If the data did not contain a VP8X/VP8L chunk the only definitive way + // to set this is by looking for alpha data (from an ALPH chunk). + *has_alpha |= (hdrs.alpha_data != NULL); + } + if (width != NULL) *width = image_width; + if (height != NULL) *height = image_height; + return VP8_STATUS_OK; + } else { + return status; + } +} + +VP8StatusCode WebPParseHeaders(WebPHeaderStructure* const headers) { + // status is marked volatile as a workaround for a clang-3.8 (aarch64) bug + volatile VP8StatusCode status; + int has_animation = 0; + assert(headers != NULL); + // fill out headers, ignore width/height/has_alpha. + status = ParseHeadersInternal(headers->data, headers->data_size, + NULL, NULL, NULL, &has_animation, + NULL, headers); + if (status == VP8_STATUS_OK || status == VP8_STATUS_NOT_ENOUGH_DATA) { + // TODO(jzern): full support of animation frames will require API additions. + if (has_animation) { + status = VP8_STATUS_UNSUPPORTED_FEATURE; + } + } + return status; +} + +//------------------------------------------------------------------------------ +// WebPDecParams + +void WebPResetDecParams(WebPDecParams* const params) { + if (params != NULL) { + memset(params, 0, sizeof(*params)); + } +} + +//------------------------------------------------------------------------------ +// "Into" decoding variants + +// Main flow +static VP8StatusCode DecodeInto(const uint8_t* const data, size_t data_size, + WebPDecParams* const params) { + VP8StatusCode status; + VP8Io io; + WebPHeaderStructure headers; + + headers.data = data; + headers.data_size = data_size; + headers.have_all_data = 1; + status = WebPParseHeaders(&headers); // Process Pre-VP8 chunks. + if (status != VP8_STATUS_OK) { + return status; + } + + assert(params != NULL); + VP8InitIo(&io); + io.data = headers.data + headers.offset; + io.data_size = headers.data_size - headers.offset; + WebPInitCustomIo(params, &io); // Plug the I/O functions. + + if (!headers.is_lossless) { + VP8Decoder* const dec = VP8New(); + if (dec == NULL) { + return VP8_STATUS_OUT_OF_MEMORY; + } + dec->alpha_data_ = headers.alpha_data; + dec->alpha_data_size_ = headers.alpha_data_size; + + // Decode bitstream header, update io->width/io->height. + if (!VP8GetHeaders(dec, &io)) { + status = dec->status_; // An error occurred. Grab error status. + } else { + // Allocate/check output buffers. + status = WebPAllocateDecBuffer(io.width, io.height, params->options, + params->output); + if (status == VP8_STATUS_OK) { // Decode + // This change must be done before calling VP8Decode() + dec->mt_method_ = VP8GetThreadMethod(params->options, &headers, + io.width, io.height); + VP8InitDithering(params->options, dec); + if (!VP8Decode(dec, &io)) { + status = dec->status_; + } + } + } + VP8Delete(dec); + } else { + VP8LDecoder* const dec = VP8LNew(); + if (dec == NULL) { + return VP8_STATUS_OUT_OF_MEMORY; + } + if (!VP8LDecodeHeader(dec, &io)) { + status = dec->status_; // An error occurred. Grab error status. + } else { + // Allocate/check output buffers. + status = WebPAllocateDecBuffer(io.width, io.height, params->options, + params->output); + if (status == VP8_STATUS_OK) { // Decode + if (!VP8LDecodeImage(dec)) { + status = dec->status_; + } + } + } + VP8LDelete(dec); + } + + if (status != VP8_STATUS_OK) { + WebPFreeDecBuffer(params->output); + } else { + if (params->options != NULL && params->options->flip) { + // This restores the original stride values if options->flip was used + // during the call to WebPAllocateDecBuffer above. + status = WebPFlipBuffer(params->output); + } + } + return status; +} + +// Helpers +static uint8_t* DecodeIntoRGBABuffer(WEBP_CSP_MODE colorspace, + const uint8_t* const data, + size_t data_size, + uint8_t* const rgba, + int stride, size_t size) { + WebPDecParams params; + WebPDecBuffer buf; + if (rgba == NULL) { + return NULL; + } + WebPInitDecBuffer(&buf); + WebPResetDecParams(¶ms); + params.output = &buf; + buf.colorspace = colorspace; + buf.u.RGBA.rgba = rgba; + buf.u.RGBA.stride = stride; + buf.u.RGBA.size = size; + buf.is_external_memory = 1; + if (DecodeInto(data, data_size, ¶ms) != VP8_STATUS_OK) { + return NULL; + } + return rgba; +} + +uint8_t* WebPDecodeRGBInto(const uint8_t* data, size_t data_size, + uint8_t* output, size_t size, int stride) { + return DecodeIntoRGBABuffer(MODE_RGB, data, data_size, output, stride, size); +} + +uint8_t* WebPDecodeRGBAInto(const uint8_t* data, size_t data_size, + uint8_t* output, size_t size, int stride) { + return DecodeIntoRGBABuffer(MODE_RGBA, data, data_size, output, stride, size); +} + +uint8_t* WebPDecodeARGBInto(const uint8_t* data, size_t data_size, + uint8_t* output, size_t size, int stride) { + return DecodeIntoRGBABuffer(MODE_ARGB, data, data_size, output, stride, size); +} + +uint8_t* WebPDecodeBGRInto(const uint8_t* data, size_t data_size, + uint8_t* output, size_t size, int stride) { + return DecodeIntoRGBABuffer(MODE_BGR, data, data_size, output, stride, size); +} + +uint8_t* WebPDecodeBGRAInto(const uint8_t* data, size_t data_size, + uint8_t* output, size_t size, int stride) { + return DecodeIntoRGBABuffer(MODE_BGRA, data, data_size, output, stride, size); +} + +uint8_t* WebPDecodeYUVInto(const uint8_t* data, size_t data_size, + uint8_t* luma, size_t luma_size, int luma_stride, + uint8_t* u, size_t u_size, int u_stride, + uint8_t* v, size_t v_size, int v_stride) { + WebPDecParams params; + WebPDecBuffer output; + if (luma == NULL) return NULL; + WebPInitDecBuffer(&output); + WebPResetDecParams(¶ms); + params.output = &output; + output.colorspace = MODE_YUV; + output.u.YUVA.y = luma; + output.u.YUVA.y_stride = luma_stride; + output.u.YUVA.y_size = luma_size; + output.u.YUVA.u = u; + output.u.YUVA.u_stride = u_stride; + output.u.YUVA.u_size = u_size; + output.u.YUVA.v = v; + output.u.YUVA.v_stride = v_stride; + output.u.YUVA.v_size = v_size; + output.is_external_memory = 1; + if (DecodeInto(data, data_size, ¶ms) != VP8_STATUS_OK) { + return NULL; + } + return luma; +} + +//------------------------------------------------------------------------------ + +static uint8_t* Decode(WEBP_CSP_MODE mode, const uint8_t* const data, + size_t data_size, int* const width, int* const height, + WebPDecBuffer* const keep_info) { + WebPDecParams params; + WebPDecBuffer output; + + WebPInitDecBuffer(&output); + WebPResetDecParams(¶ms); + params.output = &output; + output.colorspace = mode; + + // Retrieve (and report back) the required dimensions from bitstream. + if (!WebPGetInfo(data, data_size, &output.width, &output.height)) { + return NULL; + } + if (width != NULL) *width = output.width; + if (height != NULL) *height = output.height; + + // Decode + if (DecodeInto(data, data_size, ¶ms) != VP8_STATUS_OK) { + return NULL; + } + if (keep_info != NULL) { // keep track of the side-info + WebPCopyDecBuffer(&output, keep_info); + } + // return decoded samples (don't clear 'output'!) + return WebPIsRGBMode(mode) ? output.u.RGBA.rgba : output.u.YUVA.y; +} + +uint8_t* WebPDecodeRGB(const uint8_t* data, size_t data_size, + int* width, int* height) { + return Decode(MODE_RGB, data, data_size, width, height, NULL); +} + +uint8_t* WebPDecodeRGBA(const uint8_t* data, size_t data_size, + int* width, int* height) { + return Decode(MODE_RGBA, data, data_size, width, height, NULL); +} + +uint8_t* WebPDecodeARGB(const uint8_t* data, size_t data_size, + int* width, int* height) { + return Decode(MODE_ARGB, data, data_size, width, height, NULL); +} + +uint8_t* WebPDecodeBGR(const uint8_t* data, size_t data_size, + int* width, int* height) { + return Decode(MODE_BGR, data, data_size, width, height, NULL); +} + +uint8_t* WebPDecodeBGRA(const uint8_t* data, size_t data_size, + int* width, int* height) { + return Decode(MODE_BGRA, data, data_size, width, height, NULL); +} + +uint8_t* WebPDecodeYUV(const uint8_t* data, size_t data_size, + int* width, int* height, uint8_t** u, uint8_t** v, + int* stride, int* uv_stride) { + WebPDecBuffer output; // only to preserve the side-infos + uint8_t* const out = Decode(MODE_YUV, data, data_size, + width, height, &output); + + if (out != NULL) { + const WebPYUVABuffer* const buf = &output.u.YUVA; + *u = buf->u; + *v = buf->v; + *stride = buf->y_stride; + *uv_stride = buf->u_stride; + assert(buf->u_stride == buf->v_stride); + } + return out; +} + +static void DefaultFeatures(WebPBitstreamFeatures* const features) { + assert(features != NULL); + memset(features, 0, sizeof(*features)); +} + +static VP8StatusCode GetFeatures(const uint8_t* const data, size_t data_size, + WebPBitstreamFeatures* const features) { + if (features == NULL || data == NULL) { + return VP8_STATUS_INVALID_PARAM; + } + DefaultFeatures(features); + + // Only parse enough of the data to retrieve the features. + return ParseHeadersInternal(data, data_size, + &features->width, &features->height, + &features->has_alpha, &features->has_animation, + &features->format, NULL); +} + +//------------------------------------------------------------------------------ +// WebPGetInfo() + +int WebPGetInfo(const uint8_t* data, size_t data_size, + int* width, int* height) { + WebPBitstreamFeatures features; + + if (GetFeatures(data, data_size, &features) != VP8_STATUS_OK) { + return 0; + } + + if (width != NULL) { + *width = features.width; + } + if (height != NULL) { + *height = features.height; + } + + return 1; +} + +//------------------------------------------------------------------------------ +// Advance decoding API + +int WebPInitDecoderConfigInternal(WebPDecoderConfig* config, + int version) { + if (WEBP_ABI_IS_INCOMPATIBLE(version, WEBP_DECODER_ABI_VERSION)) { + return 0; // version mismatch + } + if (config == NULL) { + return 0; + } + memset(config, 0, sizeof(*config)); + DefaultFeatures(&config->input); + WebPInitDecBuffer(&config->output); + return 1; +} + +VP8StatusCode WebPGetFeaturesInternal(const uint8_t* data, size_t data_size, + WebPBitstreamFeatures* features, + int version) { + if (WEBP_ABI_IS_INCOMPATIBLE(version, WEBP_DECODER_ABI_VERSION)) { + return VP8_STATUS_INVALID_PARAM; // version mismatch + } + if (features == NULL) { + return VP8_STATUS_INVALID_PARAM; + } + return GetFeatures(data, data_size, features); +} + +VP8StatusCode WebPDecode(const uint8_t* data, size_t data_size, + WebPDecoderConfig* config) { + WebPDecParams params; + VP8StatusCode status; + + if (config == NULL) { + return VP8_STATUS_INVALID_PARAM; + } + + status = GetFeatures(data, data_size, &config->input); + if (status != VP8_STATUS_OK) { + if (status == VP8_STATUS_NOT_ENOUGH_DATA) { + return VP8_STATUS_BITSTREAM_ERROR; // Not-enough-data treated as error. + } + return status; + } + + WebPResetDecParams(¶ms); + params.options = &config->options; + params.output = &config->output; + if (WebPAvoidSlowMemory(params.output, &config->input)) { + // decoding to slow memory: use a temporary in-mem buffer to decode into. + WebPDecBuffer in_mem_buffer; + WebPInitDecBuffer(&in_mem_buffer); + in_mem_buffer.colorspace = config->output.colorspace; + in_mem_buffer.width = config->input.width; + in_mem_buffer.height = config->input.height; + params.output = &in_mem_buffer; + status = DecodeInto(data, data_size, ¶ms); + if (status == VP8_STATUS_OK) { // do the slow-copy + status = WebPCopyDecBufferPixels(&in_mem_buffer, &config->output); + } + WebPFreeDecBuffer(&in_mem_buffer); + } else { + status = DecodeInto(data, data_size, ¶ms); + } + + return status; +} + +//------------------------------------------------------------------------------ +// Cropping and rescaling. + +int WebPIoInitFromOptions(const WebPDecoderOptions* const options, + VP8Io* const io, WEBP_CSP_MODE src_colorspace) { + const int W = io->width; + const int H = io->height; + int x = 0, y = 0, w = W, h = H; + + // Cropping + io->use_cropping = (options != NULL) && (options->use_cropping > 0); + if (io->use_cropping) { + w = options->crop_width; + h = options->crop_height; + x = options->crop_left; + y = options->crop_top; + if (!WebPIsRGBMode(src_colorspace)) { // only snap for YUV420 + x &= ~1; + y &= ~1; + } + if (x < 0 || y < 0 || w <= 0 || h <= 0 || x + w > W || y + h > H) { + return 0; // out of frame boundary error + } + } + io->crop_left = x; + io->crop_top = y; + io->crop_right = x + w; + io->crop_bottom = y + h; + io->mb_w = w; + io->mb_h = h; + + // Scaling + io->use_scaling = (options != NULL) && (options->use_scaling > 0); + if (io->use_scaling) { + int scaled_width = options->scaled_width; + int scaled_height = options->scaled_height; + if (!WebPRescalerGetScaledDimensions(w, h, &scaled_width, &scaled_height)) { + return 0; + } + io->scaled_width = scaled_width; + io->scaled_height = scaled_height; + } + + // Filter + io->bypass_filtering = (options != NULL) && options->bypass_filtering; + + // Fancy upsampler +#ifdef FANCY_UPSAMPLING + io->fancy_upsampling = (options == NULL) || (!options->no_fancy_upsampling); +#endif + + if (io->use_scaling) { + // disable filter (only for large downscaling ratio). + io->bypass_filtering = (io->scaled_width < W * 3 / 4) && + (io->scaled_height < H * 3 / 4); + io->fancy_upsampling = 0; + } + return 1; +} + +//------------------------------------------------------------------------------ diff --git a/thirdparty/libwebp/dec/webpi.h b/thirdparty/libwebp/dec/webpi.h deleted file mode 100644 index 991b194c22..0000000000 --- a/thirdparty/libwebp/dec/webpi.h +++ /dev/null @@ -1,142 +0,0 @@ -// Copyright 2011 Google Inc. All Rights Reserved. -// -// Use of this source code is governed by a BSD-style license -// that can be found in the COPYING file in the root of the source -// tree. An additional intellectual property rights grant can be found -// in the file PATENTS. All contributing project authors may -// be found in the AUTHORS file in the root of the source tree. -// ----------------------------------------------------------------------------- -// -// Internal header: WebP decoding parameters and custom IO on buffer -// -// Author: somnath@google.com (Somnath Banerjee) - -#ifndef WEBP_DEC_WEBPI_H_ -#define WEBP_DEC_WEBPI_H_ - -#ifdef __cplusplus -extern "C" { -#endif - -#include "../utils/rescaler.h" -#include "./decode_vp8.h" - -//------------------------------------------------------------------------------ -// WebPDecParams: Decoding output parameters. Transient internal object. - -typedef struct WebPDecParams WebPDecParams; -typedef int (*OutputFunc)(const VP8Io* const io, WebPDecParams* const p); -typedef int (*OutputAlphaFunc)(const VP8Io* const io, WebPDecParams* const p, - int expected_num_out_lines); -typedef int (*OutputRowFunc)(WebPDecParams* const p, int y_pos, - int max_out_lines); - -struct WebPDecParams { - WebPDecBuffer* output; // output buffer. - uint8_t* tmp_y, *tmp_u, *tmp_v; // cache for the fancy upsampler - // or used for tmp rescaling - - int last_y; // coordinate of the line that was last output - const WebPDecoderOptions* options; // if not NULL, use alt decoding features - // rescalers - WebPRescaler scaler_y, scaler_u, scaler_v, scaler_a; - void* memory; // overall scratch memory for the output work. - - OutputFunc emit; // output RGB or YUV samples - OutputAlphaFunc emit_alpha; // output alpha channel - OutputRowFunc emit_alpha_row; // output one line of rescaled alpha values - - WebPDecBuffer* final_output; // In case the user supplied a slow-memory - // output, we decode image in temporary buffer - // (this::output) and copy it here. - WebPDecBuffer tmp_buffer; // this::output will point to this one in case - // of slow memory. -}; - -// Should be called first, before any use of the WebPDecParams object. -void WebPResetDecParams(WebPDecParams* const params); - -// Delete all memory (after an error occurred, for instance) -void WebPFreeDecParams(WebPDecParams* const params); - -//------------------------------------------------------------------------------ -// Header parsing helpers - -// Structure storing a description of the RIFF headers. -typedef struct { - const uint8_t* data; // input buffer - size_t data_size; // input buffer size - int have_all_data; // true if all data is known to be available - size_t offset; // offset to main data chunk (VP8 or VP8L) - const uint8_t* alpha_data; // points to alpha chunk (if present) - size_t alpha_data_size; // alpha chunk size - size_t compressed_size; // VP8/VP8L compressed data size - size_t riff_size; // size of the riff payload (or 0 if absent) - int is_lossless; // true if a VP8L chunk is present -} WebPHeaderStructure; - -// Skips over all valid chunks prior to the first VP8/VP8L frame header. -// Returns: VP8_STATUS_OK, VP8_STATUS_BITSTREAM_ERROR (invalid header/chunk), -// VP8_STATUS_NOT_ENOUGH_DATA (partial input) or VP8_STATUS_UNSUPPORTED_FEATURE -// in the case of non-decodable features (animation for instance). -// In 'headers', compressed_size, offset, alpha_data, alpha_size, and lossless -// fields are updated appropriately upon success. -VP8StatusCode WebPParseHeaders(WebPHeaderStructure* const headers); - -//------------------------------------------------------------------------------ -// Misc utils - -// Initializes VP8Io with custom setup, io and teardown functions. The default -// hooks will use the supplied 'params' as io->opaque handle. -void WebPInitCustomIo(WebPDecParams* const params, VP8Io* const io); - -// Setup crop_xxx fields, mb_w and mb_h in io. 'src_colorspace' refers -// to the *compressed* format, not the output one. -int WebPIoInitFromOptions(const WebPDecoderOptions* const options, - VP8Io* const io, WEBP_CSP_MODE src_colorspace); - -//------------------------------------------------------------------------------ -// Internal functions regarding WebPDecBuffer memory (in buffer.c). -// Don't really need to be externally visible for now. - -// Prepare 'buffer' with the requested initial dimensions width/height. -// If no external storage is supplied, initializes buffer by allocating output -// memory and setting up the stride information. Validate the parameters. Return -// an error code in case of problem (no memory, or invalid stride / size / -// dimension / etc.). If *options is not NULL, also verify that the options' -// parameters are valid and apply them to the width/height dimensions of the -// output buffer. This takes cropping / scaling / rotation into account. -// Also incorporates the options->flip flag to flip the buffer parameters if -// needed. -VP8StatusCode WebPAllocateDecBuffer(int width, int height, - const WebPDecoderOptions* const options, - WebPDecBuffer* const buffer); - -// Flip buffer vertically by negating the various strides. -VP8StatusCode WebPFlipBuffer(WebPDecBuffer* const buffer); - -// Copy 'src' into 'dst' buffer, making sure 'dst' is not marked as owner of the -// memory (still held by 'src'). No pixels are copied. -void WebPCopyDecBuffer(const WebPDecBuffer* const src, - WebPDecBuffer* const dst); - -// Copy and transfer ownership from src to dst (beware of parameter order!) -void WebPGrabDecBuffer(WebPDecBuffer* const src, WebPDecBuffer* const dst); - -// Copy pixels from 'src' into a *preallocated* 'dst' buffer. Returns -// VP8_STATUS_INVALID_PARAM if the 'dst' is not set up correctly for the copy. -VP8StatusCode WebPCopyDecBufferPixels(const WebPDecBuffer* const src, - WebPDecBuffer* const dst); - -// Returns true if decoding will be slow with the current configuration -// and bitstream features. -int WebPAvoidSlowMemory(const WebPDecBuffer* const output, - const WebPBitstreamFeatures* const features); - -//------------------------------------------------------------------------------ - -#ifdef __cplusplus -} // extern "C" -#endif - -#endif /* WEBP_DEC_WEBPI_H_ */ diff --git a/thirdparty/libwebp/dec/webpi_dec.h b/thirdparty/libwebp/dec/webpi_dec.h new file mode 100644 index 0000000000..696abc1958 --- /dev/null +++ b/thirdparty/libwebp/dec/webpi_dec.h @@ -0,0 +1,133 @@ +// Copyright 2011 Google Inc. All Rights Reserved. +// +// Use of this source code is governed by a BSD-style license +// that can be found in the COPYING file in the root of the source +// tree. An additional intellectual property rights grant can be found +// in the file PATENTS. All contributing project authors may +// be found in the AUTHORS file in the root of the source tree. +// ----------------------------------------------------------------------------- +// +// Internal header: WebP decoding parameters and custom IO on buffer +// +// Author: somnath@google.com (Somnath Banerjee) + +#ifndef WEBP_DEC_WEBPI_H_ +#define WEBP_DEC_WEBPI_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +#include "../utils/rescaler_utils.h" +#include "./vp8_dec.h" + +//------------------------------------------------------------------------------ +// WebPDecParams: Decoding output parameters. Transient internal object. + +typedef struct WebPDecParams WebPDecParams; +typedef int (*OutputFunc)(const VP8Io* const io, WebPDecParams* const p); +typedef int (*OutputAlphaFunc)(const VP8Io* const io, WebPDecParams* const p, + int expected_num_out_lines); +typedef int (*OutputRowFunc)(WebPDecParams* const p, int y_pos, + int max_out_lines); + +struct WebPDecParams { + WebPDecBuffer* output; // output buffer. + uint8_t* tmp_y, *tmp_u, *tmp_v; // cache for the fancy upsampler + // or used for tmp rescaling + + int last_y; // coordinate of the line that was last output + const WebPDecoderOptions* options; // if not NULL, use alt decoding features + + WebPRescaler* scaler_y, *scaler_u, *scaler_v, *scaler_a; // rescalers + void* memory; // overall scratch memory for the output work. + + OutputFunc emit; // output RGB or YUV samples + OutputAlphaFunc emit_alpha; // output alpha channel + OutputRowFunc emit_alpha_row; // output one line of rescaled alpha values +}; + +// Should be called first, before any use of the WebPDecParams object. +void WebPResetDecParams(WebPDecParams* const params); + +//------------------------------------------------------------------------------ +// Header parsing helpers + +// Structure storing a description of the RIFF headers. +typedef struct { + const uint8_t* data; // input buffer + size_t data_size; // input buffer size + int have_all_data; // true if all data is known to be available + size_t offset; // offset to main data chunk (VP8 or VP8L) + const uint8_t* alpha_data; // points to alpha chunk (if present) + size_t alpha_data_size; // alpha chunk size + size_t compressed_size; // VP8/VP8L compressed data size + size_t riff_size; // size of the riff payload (or 0 if absent) + int is_lossless; // true if a VP8L chunk is present +} WebPHeaderStructure; + +// Skips over all valid chunks prior to the first VP8/VP8L frame header. +// Returns: VP8_STATUS_OK, VP8_STATUS_BITSTREAM_ERROR (invalid header/chunk), +// VP8_STATUS_NOT_ENOUGH_DATA (partial input) or VP8_STATUS_UNSUPPORTED_FEATURE +// in the case of non-decodable features (animation for instance). +// In 'headers', compressed_size, offset, alpha_data, alpha_size, and lossless +// fields are updated appropriately upon success. +VP8StatusCode WebPParseHeaders(WebPHeaderStructure* const headers); + +//------------------------------------------------------------------------------ +// Misc utils + +// Initializes VP8Io with custom setup, io and teardown functions. The default +// hooks will use the supplied 'params' as io->opaque handle. +void WebPInitCustomIo(WebPDecParams* const params, VP8Io* const io); + +// Setup crop_xxx fields, mb_w and mb_h in io. 'src_colorspace' refers +// to the *compressed* format, not the output one. +int WebPIoInitFromOptions(const WebPDecoderOptions* const options, + VP8Io* const io, WEBP_CSP_MODE src_colorspace); + +//------------------------------------------------------------------------------ +// Internal functions regarding WebPDecBuffer memory (in buffer.c). +// Don't really need to be externally visible for now. + +// Prepare 'buffer' with the requested initial dimensions width/height. +// If no external storage is supplied, initializes buffer by allocating output +// memory and setting up the stride information. Validate the parameters. Return +// an error code in case of problem (no memory, or invalid stride / size / +// dimension / etc.). If *options is not NULL, also verify that the options' +// parameters are valid and apply them to the width/height dimensions of the +// output buffer. This takes cropping / scaling / rotation into account. +// Also incorporates the options->flip flag to flip the buffer parameters if +// needed. +VP8StatusCode WebPAllocateDecBuffer(int width, int height, + const WebPDecoderOptions* const options, + WebPDecBuffer* const buffer); + +// Flip buffer vertically by negating the various strides. +VP8StatusCode WebPFlipBuffer(WebPDecBuffer* const buffer); + +// Copy 'src' into 'dst' buffer, making sure 'dst' is not marked as owner of the +// memory (still held by 'src'). No pixels are copied. +void WebPCopyDecBuffer(const WebPDecBuffer* const src, + WebPDecBuffer* const dst); + +// Copy and transfer ownership from src to dst (beware of parameter order!) +void WebPGrabDecBuffer(WebPDecBuffer* const src, WebPDecBuffer* const dst); + +// Copy pixels from 'src' into a *preallocated* 'dst' buffer. Returns +// VP8_STATUS_INVALID_PARAM if the 'dst' is not set up correctly for the copy. +VP8StatusCode WebPCopyDecBufferPixels(const WebPDecBuffer* const src, + WebPDecBuffer* const dst); + +// Returns true if decoding will be slow with the current configuration +// and bitstream features. +int WebPAvoidSlowMemory(const WebPDecBuffer* const output, + const WebPBitstreamFeatures* const features); + +//------------------------------------------------------------------------------ + +#ifdef __cplusplus +} // extern "C" +#endif + +#endif /* WEBP_DEC_WEBPI_H_ */ diff --git a/thirdparty/libwebp/demux/demux.c b/thirdparty/libwebp/demux/demux.c index 1cb9bd5780..100eab8c01 100644 --- a/thirdparty/libwebp/demux/demux.c +++ b/thirdparty/libwebp/demux/demux.c @@ -25,7 +25,7 @@ #define DMUX_MAJ_VERSION 0 #define DMUX_MIN_VERSION 3 -#define DMUX_REV_VERSION 1 +#define DMUX_REV_VERSION 2 typedef struct { size_t start_; // start location of the data @@ -590,7 +590,6 @@ static int CheckFrameBounds(const Frame* const frame, int exact, static int IsValidExtendedFormat(const WebPDemuxer* const dmux) { const int is_animation = !!(dmux->feature_flags_ & ANIMATION_FLAG); - const int is_fragmented = !!(dmux->feature_flags_ & FRAGMENTS_FLAG); const Frame* f = dmux->frames_; if (dmux->state_ == WEBP_DEMUX_PARSING_HEADER) return 1; @@ -598,7 +597,7 @@ static int IsValidExtendedFormat(const WebPDemuxer* const dmux) { if (dmux->canvas_width_ <= 0 || dmux->canvas_height_ <= 0) return 0; if (dmux->loop_count_ < 0) return 0; if (dmux->state_ == WEBP_DEMUX_DONE && dmux->frames_ == NULL) return 0; - if (is_fragmented) return 0; + if (dmux->feature_flags_ & ~ALL_VALID_FLAGS) return 0; // invalid bitstream while (f != NULL) { const int cur_frame_set = f->frame_num_; diff --git a/thirdparty/libwebp/dsp/alpha_processing.c b/thirdparty/libwebp/dsp/alpha_processing.c index 1716cace8d..4b60e092be 100644 --- a/thirdparty/libwebp/dsp/alpha_processing.c +++ b/thirdparty/libwebp/dsp/alpha_processing.c @@ -284,9 +284,9 @@ static void ApplyAlphaMultiply_16b(uint8_t* rgba4444, #endif } -static int DispatchAlpha(const uint8_t* alpha, int alpha_stride, - int width, int height, - uint8_t* dst, int dst_stride) { +static int DispatchAlpha_C(const uint8_t* alpha, int alpha_stride, + int width, int height, + uint8_t* dst, int dst_stride) { uint32_t alpha_mask = 0xff; int i, j; @@ -303,9 +303,9 @@ static int DispatchAlpha(const uint8_t* alpha, int alpha_stride, return (alpha_mask != 0xff); } -static void DispatchAlphaToGreen(const uint8_t* alpha, int alpha_stride, - int width, int height, - uint32_t* dst, int dst_stride) { +static void DispatchAlphaToGreen_C(const uint8_t* alpha, int alpha_stride, + int width, int height, + uint32_t* dst, int dst_stride) { int i, j; for (j = 0; j < height; ++j) { for (i = 0; i < width; ++i) { @@ -316,9 +316,9 @@ static void DispatchAlphaToGreen(const uint8_t* alpha, int alpha_stride, } } -static int ExtractAlpha(const uint8_t* argb, int argb_stride, - int width, int height, - uint8_t* alpha, int alpha_stride) { +static int ExtractAlpha_C(const uint8_t* argb, int argb_stride, + int width, int height, + uint8_t* alpha, int alpha_stride) { uint8_t alpha_mask = 0xff; int i, j; @@ -334,11 +334,17 @@ static int ExtractAlpha(const uint8_t* argb, int argb_stride, return (alpha_mask == 0xff); } +static void ExtractGreen_C(const uint32_t* argb, uint8_t* alpha, int size) { + int i; + for (i = 0; i < size; ++i) alpha[i] = argb[i] >> 8; +} + void (*WebPApplyAlphaMultiply)(uint8_t*, int, int, int, int); void (*WebPApplyAlphaMultiply4444)(uint8_t*, int, int, int); int (*WebPDispatchAlpha)(const uint8_t*, int, int, int, uint8_t*, int); void (*WebPDispatchAlphaToGreen)(const uint8_t*, int, int, int, uint32_t*, int); int (*WebPExtractAlpha)(const uint8_t*, int, int, int, uint8_t*, int); +void (*WebPExtractGreen)(const uint32_t* argb, uint8_t* alpha, int size); //------------------------------------------------------------------------------ // Init function @@ -346,6 +352,7 @@ int (*WebPExtractAlpha)(const uint8_t*, int, int, int, uint8_t*, int); extern void WebPInitAlphaProcessingMIPSdspR2(void); extern void WebPInitAlphaProcessingSSE2(void); extern void WebPInitAlphaProcessingSSE41(void); +extern void WebPInitAlphaProcessingNEON(void); static volatile VP8CPUInfo alpha_processing_last_cpuinfo_used = (VP8CPUInfo)&alpha_processing_last_cpuinfo_used; @@ -357,9 +364,11 @@ WEBP_TSAN_IGNORE_FUNCTION void WebPInitAlphaProcessing(void) { WebPMultRow = WebPMultRowC; WebPApplyAlphaMultiply = ApplyAlphaMultiply; WebPApplyAlphaMultiply4444 = ApplyAlphaMultiply_16b; - WebPDispatchAlpha = DispatchAlpha; - WebPDispatchAlphaToGreen = DispatchAlphaToGreen; - WebPExtractAlpha = ExtractAlpha; + + WebPDispatchAlpha = DispatchAlpha_C; + WebPDispatchAlphaToGreen = DispatchAlphaToGreen_C; + WebPExtractAlpha = ExtractAlpha_C; + WebPExtractGreen = ExtractGreen_C; // If defined, use CPUInfo() to overwrite some pointers with faster versions. if (VP8GetCPUInfo != NULL) { @@ -373,6 +382,11 @@ WEBP_TSAN_IGNORE_FUNCTION void WebPInitAlphaProcessing(void) { #endif } #endif +#if defined(WEBP_USE_NEON) + if (VP8GetCPUInfo(kNEON)) { + WebPInitAlphaProcessingNEON(); + } +#endif #if defined(WEBP_USE_MIPS_DSP_R2) if (VP8GetCPUInfo(kMIPSdspR2)) { WebPInitAlphaProcessingMIPSdspR2(); diff --git a/thirdparty/libwebp/dsp/alpha_processing_neon.c b/thirdparty/libwebp/dsp/alpha_processing_neon.c new file mode 100644 index 0000000000..606a401cf7 --- /dev/null +++ b/thirdparty/libwebp/dsp/alpha_processing_neon.c @@ -0,0 +1,191 @@ +// Copyright 2017 Google Inc. All Rights Reserved. +// +// Use of this source code is governed by a BSD-style license +// that can be found in the COPYING file in the root of the source +// tree. An additional intellectual property rights grant can be found +// in the file PATENTS. All contributing project authors may +// be found in the AUTHORS file in the root of the source tree. +// ----------------------------------------------------------------------------- +// +// Utilities for processing transparent channel, NEON version. +// +// Author: Skal (pascal.massimino@gmail.com) + +#include "./dsp.h" + +#if defined(WEBP_USE_NEON) + +#include "./neon.h" + +//------------------------------------------------------------------------------ + +#define MULTIPLIER(a) ((a) * 0x8081) +#define PREMULTIPLY(x, m) (((x) * (m)) >> 23) + +#define MULTIPLY_BY_ALPHA(V, ALPHA, OTHER) do { \ + const uint8x8_t alpha = (V).val[(ALPHA)]; \ + const uint16x8_t r1 = vmull_u8((V).val[1], alpha); \ + const uint16x8_t g1 = vmull_u8((V).val[2], alpha); \ + const uint16x8_t b1 = vmull_u8((V).val[(OTHER)], alpha); \ + /* we use: v / 255 = (v + 1 + (v >> 8)) >> 8 */ \ + const uint16x8_t r2 = vsraq_n_u16(r1, r1, 8); \ + const uint16x8_t g2 = vsraq_n_u16(g1, g1, 8); \ + const uint16x8_t b2 = vsraq_n_u16(b1, b1, 8); \ + const uint16x8_t r3 = vaddq_u16(r2, kOne); \ + const uint16x8_t g3 = vaddq_u16(g2, kOne); \ + const uint16x8_t b3 = vaddq_u16(b2, kOne); \ + (V).val[1] = vshrn_n_u16(r3, 8); \ + (V).val[2] = vshrn_n_u16(g3, 8); \ + (V).val[(OTHER)] = vshrn_n_u16(b3, 8); \ +} while (0) + +static void ApplyAlphaMultiply_NEON(uint8_t* rgba, int alpha_first, + int w, int h, int stride) { + const uint16x8_t kOne = vdupq_n_u16(1u); + while (h-- > 0) { + uint32_t* const rgbx = (uint32_t*)rgba; + int i = 0; + if (alpha_first) { + for (; i + 8 <= w; i += 8) { + // load aaaa...|rrrr...|gggg...|bbbb... + uint8x8x4_t RGBX = vld4_u8((const uint8_t*)(rgbx + i)); + MULTIPLY_BY_ALPHA(RGBX, 0, 3); + vst4_u8((uint8_t*)(rgbx + i), RGBX); + } + } else { + for (; i + 8 <= w; i += 8) { + uint8x8x4_t RGBX = vld4_u8((const uint8_t*)(rgbx + i)); + MULTIPLY_BY_ALPHA(RGBX, 3, 0); + vst4_u8((uint8_t*)(rgbx + i), RGBX); + } + } + // Finish with left-overs. + for (; i < w; ++i) { + uint8_t* const rgb = rgba + (alpha_first ? 1 : 0); + const uint8_t* const alpha = rgba + (alpha_first ? 0 : 3); + const uint32_t a = alpha[4 * i]; + if (a != 0xff) { + const uint32_t mult = MULTIPLIER(a); + rgb[4 * i + 0] = PREMULTIPLY(rgb[4 * i + 0], mult); + rgb[4 * i + 1] = PREMULTIPLY(rgb[4 * i + 1], mult); + rgb[4 * i + 2] = PREMULTIPLY(rgb[4 * i + 2], mult); + } + } + rgba += stride; + } +} +#undef MULTIPLY_BY_ALPHA +#undef MULTIPLIER +#undef PREMULTIPLY + +//------------------------------------------------------------------------------ + +static int DispatchAlpha_NEON(const uint8_t* alpha, int alpha_stride, + int width, int height, + uint8_t* dst, int dst_stride) { + uint32_t alpha_mask = 0xffffffffu; + uint8x8_t mask8 = vdup_n_u8(0xff); + uint32_t tmp[2]; + int i, j; + for (j = 0; j < height; ++j) { + // We don't know if alpha is first or last in dst[] (depending on rgbA/Argb + // mode). So we must be sure dst[4*i + 8 - 1] is writable for the store. + // Hence the test with 'width - 1' instead of just 'width'. + for (i = 0; i + 8 <= width - 1; i += 8) { + uint8x8x4_t rgbX = vld4_u8((const uint8_t*)(dst + 4 * i)); + const uint8x8_t alphas = vld1_u8(alpha + i); + rgbX.val[0] = alphas; + vst4_u8((uint8_t*)(dst + 4 * i), rgbX); + mask8 = vand_u8(mask8, alphas); + } + for (; i < width; ++i) { + const uint32_t alpha_value = alpha[i]; + dst[4 * i] = alpha_value; + alpha_mask &= alpha_value; + } + alpha += alpha_stride; + dst += dst_stride; + } + vst1_u8((uint8_t*)tmp, mask8); + alpha_mask &= tmp[0]; + alpha_mask &= tmp[1]; + return (alpha_mask != 0xffffffffu); +} + +static void DispatchAlphaToGreen_NEON(const uint8_t* alpha, int alpha_stride, + int width, int height, + uint32_t* dst, int dst_stride) { + int i, j; + uint8x8x4_t greens; // leave A/R/B channels zero'd. + greens.val[0] = vdup_n_u8(0); + greens.val[2] = vdup_n_u8(0); + greens.val[3] = vdup_n_u8(0); + for (j = 0; j < height; ++j) { + for (i = 0; i + 8 <= width; i += 8) { + greens.val[1] = vld1_u8(alpha + i); + vst4_u8((uint8_t*)(dst + i), greens); + } + for (; i < width; ++i) dst[i] = alpha[i] << 8; + alpha += alpha_stride; + dst += dst_stride; + } +} + +static int ExtractAlpha_NEON(const uint8_t* argb, int argb_stride, + int width, int height, + uint8_t* alpha, int alpha_stride) { + uint32_t alpha_mask = 0xffffffffu; + uint8x8_t mask8 = vdup_n_u8(0xff); + uint32_t tmp[2]; + int i, j; + for (j = 0; j < height; ++j) { + // We don't know if alpha is first or last in dst[] (depending on rgbA/Argb + // mode). So we must be sure dst[4*i + 8 - 1] is writable for the store. + // Hence the test with 'width - 1' instead of just 'width'. + for (i = 0; i + 8 <= width - 1; i += 8) { + const uint8x8x4_t rgbX = vld4_u8((const uint8_t*)(argb + 4 * i)); + const uint8x8_t alphas = rgbX.val[0]; + vst1_u8((uint8_t*)(alpha + i), alphas); + mask8 = vand_u8(mask8, alphas); + } + for (; i < width; ++i) { + alpha[i] = argb[4 * i]; + alpha_mask &= alpha[i]; + } + argb += argb_stride; + alpha += alpha_stride; + } + vst1_u8((uint8_t*)tmp, mask8); + alpha_mask &= tmp[0]; + alpha_mask &= tmp[1]; + return (alpha_mask == 0xffffffffu); +} + +static void ExtractGreen_NEON(const uint32_t* argb, + uint8_t* alpha, int size) { + int i; + for (i = 0; i + 16 <= size; i += 16) { + const uint8x16x4_t rgbX = vld4q_u8((const uint8_t*)(argb + i)); + const uint8x16_t greens = rgbX.val[1]; + vst1q_u8(alpha + i, greens); + } + for (; i < size; ++i) alpha[i] = (argb[i] >> 8) & 0xff; +} + +//------------------------------------------------------------------------------ + +extern void WebPInitAlphaProcessingNEON(void); + +WEBP_TSAN_IGNORE_FUNCTION void WebPInitAlphaProcessingNEON(void) { + WebPApplyAlphaMultiply = ApplyAlphaMultiply_NEON; + WebPDispatchAlpha = DispatchAlpha_NEON; + WebPDispatchAlphaToGreen = DispatchAlphaToGreen_NEON; + WebPExtractAlpha = ExtractAlpha_NEON; + WebPExtractGreen = ExtractGreen_NEON; +} + +#else // !WEBP_USE_NEON + +WEBP_DSP_INIT_STUB(WebPInitAlphaProcessingNEON) + +#endif // WEBP_USE_NEON diff --git a/thirdparty/libwebp/dsp/alpha_processing_sse2.c b/thirdparty/libwebp/dsp/alpha_processing_sse2.c index 5acb481dcd..83dc559fac 100644 --- a/thirdparty/libwebp/dsp/alpha_processing_sse2.c +++ b/thirdparty/libwebp/dsp/alpha_processing_sse2.c @@ -150,46 +150,46 @@ static int ExtractAlpha(const uint8_t* argb, int argb_stride, #define PREMULTIPLY(x, m) (((x) * (m)) >> 23) // We can't use a 'const int' for the SHUFFLE value, because it has to be an -// immediate in the _mm_shufflexx_epi16() instruction. We really a macro here. -#define APPLY_ALPHA(RGBX, SHUFFLE, MASK, MULT) do { \ - const __m128i argb0 = _mm_loadl_epi64((__m128i*)&(RGBX)); \ - const __m128i argb1 = _mm_unpacklo_epi8(argb0, zero); \ - const __m128i alpha0 = _mm_and_si128(argb1, MASK); \ - const __m128i alpha1 = _mm_shufflelo_epi16(alpha0, SHUFFLE); \ - const __m128i alpha2 = _mm_shufflehi_epi16(alpha1, SHUFFLE); \ - /* alpha2 = [0 a0 a0 a0][0 a1 a1 a1] */ \ - const __m128i scale0 = _mm_mullo_epi16(alpha2, MULT); \ - const __m128i scale1 = _mm_mulhi_epu16(alpha2, MULT); \ - const __m128i argb2 = _mm_mulhi_epu16(argb1, scale0); \ - const __m128i argb3 = _mm_mullo_epi16(argb1, scale1); \ - const __m128i argb4 = _mm_adds_epu16(argb2, argb3); \ - const __m128i argb5 = _mm_srli_epi16(argb4, 7); \ - const __m128i argb6 = _mm_or_si128(argb5, alpha0); \ - const __m128i argb7 = _mm_packus_epi16(argb6, zero); \ - _mm_storel_epi64((__m128i*)&(RGBX), argb7); \ +// immediate in the _mm_shufflexx_epi16() instruction. We really need a macro. +// We use: v / 255 = (v * 0x8081) >> 23, where v = alpha * {r,g,b} is a 16bit +// value. +#define APPLY_ALPHA(RGBX, SHUFFLE) do { \ + const __m128i argb0 = _mm_loadu_si128((const __m128i*)&(RGBX)); \ + const __m128i argb1_lo = _mm_unpacklo_epi8(argb0, zero); \ + const __m128i argb1_hi = _mm_unpackhi_epi8(argb0, zero); \ + const __m128i alpha0_lo = _mm_or_si128(argb1_lo, kMask); \ + const __m128i alpha0_hi = _mm_or_si128(argb1_hi, kMask); \ + const __m128i alpha1_lo = _mm_shufflelo_epi16(alpha0_lo, SHUFFLE); \ + const __m128i alpha1_hi = _mm_shufflelo_epi16(alpha0_hi, SHUFFLE); \ + const __m128i alpha2_lo = _mm_shufflehi_epi16(alpha1_lo, SHUFFLE); \ + const __m128i alpha2_hi = _mm_shufflehi_epi16(alpha1_hi, SHUFFLE); \ + /* alpha2 = [ff a0 a0 a0][ff a1 a1 a1] */ \ + const __m128i A0_lo = _mm_mullo_epi16(alpha2_lo, argb1_lo); \ + const __m128i A0_hi = _mm_mullo_epi16(alpha2_hi, argb1_hi); \ + const __m128i A1_lo = _mm_mulhi_epu16(A0_lo, kMult); \ + const __m128i A1_hi = _mm_mulhi_epu16(A0_hi, kMult); \ + const __m128i A2_lo = _mm_srli_epi16(A1_lo, 7); \ + const __m128i A2_hi = _mm_srli_epi16(A1_hi, 7); \ + const __m128i A3 = _mm_packus_epi16(A2_lo, A2_hi); \ + _mm_storeu_si128((__m128i*)&(RGBX), A3); \ } while (0) -static void ApplyAlphaMultiply(uint8_t* rgba, int alpha_first, - int w, int h, int stride) { +static void ApplyAlphaMultiply_SSE2(uint8_t* rgba, int alpha_first, + int w, int h, int stride) { const __m128i zero = _mm_setzero_si128(); - const int kSpan = 2; - const int w2 = w & ~(kSpan - 1); + const __m128i kMult = _mm_set1_epi16(0x8081u); + const __m128i kMask = _mm_set_epi16(0, 0xff, 0xff, 0, 0, 0xff, 0xff, 0); + const int kSpan = 4; while (h-- > 0) { uint32_t* const rgbx = (uint32_t*)rgba; int i; if (!alpha_first) { - const __m128i kMask = _mm_set_epi16(0xff, 0, 0, 0, 0xff, 0, 0, 0); - const __m128i kMult = - _mm_set_epi16(0, 0x8081, 0x8081, 0x8081, 0, 0x8081, 0x8081, 0x8081); - for (i = 0; i < w2; i += kSpan) { - APPLY_ALPHA(rgbx[i], _MM_SHUFFLE(0, 3, 3, 3), kMask, kMult); + for (i = 0; i + kSpan <= w; i += kSpan) { + APPLY_ALPHA(rgbx[i], _MM_SHUFFLE(2, 3, 3, 3)); } } else { - const __m128i kMask = _mm_set_epi16(0, 0, 0, 0xff, 0, 0, 0, 0xff); - const __m128i kMult = - _mm_set_epi16(0x8081, 0x8081, 0x8081, 0, 0x8081, 0x8081, 0x8081, 0); - for (i = 0; i < w2; i += kSpan) { - APPLY_ALPHA(rgbx[i], _MM_SHUFFLE(0, 0, 0, 3), kMask, kMult); + for (i = 0; i + kSpan <= w; i += kSpan) { + APPLY_ALPHA(rgbx[i], _MM_SHUFFLE(0, 0, 0, 1)); } } // Finish with left-overs. @@ -213,64 +213,51 @@ static void ApplyAlphaMultiply(uint8_t* rgba, int alpha_first, // ----------------------------------------------------------------------------- // Apply alpha value to rows -// We use: kINV255 = (1 << 24) / 255 = 0x010101 -// So: a * kINV255 = (a << 16) | [(a << 8) | a] -// -> _mm_mulhi_epu16() takes care of the (a<<16) part, -// and _mm_mullo_epu16(a * 0x0101,...) takes care of the "(a << 8) | a" one. - -static void MultARGBRow(uint32_t* const ptr, int width, int inverse) { +static void MultARGBRow_SSE2(uint32_t* const ptr, int width, int inverse) { int x = 0; if (!inverse) { const int kSpan = 2; const __m128i zero = _mm_setzero_si128(); - const __m128i kRound = - _mm_set_epi16(0, 1 << 7, 1 << 7, 1 << 7, 0, 1 << 7, 1 << 7, 1 << 7); - const __m128i kMult = - _mm_set_epi16(0, 0x0101, 0x0101, 0x0101, 0, 0x0101, 0x0101, 0x0101); - const __m128i kOne64 = _mm_set_epi16(1u << 8, 0, 0, 0, 1u << 8, 0, 0, 0); - const int w2 = width & ~(kSpan - 1); - for (x = 0; x < w2; x += kSpan) { - const __m128i argb0 = _mm_loadl_epi64((__m128i*)&ptr[x]); - const __m128i argb1 = _mm_unpacklo_epi8(argb0, zero); - const __m128i tmp0 = _mm_shufflelo_epi16(argb1, _MM_SHUFFLE(3, 3, 3, 3)); - const __m128i tmp1 = _mm_shufflehi_epi16(tmp0, _MM_SHUFFLE(3, 3, 3, 3)); - const __m128i tmp2 = _mm_srli_epi64(tmp1, 16); - const __m128i scale0 = _mm_mullo_epi16(tmp1, kMult); - const __m128i scale1 = _mm_or_si128(tmp2, kOne64); - const __m128i argb2 = _mm_mulhi_epu16(argb1, scale0); - const __m128i argb3 = _mm_mullo_epi16(argb1, scale1); - const __m128i argb4 = _mm_adds_epu16(argb2, argb3); - const __m128i argb5 = _mm_adds_epu16(argb4, kRound); - const __m128i argb6 = _mm_srli_epi16(argb5, 8); - const __m128i argb7 = _mm_packus_epi16(argb6, zero); - _mm_storel_epi64((__m128i*)&ptr[x], argb7); + const __m128i k128 = _mm_set1_epi16(128); + const __m128i kMult = _mm_set1_epi16(0x0101); + const __m128i kMask = _mm_set_epi16(0, 0xff, 0, 0, 0, 0xff, 0, 0); + for (x = 0; x + kSpan <= width; x += kSpan) { + // To compute 'result = (int)(a * x / 255. + .5)', we use: + // tmp = a * v + 128, result = (tmp * 0x0101u) >> 16 + const __m128i A0 = _mm_loadl_epi64((const __m128i*)&ptr[x]); + const __m128i A1 = _mm_unpacklo_epi8(A0, zero); + const __m128i A2 = _mm_or_si128(A1, kMask); + const __m128i A3 = _mm_shufflelo_epi16(A2, _MM_SHUFFLE(2, 3, 3, 3)); + const __m128i A4 = _mm_shufflehi_epi16(A3, _MM_SHUFFLE(2, 3, 3, 3)); + // here, A4 = [ff a0 a0 a0][ff a1 a1 a1] + const __m128i A5 = _mm_mullo_epi16(A4, A1); + const __m128i A6 = _mm_add_epi16(A5, k128); + const __m128i A7 = _mm_mulhi_epu16(A6, kMult); + const __m128i A10 = _mm_packus_epi16(A7, zero); + _mm_storel_epi64((__m128i*)&ptr[x], A10); } } width -= x; if (width > 0) WebPMultARGBRowC(ptr + x, width, inverse); } -static void MultRow(uint8_t* const ptr, const uint8_t* const alpha, - int width, int inverse) { +static void MultRow_SSE2(uint8_t* const ptr, const uint8_t* const alpha, + int width, int inverse) { int x = 0; if (!inverse) { - const int kSpan = 8; const __m128i zero = _mm_setzero_si128(); - const __m128i kRound = _mm_set1_epi16(1 << 7); - const int w2 = width & ~(kSpan - 1); - for (x = 0; x < w2; x += kSpan) { + const __m128i k128 = _mm_set1_epi16(128); + const __m128i kMult = _mm_set1_epi16(0x0101); + for (x = 0; x + 8 <= width; x += 8) { const __m128i v0 = _mm_loadl_epi64((__m128i*)&ptr[x]); + const __m128i a0 = _mm_loadl_epi64((const __m128i*)&alpha[x]); const __m128i v1 = _mm_unpacklo_epi8(v0, zero); - const __m128i alpha0 = _mm_loadl_epi64((const __m128i*)&alpha[x]); - const __m128i alpha1 = _mm_unpacklo_epi8(alpha0, zero); - const __m128i alpha2 = _mm_unpacklo_epi8(alpha0, alpha0); - const __m128i v2 = _mm_mulhi_epu16(v1, alpha2); - const __m128i v3 = _mm_mullo_epi16(v1, alpha1); - const __m128i v4 = _mm_adds_epu16(v2, v3); - const __m128i v5 = _mm_adds_epu16(v4, kRound); - const __m128i v6 = _mm_srli_epi16(v5, 8); - const __m128i v7 = _mm_packus_epi16(v6, zero); - _mm_storel_epi64((__m128i*)&ptr[x], v7); + const __m128i a1 = _mm_unpacklo_epi8(a0, zero); + const __m128i v2 = _mm_mullo_epi16(v1, a1); + const __m128i v3 = _mm_add_epi16(v2, k128); + const __m128i v4 = _mm_mulhi_epu16(v3, kMult); + const __m128i v5 = _mm_packus_epi16(v4, zero); + _mm_storel_epi64((__m128i*)&ptr[x], v5); } } width -= x; @@ -283,9 +270,9 @@ static void MultRow(uint8_t* const ptr, const uint8_t* const alpha, extern void WebPInitAlphaProcessingSSE2(void); WEBP_TSAN_IGNORE_FUNCTION void WebPInitAlphaProcessingSSE2(void) { - WebPMultARGBRow = MultARGBRow; - WebPMultRow = MultRow; - WebPApplyAlphaMultiply = ApplyAlphaMultiply; + WebPMultARGBRow = MultARGBRow_SSE2; + WebPMultRow = MultRow_SSE2; + WebPApplyAlphaMultiply = ApplyAlphaMultiply_SSE2; WebPDispatchAlpha = DispatchAlpha; WebPDispatchAlphaToGreen = DispatchAlphaToGreen; WebPExtractAlpha = ExtractAlpha; diff --git a/thirdparty/libwebp/dsp/common_sse2.h b/thirdparty/libwebp/dsp/common_sse2.h index 7cea13fb3c..995d7cf4ea 100644 --- a/thirdparty/libwebp/dsp/common_sse2.h +++ b/thirdparty/libwebp/dsp/common_sse2.h @@ -100,6 +100,91 @@ static WEBP_INLINE void VP8Transpose_2_4x4_16b( // a03 a13 a23 a33 b03 b13 b23 b33 } +//------------------------------------------------------------------------------ +// Channel mixing. + +// Function used several times in VP8PlanarTo24b. +// It samples the in buffer as follows: one every two unsigned char is stored +// at the beginning of the buffer, while the other half is stored at the end. +#define VP8PlanarTo24bHelper(IN, OUT) \ + do { \ + const __m128i v_mask = _mm_set1_epi16(0x00ff); \ + /* Take one every two upper 8b values.*/ \ + (OUT##0) = _mm_packus_epi16(_mm_and_si128((IN##0), v_mask), \ + _mm_and_si128((IN##1), v_mask)); \ + (OUT##1) = _mm_packus_epi16(_mm_and_si128((IN##2), v_mask), \ + _mm_and_si128((IN##3), v_mask)); \ + (OUT##2) = _mm_packus_epi16(_mm_and_si128((IN##4), v_mask), \ + _mm_and_si128((IN##5), v_mask)); \ + /* Take one every two lower 8b values.*/ \ + (OUT##3) = _mm_packus_epi16(_mm_srli_epi16((IN##0), 8), \ + _mm_srli_epi16((IN##1), 8)); \ + (OUT##4) = _mm_packus_epi16(_mm_srli_epi16((IN##2), 8), \ + _mm_srli_epi16((IN##3), 8)); \ + (OUT##5) = _mm_packus_epi16(_mm_srli_epi16((IN##4), 8), \ + _mm_srli_epi16((IN##5), 8)); \ + } while (0) + +// Pack the planar buffers +// rrrr... rrrr... gggg... gggg... bbbb... bbbb.... +// triplet by triplet in the output buffer rgb as rgbrgbrgbrgb ... +static WEBP_INLINE void VP8PlanarTo24b(__m128i* const in0, __m128i* const in1, + __m128i* const in2, __m128i* const in3, + __m128i* const in4, __m128i* const in5) { + // The input is 6 registers of sixteen 8b but for the sake of explanation, + // let's take 6 registers of four 8b values. + // To pack, we will keep taking one every two 8b integer and move it + // around as follows: + // Input: + // r0r1r2r3 | r4r5r6r7 | g0g1g2g3 | g4g5g6g7 | b0b1b2b3 | b4b5b6b7 + // Split the 6 registers in two sets of 3 registers: the first set as the even + // 8b bytes, the second the odd ones: + // r0r2r4r6 | g0g2g4g6 | b0b2b4b6 | r1r3r5r7 | g1g3g5g7 | b1b3b5b7 + // Repeat the same permutations twice more: + // r0r4g0g4 | b0b4r1r5 | g1g5b1b5 | r2r6g2g6 | b2b6r3r7 | g3g7b3b7 + // r0g0b0r1 | g1b1r2g2 | b2r3g3b3 | r4g4b4r5 | g5b5r6g6 | b6r7g7b7 + __m128i tmp0, tmp1, tmp2, tmp3, tmp4, tmp5; + VP8PlanarTo24bHelper(*in, tmp); + VP8PlanarTo24bHelper(tmp, *in); + VP8PlanarTo24bHelper(*in, tmp); + // We need to do it two more times than the example as we have sixteen bytes. + { + __m128i out0, out1, out2, out3, out4, out5; + VP8PlanarTo24bHelper(tmp, out); + VP8PlanarTo24bHelper(out, *in); + } +} + +#undef VP8PlanarTo24bHelper + +// Convert four packed four-channel buffers like argbargbargbargb... into the +// split channels aaaaa ... rrrr ... gggg .... bbbbb ...... +static WEBP_INLINE void VP8L32bToPlanar(__m128i* const in0, + __m128i* const in1, + __m128i* const in2, + __m128i* const in3) { + // Column-wise transpose. + const __m128i A0 = _mm_unpacklo_epi8(*in0, *in1); + const __m128i A1 = _mm_unpackhi_epi8(*in0, *in1); + const __m128i A2 = _mm_unpacklo_epi8(*in2, *in3); + const __m128i A3 = _mm_unpackhi_epi8(*in2, *in3); + const __m128i B0 = _mm_unpacklo_epi8(A0, A1); + const __m128i B1 = _mm_unpackhi_epi8(A0, A1); + const __m128i B2 = _mm_unpacklo_epi8(A2, A3); + const __m128i B3 = _mm_unpackhi_epi8(A2, A3); + // C0 = g7 g6 ... g1 g0 | b7 b6 ... b1 b0 + // C1 = a7 a6 ... a1 a0 | r7 r6 ... r1 r0 + const __m128i C0 = _mm_unpacklo_epi8(B0, B1); + const __m128i C1 = _mm_unpackhi_epi8(B0, B1); + const __m128i C2 = _mm_unpacklo_epi8(B2, B3); + const __m128i C3 = _mm_unpackhi_epi8(B2, B3); + // Gather the channels. + *in0 = _mm_unpackhi_epi64(C1, C3); + *in1 = _mm_unpacklo_epi64(C1, C3); + *in2 = _mm_unpackhi_epi64(C0, C2); + *in3 = _mm_unpacklo_epi64(C0, C2); +} + #endif // WEBP_USE_SSE2 #ifdef __cplusplus diff --git a/thirdparty/libwebp/dsp/cost.c b/thirdparty/libwebp/dsp/cost.c index fe72d26e79..58ddea7248 100644 --- a/thirdparty/libwebp/dsp/cost.c +++ b/thirdparty/libwebp/dsp/cost.c @@ -10,7 +10,7 @@ // Author: Skal (pascal.massimino@gmail.com) #include "./dsp.h" -#include "../enc/cost.h" +#include "../enc/cost_enc.h" //------------------------------------------------------------------------------ // Boolean-cost cost table diff --git a/thirdparty/libwebp/dsp/cost_mips32.c b/thirdparty/libwebp/dsp/cost_mips32.c index d1e240e191..3102da877a 100644 --- a/thirdparty/libwebp/dsp/cost_mips32.c +++ b/thirdparty/libwebp/dsp/cost_mips32.c @@ -13,7 +13,7 @@ #if defined(WEBP_USE_MIPS32) -#include "../enc/cost.h" +#include "../enc/cost_enc.h" static int GetResidualCost(int ctx0, const VP8Residual* const res) { int temp0, temp1; diff --git a/thirdparty/libwebp/dsp/cost_mips_dsp_r2.c b/thirdparty/libwebp/dsp/cost_mips_dsp_r2.c index ce64067756..6ec8aeb610 100644 --- a/thirdparty/libwebp/dsp/cost_mips_dsp_r2.c +++ b/thirdparty/libwebp/dsp/cost_mips_dsp_r2.c @@ -13,7 +13,7 @@ #if defined(WEBP_USE_MIPS_DSP_R2) -#include "../enc/cost.h" +#include "../enc/cost_enc.h" static int GetResidualCost(int ctx0, const VP8Residual* const res) { int temp0, temp1; diff --git a/thirdparty/libwebp/dsp/cost_sse2.c b/thirdparty/libwebp/dsp/cost_sse2.c index 0cb1c1fa04..421d51fdd5 100644 --- a/thirdparty/libwebp/dsp/cost_sse2.c +++ b/thirdparty/libwebp/dsp/cost_sse2.c @@ -16,8 +16,8 @@ #if defined(WEBP_USE_SSE2) #include -#include "../enc/cost.h" -#include "../enc/vp8enci.h" +#include "../enc/cost_enc.h" +#include "../enc/vp8i_enc.h" #include "../utils/utils.h" //------------------------------------------------------------------------------ diff --git a/thirdparty/libwebp/dsp/cpu.c b/thirdparty/libwebp/dsp/cpu.c index cbb08db90a..b5583b6e9b 100644 --- a/thirdparty/libwebp/dsp/cpu.c +++ b/thirdparty/libwebp/dsp/cpu.c @@ -95,26 +95,62 @@ static WEBP_INLINE uint64_t xgetbv(void) { #endif #if defined(__i386__) || defined(__x86_64__) || defined(WEBP_MSC_SSE2) + +// helper function for run-time detection of slow SSSE3 platforms +static int CheckSlowModel(int info) { + // Table listing display models with longer latencies for the bsr instruction + // (ie 2 cycles vs 10/16 cycles) and some SSSE3 instructions like pshufb. + // Refer to Intel 64 and IA-32 Architectures Optimization Reference Manual. + static const uint8_t kSlowModels[] = { + 0x37, 0x4a, 0x4d, // Silvermont Microarchitecture + 0x1c, 0x26, 0x27 // Atom Microarchitecture + }; + const uint32_t model = ((info & 0xf0000) >> 12) | ((info >> 4) & 0xf); + const uint32_t family = (info >> 8) & 0xf; + if (family == 0x06) { + size_t i; + for (i = 0; i < sizeof(kSlowModels) / sizeof(kSlowModels[0]); ++i) { + if (model == kSlowModels[i]) return 1; + } + } + return 0; +} + static int x86CPUInfo(CPUFeature feature) { int max_cpuid_value; int cpu_info[4]; + int is_intel = 0; // get the highest feature value cpuid supports GetCPUInfo(cpu_info, 0); max_cpuid_value = cpu_info[0]; if (max_cpuid_value < 1) { return 0; + } else { + const int VENDOR_ID_INTEL_EBX = 0x756e6547; // uneG + const int VENDOR_ID_INTEL_EDX = 0x49656e69; // Ieni + const int VENDOR_ID_INTEL_ECX = 0x6c65746e; // letn + is_intel = (cpu_info[1] == VENDOR_ID_INTEL_EBX && + cpu_info[2] == VENDOR_ID_INTEL_ECX && + cpu_info[3] == VENDOR_ID_INTEL_EDX); // genuine Intel? } GetCPUInfo(cpu_info, 1); if (feature == kSSE2) { - return 0 != (cpu_info[3] & 0x04000000); + return !!(cpu_info[3] & (1 << 26)); } if (feature == kSSE3) { - return 0 != (cpu_info[2] & 0x00000001); + return !!(cpu_info[2] & (1 << 0)); + } + if (feature == kSlowSSSE3) { + if (is_intel && (cpu_info[2] & (1 << 0))) { // SSSE3? + return CheckSlowModel(cpu_info[0]); + } + return 0; } + if (feature == kSSE4_1) { - return 0 != (cpu_info[2] & 0x00080000); + return !!(cpu_info[2] & (1 << 19)); } if (feature == kAVX) { // bits 27 (OSXSAVE) & 28 (256-bit AVX) @@ -126,7 +162,7 @@ static int x86CPUInfo(CPUFeature feature) { if (feature == kAVX2) { if (x86CPUInfo(kAVX) && max_cpuid_value >= 7) { GetCPUInfo(cpu_info, 7); - return ((cpu_info[1] & 0x00000020) == 0x00000020); + return !!(cpu_info[1] & (1 << 5)); } } return 0; @@ -184,4 +220,3 @@ VP8CPUInfo VP8GetCPUInfo = mipsCPUInfo; #else VP8CPUInfo VP8GetCPUInfo = NULL; #endif - diff --git a/thirdparty/libwebp/dsp/dec.c b/thirdparty/libwebp/dsp/dec.c index 49bd16d976..007e985d8b 100644 --- a/thirdparty/libwebp/dsp/dec.c +++ b/thirdparty/libwebp/dsp/dec.c @@ -12,7 +12,7 @@ // Author: Skal (pascal.massimino@gmail.com) #include "./dsp.h" -#include "../dec/vp8i.h" +#include "../dec/vp8i_dec.h" #include "../utils/utils.h" //------------------------------------------------------------------------------ diff --git a/thirdparty/libwebp/dsp/dec_clip_tables.c b/thirdparty/libwebp/dsp/dec_clip_tables.c index 3b6dde86ba..74ba34c0bb 100644 --- a/thirdparty/libwebp/dsp/dec_clip_tables.c +++ b/thirdparty/libwebp/dsp/dec_clip_tables.c @@ -63,7 +63,7 @@ static const uint8_t abs0[255 + 255 + 1] = { 0xf9, 0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0xff }; -static const int8_t sclip1[1020 + 1020 + 1] = { +static const uint8_t sclip1[1020 + 1020 + 1] = { 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, @@ -236,7 +236,7 @@ static const int8_t sclip1[1020 + 1020 + 1] = { 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f }; -static const int8_t sclip2[112 + 112 + 1] = { +static const uint8_t sclip2[112 + 112 + 1] = { 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, @@ -339,8 +339,8 @@ static volatile int tables_ok = 0; #endif -const int8_t* const VP8ksclip1 = &sclip1[1020]; -const int8_t* const VP8ksclip2 = &sclip2[112]; +const int8_t* const VP8ksclip1 = (const int8_t*)&sclip1[1020]; +const int8_t* const VP8ksclip2 = (const int8_t*)&sclip2[112]; const uint8_t* const VP8kclip1 = &clip1[255]; const uint8_t* const VP8kabs0 = &abs0[255]; diff --git a/thirdparty/libwebp/dsp/dec_msa.c b/thirdparty/libwebp/dsp/dec_msa.c index f76055cab0..8d9c98c3cf 100644 --- a/thirdparty/libwebp/dsp/dec_msa.c +++ b/thirdparty/libwebp/dsp/dec_msa.c @@ -153,6 +153,820 @@ static void TransformAC3(const int16_t* in, uint8_t* dst) { ST4x4_UB(res0, res0, 3, 2, 1, 0, dst, BPS); } +//------------------------------------------------------------------------------ +// Edge filtering functions + +#define FLIP_SIGN2(in0, in1, out0, out1) { \ + out0 = (v16i8)__msa_xori_b(in0, 0x80); \ + out1 = (v16i8)__msa_xori_b(in1, 0x80); \ +} + +#define FLIP_SIGN4(in0, in1, in2, in3, out0, out1, out2, out3) { \ + FLIP_SIGN2(in0, in1, out0, out1); \ + FLIP_SIGN2(in2, in3, out2, out3); \ +} + +#define FILT_VAL(q0_m, p0_m, mask, filt) do { \ + v16i8 q0_sub_p0; \ + q0_sub_p0 = __msa_subs_s_b(q0_m, p0_m); \ + filt = __msa_adds_s_b(filt, q0_sub_p0); \ + filt = __msa_adds_s_b(filt, q0_sub_p0); \ + filt = __msa_adds_s_b(filt, q0_sub_p0); \ + filt = filt & mask; \ +} while (0) + +#define FILT2(q_m, p_m, q, p) do { \ + u_r = SRAI_H(temp1, 7); \ + u_r = __msa_sat_s_h(u_r, 7); \ + u_l = SRAI_H(temp3, 7); \ + u_l = __msa_sat_s_h(u_l, 7); \ + u = __msa_pckev_b((v16i8)u_l, (v16i8)u_r); \ + q_m = __msa_subs_s_b(q_m, u); \ + p_m = __msa_adds_s_b(p_m, u); \ + q = __msa_xori_b((v16u8)q_m, 0x80); \ + p = __msa_xori_b((v16u8)p_m, 0x80); \ +} while (0) + +#define LPF_FILTER4_4W(p1, p0, q0, q1, mask, hev) do { \ + v16i8 p1_m, p0_m, q0_m, q1_m; \ + v16i8 filt, t1, t2; \ + const v16i8 cnst4b = __msa_ldi_b(4); \ + const v16i8 cnst3b = __msa_ldi_b(3); \ + \ + FLIP_SIGN4(p1, p0, q0, q1, p1_m, p0_m, q0_m, q1_m); \ + filt = __msa_subs_s_b(p1_m, q1_m); \ + filt = filt & hev; \ + FILT_VAL(q0_m, p0_m, mask, filt); \ + t1 = __msa_adds_s_b(filt, cnst4b); \ + t1 = SRAI_B(t1, 3); \ + t2 = __msa_adds_s_b(filt, cnst3b); \ + t2 = SRAI_B(t2, 3); \ + q0_m = __msa_subs_s_b(q0_m, t1); \ + q0 = __msa_xori_b((v16u8)q0_m, 0x80); \ + p0_m = __msa_adds_s_b(p0_m, t2); \ + p0 = __msa_xori_b((v16u8)p0_m, 0x80); \ + filt = __msa_srari_b(t1, 1); \ + hev = __msa_xori_b(hev, 0xff); \ + filt = filt & hev; \ + q1_m = __msa_subs_s_b(q1_m, filt); \ + q1 = __msa_xori_b((v16u8)q1_m, 0x80); \ + p1_m = __msa_adds_s_b(p1_m, filt); \ + p1 = __msa_xori_b((v16u8)p1_m, 0x80); \ +} while (0) + +#define LPF_MBFILTER(p2, p1, p0, q0, q1, q2, mask, hev) do { \ + v16i8 p2_m, p1_m, p0_m, q2_m, q1_m, q0_m; \ + v16i8 u, filt, t1, t2, filt_sign; \ + v8i16 filt_r, filt_l, u_r, u_l; \ + v8i16 temp0, temp1, temp2, temp3; \ + const v16i8 cnst4b = __msa_ldi_b(4); \ + const v16i8 cnst3b = __msa_ldi_b(3); \ + const v8i16 cnst9h = __msa_ldi_h(9); \ + \ + FLIP_SIGN4(p1, p0, q0, q1, p1_m, p0_m, q0_m, q1_m); \ + filt = __msa_subs_s_b(p1_m, q1_m); \ + FILT_VAL(q0_m, p0_m, mask, filt); \ + FLIP_SIGN2(p2, q2, p2_m, q2_m); \ + t2 = filt & hev; \ + /* filt_val &= ~hev */ \ + hev = __msa_xori_b(hev, 0xff); \ + filt = filt & hev; \ + t1 = __msa_adds_s_b(t2, cnst4b); \ + t1 = SRAI_B(t1, 3); \ + t2 = __msa_adds_s_b(t2, cnst3b); \ + t2 = SRAI_B(t2, 3); \ + q0_m = __msa_subs_s_b(q0_m, t1); \ + p0_m = __msa_adds_s_b(p0_m, t2); \ + filt_sign = __msa_clti_s_b(filt, 0); \ + ILVRL_B2_SH(filt_sign, filt, filt_r, filt_l); \ + /* update q2/p2 */ \ + temp0 = filt_r * cnst9h; \ + temp1 = ADDVI_H(temp0, 63); \ + temp2 = filt_l * cnst9h; \ + temp3 = ADDVI_H(temp2, 63); \ + FILT2(q2_m, p2_m, q2, p2); \ + /* update q1/p1 */ \ + temp1 = temp1 + temp0; \ + temp3 = temp3 + temp2; \ + FILT2(q1_m, p1_m, q1, p1); \ + /* update q0/p0 */ \ + temp1 = temp1 + temp0; \ + temp3 = temp3 + temp2; \ + FILT2(q0_m, p0_m, q0, p0); \ +} while (0) + +#define LPF_MASK_HEV(p3_in, p2_in, p1_in, p0_in, \ + q0_in, q1_in, q2_in, q3_in, \ + limit_in, b_limit_in, thresh_in, \ + hev_out, mask_out) do { \ + v16u8 p3_asub_p2_m, p2_asub_p1_m, p1_asub_p0_m, q1_asub_q0_m; \ + v16u8 p1_asub_q1_m, p0_asub_q0_m, q3_asub_q2_m, q2_asub_q1_m; \ + v16u8 flat_out; \ + \ + /* absolute subtraction of pixel values */ \ + p3_asub_p2_m = __msa_asub_u_b(p3_in, p2_in); \ + p2_asub_p1_m = __msa_asub_u_b(p2_in, p1_in); \ + p1_asub_p0_m = __msa_asub_u_b(p1_in, p0_in); \ + q1_asub_q0_m = __msa_asub_u_b(q1_in, q0_in); \ + q2_asub_q1_m = __msa_asub_u_b(q2_in, q1_in); \ + q3_asub_q2_m = __msa_asub_u_b(q3_in, q2_in); \ + p0_asub_q0_m = __msa_asub_u_b(p0_in, q0_in); \ + p1_asub_q1_m = __msa_asub_u_b(p1_in, q1_in); \ + /* calculation of hev */ \ + flat_out = __msa_max_u_b(p1_asub_p0_m, q1_asub_q0_m); \ + hev_out = (thresh_in < flat_out); \ + /* calculation of mask */ \ + p0_asub_q0_m = __msa_adds_u_b(p0_asub_q0_m, p0_asub_q0_m); \ + p1_asub_q1_m = SRAI_B(p1_asub_q1_m, 1); \ + p0_asub_q0_m = __msa_adds_u_b(p0_asub_q0_m, p1_asub_q1_m); \ + mask_out = (b_limit_in < p0_asub_q0_m); \ + mask_out = __msa_max_u_b(flat_out, mask_out); \ + p3_asub_p2_m = __msa_max_u_b(p3_asub_p2_m, p2_asub_p1_m); \ + mask_out = __msa_max_u_b(p3_asub_p2_m, mask_out); \ + q2_asub_q1_m = __msa_max_u_b(q2_asub_q1_m, q3_asub_q2_m); \ + mask_out = __msa_max_u_b(q2_asub_q1_m, mask_out); \ + mask_out = (limit_in < mask_out); \ + mask_out = __msa_xori_b(mask_out, 0xff); \ +} while (0) + +#define ST6x1_UB(in0, in0_idx, in1, in1_idx, pdst, stride) do { \ + const uint16_t tmp0_h = __msa_copy_s_h((v8i16)in1, in1_idx); \ + const uint32_t tmp0_w = __msa_copy_s_w((v4i32)in0, in0_idx); \ + SW(tmp0_w, pdst); \ + SH(tmp0_h, pdst + stride); \ +} while (0) + +#define ST6x4_UB(in0, start_in0_idx, in1, start_in1_idx, pdst, stride) do { \ + uint8_t* ptmp1 = (uint8_t*)pdst; \ + ST6x1_UB(in0, start_in0_idx, in1, start_in1_idx, ptmp1, 4); \ + ptmp1 += stride; \ + ST6x1_UB(in0, start_in0_idx + 1, in1, start_in1_idx + 1, ptmp1, 4); \ + ptmp1 += stride; \ + ST6x1_UB(in0, start_in0_idx + 2, in1, start_in1_idx + 2, ptmp1, 4); \ + ptmp1 += stride; \ + ST6x1_UB(in0, start_in0_idx + 3, in1, start_in1_idx + 3, ptmp1, 4); \ +} while (0) + +#define LPF_SIMPLE_FILT(p1_in, p0_in, q0_in, q1_in, mask) do { \ + v16i8 p1_m, p0_m, q0_m, q1_m, filt, filt1, filt2; \ + const v16i8 cnst4b = __msa_ldi_b(4); \ + const v16i8 cnst3b = __msa_ldi_b(3); \ + \ + FLIP_SIGN4(p1_in, p0_in, q0_in, q1_in, p1_m, p0_m, q0_m, q1_m); \ + filt = __msa_subs_s_b(p1_m, q1_m); \ + FILT_VAL(q0_m, p0_m, mask, filt); \ + filt1 = __msa_adds_s_b(filt, cnst4b); \ + filt1 = SRAI_B(filt1, 3); \ + filt2 = __msa_adds_s_b(filt, cnst3b); \ + filt2 = SRAI_B(filt2, 3); \ + q0_m = __msa_subs_s_b(q0_m, filt1); \ + p0_m = __msa_adds_s_b(p0_m, filt2); \ + q0_in = __msa_xori_b((v16u8)q0_m, 0x80); \ + p0_in = __msa_xori_b((v16u8)p0_m, 0x80); \ +} while (0) + +#define LPF_SIMPLE_MASK(p1, p0, q0, q1, b_limit, mask) do { \ + v16u8 p1_a_sub_q1, p0_a_sub_q0; \ + \ + p0_a_sub_q0 = __msa_asub_u_b(p0, q0); \ + p1_a_sub_q1 = __msa_asub_u_b(p1, q1); \ + p1_a_sub_q1 = (v16u8)__msa_srli_b((v16i8)p1_a_sub_q1, 1); \ + p0_a_sub_q0 = __msa_adds_u_b(p0_a_sub_q0, p0_a_sub_q0); \ + mask = __msa_adds_u_b(p0_a_sub_q0, p1_a_sub_q1); \ + mask = (mask <= b_limit); \ +} while (0) + +static void VFilter16(uint8_t* src, int stride, + int b_limit_in, int limit_in, int thresh_in) { + uint8_t* ptemp = src - 4 * stride; + v16u8 p3, p2, p1, p0, q3, q2, q1, q0; + v16u8 mask, hev; + const v16u8 thresh = (v16u8)__msa_fill_b(thresh_in); + const v16u8 limit = (v16u8)__msa_fill_b(limit_in); + const v16u8 b_limit = (v16u8)__msa_fill_b(b_limit_in); + + LD_UB8(ptemp, stride, p3, p2, p1, p0, q0, q1, q2, q3); + LPF_MASK_HEV(p3, p2, p1, p0, q0, q1, q2, q3, limit, b_limit, thresh, + hev, mask); + LPF_MBFILTER(p2, p1, p0, q0, q1, q2, mask, hev); + ptemp = src - 3 * stride; + ST_UB4(p2, p1, p0, q0, ptemp, stride); + ptemp += (4 * stride); + ST_UB2(q1, q2, ptemp, stride); +} + +static void HFilter16(uint8_t* src, int stride, + int b_limit_in, int limit_in, int thresh_in) { + uint8_t* ptmp = src - 4; + v16u8 p3, p2, p1, p0, q3, q2, q1, q0; + v16u8 mask, hev; + v16u8 row0, row1, row2, row3, row4, row5, row6, row7, row8; + v16u8 row9, row10, row11, row12, row13, row14, row15; + v8i16 tmp0, tmp1, tmp2, tmp3, tmp4, tmp5, tmp6, tmp7; + const v16u8 b_limit = (v16u8)__msa_fill_b(b_limit_in); + const v16u8 limit = (v16u8)__msa_fill_b(limit_in); + const v16u8 thresh = (v16u8)__msa_fill_b(thresh_in); + + LD_UB8(ptmp, stride, row0, row1, row2, row3, row4, row5, row6, row7); + ptmp += (8 * stride); + LD_UB8(ptmp, stride, row8, row9, row10, row11, row12, row13, row14, row15); + TRANSPOSE16x8_UB_UB(row0, row1, row2, row3, row4, row5, row6, row7, + row8, row9, row10, row11, row12, row13, row14, row15, + p3, p2, p1, p0, q0, q1, q2, q3); + LPF_MASK_HEV(p3, p2, p1, p0, q0, q1, q2, q3, limit, b_limit, thresh, + hev, mask); + LPF_MBFILTER(p2, p1, p0, q0, q1, q2, mask, hev); + ILVR_B2_SH(p1, p2, q0, p0, tmp0, tmp1); + ILVRL_H2_SH(tmp1, tmp0, tmp3, tmp4); + ILVL_B2_SH(p1, p2, q0, p0, tmp0, tmp1); + ILVRL_H2_SH(tmp1, tmp0, tmp6, tmp7); + ILVRL_B2_SH(q2, q1, tmp2, tmp5); + ptmp = src - 3; + ST6x1_UB(tmp3, 0, tmp2, 0, ptmp, 4); + ptmp += stride; + ST6x1_UB(tmp3, 1, tmp2, 1, ptmp, 4); + ptmp += stride; + ST6x1_UB(tmp3, 2, tmp2, 2, ptmp, 4); + ptmp += stride; + ST6x1_UB(tmp3, 3, tmp2, 3, ptmp, 4); + ptmp += stride; + ST6x1_UB(tmp4, 0, tmp2, 4, ptmp, 4); + ptmp += stride; + ST6x1_UB(tmp4, 1, tmp2, 5, ptmp, 4); + ptmp += stride; + ST6x1_UB(tmp4, 2, tmp2, 6, ptmp, 4); + ptmp += stride; + ST6x1_UB(tmp4, 3, tmp2, 7, ptmp, 4); + ptmp += stride; + ST6x1_UB(tmp6, 0, tmp5, 0, ptmp, 4); + ptmp += stride; + ST6x1_UB(tmp6, 1, tmp5, 1, ptmp, 4); + ptmp += stride; + ST6x1_UB(tmp6, 2, tmp5, 2, ptmp, 4); + ptmp += stride; + ST6x1_UB(tmp6, 3, tmp5, 3, ptmp, 4); + ptmp += stride; + ST6x1_UB(tmp7, 0, tmp5, 4, ptmp, 4); + ptmp += stride; + ST6x1_UB(tmp7, 1, tmp5, 5, ptmp, 4); + ptmp += stride; + ST6x1_UB(tmp7, 2, tmp5, 6, ptmp, 4); + ptmp += stride; + ST6x1_UB(tmp7, 3, tmp5, 7, ptmp, 4); +} + +// on three inner edges +static void VFilterHorEdge16i(uint8_t* src, int stride, + int b_limit, int limit, int thresh) { + v16u8 mask, hev; + v16u8 p3, p2, p1, p0, q3, q2, q1, q0; + const v16u8 thresh0 = (v16u8)__msa_fill_b(thresh); + const v16u8 b_limit0 = (v16u8)__msa_fill_b(b_limit); + const v16u8 limit0 = (v16u8)__msa_fill_b(limit); + + LD_UB8((src - 4 * stride), stride, p3, p2, p1, p0, q0, q1, q2, q3); + LPF_MASK_HEV(p3, p2, p1, p0, q0, q1, q2, q3, limit0, b_limit0, thresh0, + hev, mask); + LPF_FILTER4_4W(p1, p0, q0, q1, mask, hev); + ST_UB4(p1, p0, q0, q1, (src - 2 * stride), stride); +} + +static void VFilter16i(uint8_t* src_y, int stride, + int b_limit, int limit, int thresh) { + VFilterHorEdge16i(src_y + 4 * stride, stride, b_limit, limit, thresh); + VFilterHorEdge16i(src_y + 8 * stride, stride, b_limit, limit, thresh); + VFilterHorEdge16i(src_y + 12 * stride, stride, b_limit, limit, thresh); +} + +static void HFilterVertEdge16i(uint8_t* src, int stride, + int b_limit, int limit, int thresh) { + v16u8 mask, hev; + v16u8 p3, p2, p1, p0, q3, q2, q1, q0; + v16u8 row0, row1, row2, row3, row4, row5, row6, row7; + v16u8 row8, row9, row10, row11, row12, row13, row14, row15; + v8i16 tmp0, tmp1, tmp2, tmp3, tmp4, tmp5; + const v16u8 thresh0 = (v16u8)__msa_fill_b(thresh); + const v16u8 b_limit0 = (v16u8)__msa_fill_b(b_limit); + const v16u8 limit0 = (v16u8)__msa_fill_b(limit); + + LD_UB8(src - 4, stride, row0, row1, row2, row3, row4, row5, row6, row7); + LD_UB8(src - 4 + (8 * stride), stride, + row8, row9, row10, row11, row12, row13, row14, row15); + TRANSPOSE16x8_UB_UB(row0, row1, row2, row3, row4, row5, row6, row7, + row8, row9, row10, row11, row12, row13, row14, row15, + p3, p2, p1, p0, q0, q1, q2, q3); + LPF_MASK_HEV(p3, p2, p1, p0, q0, q1, q2, q3, limit0, b_limit0, thresh0, + hev, mask); + LPF_FILTER4_4W(p1, p0, q0, q1, mask, hev); + ILVR_B2_SH(p0, p1, q1, q0, tmp0, tmp1); + ILVRL_H2_SH(tmp1, tmp0, tmp2, tmp3); + ILVL_B2_SH(p0, p1, q1, q0, tmp0, tmp1); + ILVRL_H2_SH(tmp1, tmp0, tmp4, tmp5); + src -= 2; + ST4x8_UB(tmp2, tmp3, src, stride); + src += (8 * stride); + ST4x8_UB(tmp4, tmp5, src, stride); +} + +static void HFilter16i(uint8_t* src_y, int stride, + int b_limit, int limit, int thresh) { + HFilterVertEdge16i(src_y + 4, stride, b_limit, limit, thresh); + HFilterVertEdge16i(src_y + 8, stride, b_limit, limit, thresh); + HFilterVertEdge16i(src_y + 12, stride, b_limit, limit, thresh); +} + +// 8-pixels wide variants, for chroma filtering +static void VFilter8(uint8_t* src_u, uint8_t* src_v, int stride, + int b_limit_in, int limit_in, int thresh_in) { + uint8_t* ptmp_src_u = src_u - 4 * stride; + uint8_t* ptmp_src_v = src_v - 4 * stride; + uint64_t p2_d, p1_d, p0_d, q0_d, q1_d, q2_d; + v16u8 p3, p2, p1, p0, q3, q2, q1, q0, mask, hev; + v16u8 p3_u, p2_u, p1_u, p0_u, q3_u, q2_u, q1_u, q0_u; + v16u8 p3_v, p2_v, p1_v, p0_v, q3_v, q2_v, q1_v, q0_v; + const v16u8 b_limit = (v16u8)__msa_fill_b(b_limit_in); + const v16u8 limit = (v16u8)__msa_fill_b(limit_in); + const v16u8 thresh = (v16u8)__msa_fill_b(thresh_in); + + LD_UB8(ptmp_src_u, stride, p3_u, p2_u, p1_u, p0_u, q0_u, q1_u, q2_u, q3_u); + LD_UB8(ptmp_src_v, stride, p3_v, p2_v, p1_v, p0_v, q0_v, q1_v, q2_v, q3_v); + ILVR_D4_UB(p3_v, p3_u, p2_v, p2_u, p1_v, p1_u, p0_v, p0_u, p3, p2, p1, p0); + ILVR_D4_UB(q0_v, q0_u, q1_v, q1_u, q2_v, q2_u, q3_v, q3_u, q0, q1, q2, q3); + LPF_MASK_HEV(p3, p2, p1, p0, q0, q1, q2, q3, limit, b_limit, thresh, + hev, mask); + LPF_MBFILTER(p2, p1, p0, q0, q1, q2, mask, hev); + p2_d = __msa_copy_s_d((v2i64)p2, 0); + p1_d = __msa_copy_s_d((v2i64)p1, 0); + p0_d = __msa_copy_s_d((v2i64)p0, 0); + q0_d = __msa_copy_s_d((v2i64)q0, 0); + q1_d = __msa_copy_s_d((v2i64)q1, 0); + q2_d = __msa_copy_s_d((v2i64)q2, 0); + ptmp_src_u += stride; + SD4(p2_d, p1_d, p0_d, q0_d, ptmp_src_u, stride); + ptmp_src_u += (4 * stride); + SD(q1_d, ptmp_src_u); + ptmp_src_u += stride; + SD(q2_d, ptmp_src_u); + p2_d = __msa_copy_s_d((v2i64)p2, 1); + p1_d = __msa_copy_s_d((v2i64)p1, 1); + p0_d = __msa_copy_s_d((v2i64)p0, 1); + q0_d = __msa_copy_s_d((v2i64)q0, 1); + q1_d = __msa_copy_s_d((v2i64)q1, 1); + q2_d = __msa_copy_s_d((v2i64)q2, 1); + ptmp_src_v += stride; + SD4(p2_d, p1_d, p0_d, q0_d, ptmp_src_v, stride); + ptmp_src_v += (4 * stride); + SD(q1_d, ptmp_src_v); + ptmp_src_v += stride; + SD(q2_d, ptmp_src_v); +} + +static void HFilter8(uint8_t* src_u, uint8_t* src_v, int stride, + int b_limit_in, int limit_in, int thresh_in) { + uint8_t* ptmp_src_u = src_u - 4; + uint8_t* ptmp_src_v = src_v - 4; + v16u8 p3, p2, p1, p0, q3, q2, q1, q0, mask, hev; + v16u8 row0, row1, row2, row3, row4, row5, row6, row7, row8; + v16u8 row9, row10, row11, row12, row13, row14, row15; + v8i16 tmp0, tmp1, tmp2, tmp3, tmp4, tmp5, tmp6, tmp7; + const v16u8 b_limit = (v16u8)__msa_fill_b(b_limit_in); + const v16u8 limit = (v16u8)__msa_fill_b(limit_in); + const v16u8 thresh = (v16u8)__msa_fill_b(thresh_in); + + LD_UB8(ptmp_src_u, stride, row0, row1, row2, row3, row4, row5, row6, row7); + LD_UB8(ptmp_src_v, stride, + row8, row9, row10, row11, row12, row13, row14, row15); + TRANSPOSE16x8_UB_UB(row0, row1, row2, row3, row4, row5, row6, row7, + row8, row9, row10, row11, row12, row13, row14, row15, + p3, p2, p1, p0, q0, q1, q2, q3); + LPF_MASK_HEV(p3, p2, p1, p0, q0, q1, q2, q3, limit, b_limit, thresh, + hev, mask); + LPF_MBFILTER(p2, p1, p0, q0, q1, q2, mask, hev); + ILVR_B2_SH(p1, p2, q0, p0, tmp0, tmp1); + ILVRL_H2_SH(tmp1, tmp0, tmp3, tmp4); + ILVL_B2_SH(p1, p2, q0, p0, tmp0, tmp1); + ILVRL_H2_SH(tmp1, tmp0, tmp6, tmp7); + ILVRL_B2_SH(q2, q1, tmp2, tmp5); + ptmp_src_u += 1; + ST6x4_UB(tmp3, 0, tmp2, 0, ptmp_src_u, stride); + ptmp_src_u += 4 * stride; + ST6x4_UB(tmp4, 0, tmp2, 4, ptmp_src_u, stride); + ptmp_src_v += 1; + ST6x4_UB(tmp6, 0, tmp5, 0, ptmp_src_v, stride); + ptmp_src_v += 4 * stride; + ST6x4_UB(tmp7, 0, tmp5, 4, ptmp_src_v, stride); +} + +static void VFilter8i(uint8_t* src_u, uint8_t* src_v, int stride, + int b_limit_in, int limit_in, int thresh_in) { + uint64_t p1_d, p0_d, q0_d, q1_d; + v16u8 p3, p2, p1, p0, q3, q2, q1, q0, mask, hev; + v16u8 p3_u, p2_u, p1_u, p0_u, q3_u, q2_u, q1_u, q0_u; + v16u8 p3_v, p2_v, p1_v, p0_v, q3_v, q2_v, q1_v, q0_v; + const v16u8 thresh = (v16u8)__msa_fill_b(thresh_in); + const v16u8 limit = (v16u8)__msa_fill_b(limit_in); + const v16u8 b_limit = (v16u8)__msa_fill_b(b_limit_in); + + LD_UB8(src_u, stride, p3_u, p2_u, p1_u, p0_u, q0_u, q1_u, q2_u, q3_u); + src_u += (5 * stride); + LD_UB8(src_v, stride, p3_v, p2_v, p1_v, p0_v, q0_v, q1_v, q2_v, q3_v); + src_v += (5 * stride); + ILVR_D4_UB(p3_v, p3_u, p2_v, p2_u, p1_v, p1_u, p0_v, p0_u, p3, p2, p1, p0); + ILVR_D4_UB(q0_v, q0_u, q1_v, q1_u, q2_v, q2_u, q3_v, q3_u, q0, q1, q2, q3); + LPF_MASK_HEV(p3, p2, p1, p0, q0, q1, q2, q3, limit, b_limit, thresh, + hev, mask); + LPF_FILTER4_4W(p1, p0, q0, q1, mask, hev); + p1_d = __msa_copy_s_d((v2i64)p1, 0); + p0_d = __msa_copy_s_d((v2i64)p0, 0); + q0_d = __msa_copy_s_d((v2i64)q0, 0); + q1_d = __msa_copy_s_d((v2i64)q1, 0); + SD4(q1_d, q0_d, p0_d, p1_d, src_u, -stride); + p1_d = __msa_copy_s_d((v2i64)p1, 1); + p0_d = __msa_copy_s_d((v2i64)p0, 1); + q0_d = __msa_copy_s_d((v2i64)q0, 1); + q1_d = __msa_copy_s_d((v2i64)q1, 1); + SD4(q1_d, q0_d, p0_d, p1_d, src_v, -stride); +} + +static void HFilter8i(uint8_t* src_u, uint8_t* src_v, int stride, + int b_limit_in, int limit_in, int thresh_in) { + v16u8 p3, p2, p1, p0, q3, q2, q1, q0, mask, hev; + v16u8 row0, row1, row2, row3, row4, row5, row6, row7, row8; + v16u8 row9, row10, row11, row12, row13, row14, row15; + v4i32 tmp0, tmp1, tmp2, tmp3, tmp4, tmp5; + const v16u8 thresh = (v16u8)__msa_fill_b(thresh_in); + const v16u8 limit = (v16u8)__msa_fill_b(limit_in); + const v16u8 b_limit = (v16u8)__msa_fill_b(b_limit_in); + + LD_UB8(src_u, stride, row0, row1, row2, row3, row4, row5, row6, row7); + LD_UB8(src_v, stride, + row8, row9, row10, row11, row12, row13, row14, row15); + TRANSPOSE16x8_UB_UB(row0, row1, row2, row3, row4, row5, row6, row7, + row8, row9, row10, row11, row12, row13, row14, row15, + p3, p2, p1, p0, q0, q1, q2, q3); + LPF_MASK_HEV(p3, p2, p1, p0, q0, q1, q2, q3, limit, b_limit, thresh, + hev, mask); + LPF_FILTER4_4W(p1, p0, q0, q1, mask, hev); + ILVR_B2_SW(p0, p1, q1, q0, tmp0, tmp1); + ILVRL_H2_SW(tmp1, tmp0, tmp2, tmp3); + ILVL_B2_SW(p0, p1, q1, q0, tmp0, tmp1); + ILVRL_H2_SW(tmp1, tmp0, tmp4, tmp5); + src_u += 2; + ST4x4_UB(tmp2, tmp2, 0, 1, 2, 3, src_u, stride); + src_u += 4 * stride; + ST4x4_UB(tmp3, tmp3, 0, 1, 2, 3, src_u, stride); + src_v += 2; + ST4x4_UB(tmp4, tmp4, 0, 1, 2, 3, src_v, stride); + src_v += 4 * stride; + ST4x4_UB(tmp5, tmp5, 0, 1, 2, 3, src_v, stride); +} + +static void SimpleVFilter16(uint8_t* src, int stride, int b_limit_in) { + v16u8 p1, p0, q1, q0, mask; + const v16u8 b_limit = (v16u8)__msa_fill_b(b_limit_in); + + LD_UB4(src - 2 * stride, stride, p1, p0, q0, q1); + LPF_SIMPLE_MASK(p1, p0, q0, q1, b_limit, mask); + LPF_SIMPLE_FILT(p1, p0, q0, q1, mask); + ST_UB2(p0, q0, src - stride, stride); +} + +static void SimpleHFilter16(uint8_t* src, int stride, int b_limit_in) { + v16u8 p1, p0, q1, q0, mask, row0, row1, row2, row3, row4, row5, row6, row7; + v16u8 row8, row9, row10, row11, row12, row13, row14, row15; + v8i16 tmp0, tmp1; + const v16u8 b_limit = (v16u8)__msa_fill_b(b_limit_in); + uint8_t* ptemp_src = src - 2; + + LD_UB8(ptemp_src, stride, row0, row1, row2, row3, row4, row5, row6, row7); + LD_UB8(ptemp_src + 8 * stride, stride, + row8, row9, row10, row11, row12, row13, row14, row15); + TRANSPOSE16x4_UB_UB(row0, row1, row2, row3, row4, row5, row6, row7, + row8, row9, row10, row11, row12, row13, row14, row15, + p1, p0, q0, q1); + LPF_SIMPLE_MASK(p1, p0, q0, q1, b_limit, mask); + LPF_SIMPLE_FILT(p1, p0, q0, q1, mask); + ILVRL_B2_SH(q0, p0, tmp1, tmp0); + ptemp_src += 1; + ST2x4_UB(tmp1, 0, ptemp_src, stride); + ptemp_src += 4 * stride; + ST2x4_UB(tmp1, 4, ptemp_src, stride); + ptemp_src += 4 * stride; + ST2x4_UB(tmp0, 0, ptemp_src, stride); + ptemp_src += 4 * stride; + ST2x4_UB(tmp0, 4, ptemp_src, stride); + ptemp_src += 4 * stride; +} + +static void SimpleVFilter16i(uint8_t* src_y, int stride, int b_limit_in) { + SimpleVFilter16(src_y + 4 * stride, stride, b_limit_in); + SimpleVFilter16(src_y + 8 * stride, stride, b_limit_in); + SimpleVFilter16(src_y + 12 * stride, stride, b_limit_in); +} + +static void SimpleHFilter16i(uint8_t* src_y, int stride, int b_limit_in) { + SimpleHFilter16(src_y + 4, stride, b_limit_in); + SimpleHFilter16(src_y + 8, stride, b_limit_in); + SimpleHFilter16(src_y + 12, stride, b_limit_in); +} + +//------------------------------------------------------------------------------ +// Intra predictions +//------------------------------------------------------------------------------ + +// 4x4 + +static void DC4(uint8_t* dst) { // DC + uint32_t dc = 4; + int i; + for (i = 0; i < 4; ++i) dc += dst[i - BPS] + dst[-1 + i * BPS]; + dc >>= 3; + dc = dc | (dc << 8) | (dc << 16) | (dc << 24); + SW4(dc, dc, dc, dc, dst, BPS); +} + +static void TM4(uint8_t* dst) { + const uint8_t* const ptemp = dst - BPS - 1; + v8i16 T, d, r0, r1, r2, r3; + const v16i8 zero = { 0 }; + const v8i16 TL = (v8i16)__msa_fill_h(ptemp[0 * BPS]); + const v8i16 L0 = (v8i16)__msa_fill_h(ptemp[1 * BPS]); + const v8i16 L1 = (v8i16)__msa_fill_h(ptemp[2 * BPS]); + const v8i16 L2 = (v8i16)__msa_fill_h(ptemp[3 * BPS]); + const v8i16 L3 = (v8i16)__msa_fill_h(ptemp[4 * BPS]); + const v16u8 T1 = LD_UB(ptemp + 1); + + T = (v8i16)__msa_ilvr_b(zero, (v16i8)T1); + d = T - TL; + ADD4(d, L0, d, L1, d, L2, d, L3, r0, r1, r2, r3); + CLIP_SH4_0_255(r0, r1, r2, r3); + PCKEV_ST4x4_UB(r0, r1, r2, r3, dst, BPS); +} + +static void VE4(uint8_t* dst) { // vertical + const uint8_t* const ptop = dst - BPS - 1; + const uint32_t val0 = LW(ptop + 0); + const uint32_t val1 = LW(ptop + 4); + uint32_t out; + v16u8 A, B, C, AC, B2, R; + + INSERT_W2_UB(val0, val1, A); + B = SLDI_UB(A, A, 1); + C = SLDI_UB(A, A, 2); + AC = __msa_ave_u_b(A, C); + B2 = __msa_ave_u_b(B, B); + R = __msa_aver_u_b(AC, B2); + out = __msa_copy_s_w((v4i32)R, 0); + SW4(out, out, out, out, dst, BPS); +} + +static void RD4(uint8_t* dst) { // Down-right + const uint8_t* const ptop = dst - 1 - BPS; + uint32_t val0 = LW(ptop + 0); + uint32_t val1 = LW(ptop + 4); + uint32_t val2, val3; + v16u8 A, B, C, AC, B2, R, A1; + + INSERT_W2_UB(val0, val1, A1); + A = SLDI_UB(A1, A1, 12); + A = (v16u8)__msa_insert_b((v16i8)A, 3, ptop[1 * BPS]); + A = (v16u8)__msa_insert_b((v16i8)A, 2, ptop[2 * BPS]); + A = (v16u8)__msa_insert_b((v16i8)A, 1, ptop[3 * BPS]); + A = (v16u8)__msa_insert_b((v16i8)A, 0, ptop[4 * BPS]); + B = SLDI_UB(A, A, 1); + C = SLDI_UB(A, A, 2); + AC = __msa_ave_u_b(A, C); + B2 = __msa_ave_u_b(B, B); + R = __msa_aver_u_b(AC, B2); + val3 = __msa_copy_s_w((v4i32)R, 0); + R = SLDI_UB(R, R, 1); + val2 = __msa_copy_s_w((v4i32)R, 0); + R = SLDI_UB(R, R, 1); + val1 = __msa_copy_s_w((v4i32)R, 0); + R = SLDI_UB(R, R, 1); + val0 = __msa_copy_s_w((v4i32)R, 0); + SW4(val0, val1, val2, val3, dst, BPS); +} + +static void LD4(uint8_t* dst) { // Down-Left + const uint8_t* const ptop = dst - BPS; + uint32_t val0 = LW(ptop + 0); + uint32_t val1 = LW(ptop + 4); + uint32_t val2, val3; + v16u8 A, B, C, AC, B2, R; + + INSERT_W2_UB(val0, val1, A); + B = SLDI_UB(A, A, 1); + C = SLDI_UB(A, A, 2); + C = (v16u8)__msa_insert_b((v16i8)C, 6, ptop[7]); + AC = __msa_ave_u_b(A, C); + B2 = __msa_ave_u_b(B, B); + R = __msa_aver_u_b(AC, B2); + val0 = __msa_copy_s_w((v4i32)R, 0); + R = SLDI_UB(R, R, 1); + val1 = __msa_copy_s_w((v4i32)R, 0); + R = SLDI_UB(R, R, 1); + val2 = __msa_copy_s_w((v4i32)R, 0); + R = SLDI_UB(R, R, 1); + val3 = __msa_copy_s_w((v4i32)R, 0); + SW4(val0, val1, val2, val3, dst, BPS); +} + +// 16x16 + +static void DC16(uint8_t* dst) { // DC + uint32_t dc = 16; + int i; + const v16u8 rtop = LD_UB(dst - BPS); + const v8u16 dctop = __msa_hadd_u_h(rtop, rtop); + v16u8 out; + + for (i = 0; i < 16; ++i) { + dc += dst[-1 + i * BPS]; + } + dc += HADD_UH_U32(dctop); + out = (v16u8)__msa_fill_b(dc >> 5); + ST_UB8(out, out, out, out, out, out, out, out, dst, BPS); + ST_UB8(out, out, out, out, out, out, out, out, dst + 8 * BPS, BPS); +} + +static void TM16(uint8_t* dst) { + int j; + v8i16 d1, d2; + const v16i8 zero = { 0 }; + const v8i16 TL = (v8i16)__msa_fill_h(dst[-1 - BPS]); + const v16i8 T = LD_SB(dst - BPS); + + ILVRL_B2_SH(zero, T, d1, d2); + SUB2(d1, TL, d2, TL, d1, d2); + for (j = 0; j < 16; j += 4) { + v16i8 t0, t1, t2, t3; + v8i16 r0, r1, r2, r3, r4, r5, r6, r7; + const v8i16 L0 = (v8i16)__msa_fill_h(dst[-1 + 0 * BPS]); + const v8i16 L1 = (v8i16)__msa_fill_h(dst[-1 + 1 * BPS]); + const v8i16 L2 = (v8i16)__msa_fill_h(dst[-1 + 2 * BPS]); + const v8i16 L3 = (v8i16)__msa_fill_h(dst[-1 + 3 * BPS]); + ADD4(d1, L0, d1, L1, d1, L2, d1, L3, r0, r1, r2, r3); + ADD4(d2, L0, d2, L1, d2, L2, d2, L3, r4, r5, r6, r7); + CLIP_SH4_0_255(r0, r1, r2, r3); + CLIP_SH4_0_255(r4, r5, r6, r7); + PCKEV_B4_SB(r4, r0, r5, r1, r6, r2, r7, r3, t0, t1, t2, t3); + ST_SB4(t0, t1, t2, t3, dst, BPS); + dst += 4 * BPS; + } +} + +static void VE16(uint8_t* dst) { // vertical + const v16u8 rtop = LD_UB(dst - BPS); + ST_UB8(rtop, rtop, rtop, rtop, rtop, rtop, rtop, rtop, dst, BPS); + ST_UB8(rtop, rtop, rtop, rtop, rtop, rtop, rtop, rtop, dst + 8 * BPS, BPS); +} + +static void HE16(uint8_t* dst) { // horizontal + int j; + for (j = 16; j > 0; j -= 4) { + const v16u8 L0 = (v16u8)__msa_fill_b(dst[-1 + 0 * BPS]); + const v16u8 L1 = (v16u8)__msa_fill_b(dst[-1 + 1 * BPS]); + const v16u8 L2 = (v16u8)__msa_fill_b(dst[-1 + 2 * BPS]); + const v16u8 L3 = (v16u8)__msa_fill_b(dst[-1 + 3 * BPS]); + ST_UB4(L0, L1, L2, L3, dst, BPS); + dst += 4 * BPS; + } +} + +static void DC16NoTop(uint8_t* dst) { // DC with top samples not available + int j; + uint32_t dc = 8; + v16u8 out; + + for (j = 0; j < 16; ++j) { + dc += dst[-1 + j * BPS]; + } + out = (v16u8)__msa_fill_b(dc >> 4); + ST_UB8(out, out, out, out, out, out, out, out, dst, BPS); + ST_UB8(out, out, out, out, out, out, out, out, dst + 8 * BPS, BPS); +} + +static void DC16NoLeft(uint8_t* dst) { // DC with left samples not available + uint32_t dc = 8; + const v16u8 rtop = LD_UB(dst - BPS); + const v8u16 dctop = __msa_hadd_u_h(rtop, rtop); + v16u8 out; + + dc += HADD_UH_U32(dctop); + out = (v16u8)__msa_fill_b(dc >> 4); + ST_UB8(out, out, out, out, out, out, out, out, dst, BPS); + ST_UB8(out, out, out, out, out, out, out, out, dst + 8 * BPS, BPS); +} + +static void DC16NoTopLeft(uint8_t* dst) { // DC with nothing + const v16u8 out = (v16u8)__msa_fill_b(0x80); + ST_UB8(out, out, out, out, out, out, out, out, dst, BPS); + ST_UB8(out, out, out, out, out, out, out, out, dst + 8 * BPS, BPS); +} + +// Chroma + +#define STORE8x8(out, dst) do { \ + SD4(out, out, out, out, dst + 0 * BPS, BPS); \ + SD4(out, out, out, out, dst + 4 * BPS, BPS); \ +} while (0) + +static void DC8uv(uint8_t* dst) { // DC + uint32_t dc = 8; + int i; + uint64_t out; + const v16u8 rtop = LD_UB(dst - BPS); + const v8u16 temp0 = __msa_hadd_u_h(rtop, rtop); + const v4u32 temp1 = __msa_hadd_u_w(temp0, temp0); + const v2u64 temp2 = __msa_hadd_u_d(temp1, temp1); + v16u8 dctemp; + + for (i = 0; i < 8; ++i) { + dc += dst[-1 + i * BPS]; + } + dc += __msa_copy_s_w((v4i32)temp2, 0); + dctemp = (v16u8)__msa_fill_b(dc >> 4); + out = __msa_copy_s_d((v2i64)dctemp, 0); + STORE8x8(out, dst); +} + +static void TM8uv(uint8_t* dst) { + int j; + const v16i8 T1 = LD_SB(dst - BPS); + const v16i8 zero = { 0 }; + const v8i16 T = (v8i16)__msa_ilvr_b(zero, T1); + const v8i16 TL = (v8i16)__msa_fill_h(dst[-1 - BPS]); + const v8i16 d = T - TL; + + for (j = 0; j < 8; j += 4) { + v16i8 t0, t1; + v8i16 r0 = (v8i16)__msa_fill_h(dst[-1 + 0 * BPS]); + v8i16 r1 = (v8i16)__msa_fill_h(dst[-1 + 1 * BPS]); + v8i16 r2 = (v8i16)__msa_fill_h(dst[-1 + 2 * BPS]); + v8i16 r3 = (v8i16)__msa_fill_h(dst[-1 + 3 * BPS]); + ADD4(d, r0, d, r1, d, r2, d, r3, r0, r1, r2, r3); + CLIP_SH4_0_255(r0, r1, r2, r3); + PCKEV_B2_SB(r1, r0, r3, r2, t0, t1); + ST4x4_UB(t0, t1, 0, 2, 0, 2, dst, BPS); + ST4x4_UB(t0, t1, 1, 3, 1, 3, dst + 4, BPS); + dst += 4 * BPS; + } +} + +static void VE8uv(uint8_t* dst) { // vertical + const v16u8 rtop = LD_UB(dst - BPS); + const uint64_t out = __msa_copy_s_d((v2i64)rtop, 0); + STORE8x8(out, dst); +} + +static void HE8uv(uint8_t* dst) { // horizontal + int j; + for (j = 0; j < 8; j += 4) { + const v16u8 L0 = (v16u8)__msa_fill_b(dst[-1 + 0 * BPS]); + const v16u8 L1 = (v16u8)__msa_fill_b(dst[-1 + 1 * BPS]); + const v16u8 L2 = (v16u8)__msa_fill_b(dst[-1 + 2 * BPS]); + const v16u8 L3 = (v16u8)__msa_fill_b(dst[-1 + 3 * BPS]); + const uint64_t out0 = __msa_copy_s_d((v2i64)L0, 0); + const uint64_t out1 = __msa_copy_s_d((v2i64)L1, 0); + const uint64_t out2 = __msa_copy_s_d((v2i64)L2, 0); + const uint64_t out3 = __msa_copy_s_d((v2i64)L3, 0); + SD4(out0, out1, out2, out3, dst, BPS); + dst += 4 * BPS; + } +} + +static void DC8uvNoLeft(uint8_t* dst) { // DC with no left samples + const uint32_t dc = 4; + const v16u8 rtop = LD_UB(dst - BPS); + const v8u16 temp0 = __msa_hadd_u_h(rtop, rtop); + const v4u32 temp1 = __msa_hadd_u_w(temp0, temp0); + const v2u64 temp2 = __msa_hadd_u_d(temp1, temp1); + const uint32_t sum_m = __msa_copy_s_w((v4i32)temp2, 0); + const v16u8 dcval = (v16u8)__msa_fill_b((dc + sum_m) >> 3); + const uint64_t out = __msa_copy_s_d((v2i64)dcval, 0); + STORE8x8(out, dst); +} + +static void DC8uvNoTop(uint8_t* dst) { // DC with no top samples + uint32_t dc = 4; + int i; + uint64_t out; + v16u8 dctemp; + + for (i = 0; i < 8; ++i) { + dc += dst[-1 + i * BPS]; + } + dctemp = (v16u8)__msa_fill_b(dc >> 3); + out = __msa_copy_s_d((v2i64)dctemp, 0); + STORE8x8(out, dst); +} + +static void DC8uvNoTopLeft(uint8_t* dst) { // DC with nothing + const uint64_t out = 0x8080808080808080ULL; + STORE8x8(out, dst); +} + //------------------------------------------------------------------------------ // Entry point @@ -163,6 +977,39 @@ WEBP_TSAN_IGNORE_FUNCTION void VP8DspInitMSA(void) { VP8Transform = TransformTwo; VP8TransformDC = TransformDC; VP8TransformAC3 = TransformAC3; + + VP8VFilter16 = VFilter16; + VP8HFilter16 = HFilter16; + VP8VFilter16i = VFilter16i; + VP8HFilter16i = HFilter16i; + VP8VFilter8 = VFilter8; + VP8HFilter8 = HFilter8; + VP8VFilter8i = VFilter8i; + VP8HFilter8i = HFilter8i; + VP8SimpleVFilter16 = SimpleVFilter16; + VP8SimpleHFilter16 = SimpleHFilter16; + VP8SimpleVFilter16i = SimpleVFilter16i; + VP8SimpleHFilter16i = SimpleHFilter16i; + + VP8PredLuma4[0] = DC4; + VP8PredLuma4[1] = TM4; + VP8PredLuma4[2] = VE4; + VP8PredLuma4[4] = RD4; + VP8PredLuma4[6] = LD4; + VP8PredLuma16[0] = DC16; + VP8PredLuma16[1] = TM16; + VP8PredLuma16[2] = VE16; + VP8PredLuma16[3] = HE16; + VP8PredLuma16[4] = DC16NoTop; + VP8PredLuma16[5] = DC16NoLeft; + VP8PredLuma16[6] = DC16NoTopLeft; + VP8PredChroma8[0] = DC8uv; + VP8PredChroma8[1] = TM8uv; + VP8PredChroma8[2] = VE8uv; + VP8PredChroma8[3] = HE8uv; + VP8PredChroma8[4] = DC8uvNoTop; + VP8PredChroma8[5] = DC8uvNoLeft; + VP8PredChroma8[6] = DC8uvNoTopLeft; } #else // !WEBP_USE_MSA diff --git a/thirdparty/libwebp/dsp/dec_neon.c b/thirdparty/libwebp/dsp/dec_neon.c index a63f43fe17..34796cf4a2 100644 --- a/thirdparty/libwebp/dsp/dec_neon.c +++ b/thirdparty/libwebp/dsp/dec_neon.c @@ -17,7 +17,7 @@ #if defined(WEBP_USE_NEON) #include "./neon.h" -#include "../dec/vp8i.h" +#include "../dec/vp8i_dec.h" //------------------------------------------------------------------------------ // NxM Loading functions @@ -666,9 +666,8 @@ static uint8x16_t NeedsHev(const uint8x16_t p1, const uint8x16_t p0, const uint8x16_t hev_thresh_v = vdupq_n_u8((uint8_t)hev_thresh); const uint8x16_t a_p1_p0 = vabdq_u8(p1, p0); // abs(p1 - p0) const uint8x16_t a_q1_q0 = vabdq_u8(q1, q0); // abs(q1 - q0) - const uint8x16_t mask1 = vcgtq_u8(a_p1_p0, hev_thresh_v); - const uint8x16_t mask2 = vcgtq_u8(a_q1_q0, hev_thresh_v); - const uint8x16_t mask = vorrq_u8(mask1, mask2); + const uint8x16_t a_max = vmaxq_u8(a_p1_p0, a_q1_q0); + const uint8x16_t mask = vcgtq_u8(a_max, hev_thresh_v); return mask; } @@ -756,24 +755,25 @@ static void ApplyFilter6( const int8x16_t delta, uint8x16_t* const op2, uint8x16_t* const op1, uint8x16_t* const op0, uint8x16_t* const oq0, uint8x16_t* const oq1, uint8x16_t* const oq2) { - const int16x8_t kCst63 = vdupq_n_s16(63); - const int8x8_t kCst27 = vdup_n_s8(27); - const int8x8_t kCst18 = vdup_n_s8(18); - const int8x8_t kCst9 = vdup_n_s8(9); + // We have to compute: X = (9*a+63) >> 7, Y = (18*a+63)>>7, Z = (27*a+63) >> 7 + // Turns out, there's a common sub-expression S=9 * a - 1 that can be used + // with the special vqrshrn_n_s16 rounding-shift-and-narrow instruction: + // X = (S + 64) >> 7, Y = (S + 32) >> 6, Z = (18 * a + S + 64) >> 7 const int8x8_t delta_lo = vget_low_s8(delta); const int8x8_t delta_hi = vget_high_s8(delta); - const int16x8_t s1_lo = vmlal_s8(kCst63, kCst27, delta_lo); // 63 + 27 * a - const int16x8_t s1_hi = vmlal_s8(kCst63, kCst27, delta_hi); // 63 + 27 * a - const int16x8_t s2_lo = vmlal_s8(kCst63, kCst18, delta_lo); // 63 + 18 * a - const int16x8_t s2_hi = vmlal_s8(kCst63, kCst18, delta_hi); // 63 + 18 * a - const int16x8_t s3_lo = vmlal_s8(kCst63, kCst9, delta_lo); // 63 + 9 * a - const int16x8_t s3_hi = vmlal_s8(kCst63, kCst9, delta_hi); // 63 + 9 * a - const int8x8_t a1_lo = vqshrn_n_s16(s1_lo, 7); - const int8x8_t a1_hi = vqshrn_n_s16(s1_hi, 7); - const int8x8_t a2_lo = vqshrn_n_s16(s2_lo, 7); - const int8x8_t a2_hi = vqshrn_n_s16(s2_hi, 7); - const int8x8_t a3_lo = vqshrn_n_s16(s3_lo, 7); - const int8x8_t a3_hi = vqshrn_n_s16(s3_hi, 7); + const int8x8_t kCst9 = vdup_n_s8(9); + const int16x8_t kCstm1 = vdupq_n_s16(-1); + const int8x8_t kCst18 = vdup_n_s8(18); + const int16x8_t S_lo = vmlal_s8(kCstm1, kCst9, delta_lo); // S = 9 * a - 1 + const int16x8_t S_hi = vmlal_s8(kCstm1, kCst9, delta_hi); + const int16x8_t Z_lo = vmlal_s8(S_lo, kCst18, delta_lo); // S + 18 * a + const int16x8_t Z_hi = vmlal_s8(S_hi, kCst18, delta_hi); + const int8x8_t a3_lo = vqrshrn_n_s16(S_lo, 7); // (9 * a + 63) >> 7 + const int8x8_t a3_hi = vqrshrn_n_s16(S_hi, 7); + const int8x8_t a2_lo = vqrshrn_n_s16(S_lo, 6); // (9 * a + 31) >> 6 + const int8x8_t a2_hi = vqrshrn_n_s16(S_hi, 6); + const int8x8_t a1_lo = vqrshrn_n_s16(Z_lo, 7); // (27 * a + 63) >> 7 + const int8x8_t a1_hi = vqrshrn_n_s16(Z_hi, 7); const int8x16_t a1 = vcombine_s8(a1_lo, a1_hi); const int8x16_t a2 = vcombine_s8(a2_lo, a2_hi); const int8x16_t a3 = vcombine_s8(a3_lo, a3_hi); diff --git a/thirdparty/libwebp/dsp/dec_sse2.c b/thirdparty/libwebp/dsp/dec_sse2.c index f0a8ddcaf3..411fb02768 100644 --- a/thirdparty/libwebp/dsp/dec_sse2.c +++ b/thirdparty/libwebp/dsp/dec_sse2.c @@ -22,7 +22,7 @@ #include #include "./common_sse2.h" -#include "../dec/vp8i.h" +#include "../dec/vp8i_dec.h" #include "../utils/utils.h" //------------------------------------------------------------------------------ @@ -140,7 +140,7 @@ static void Transform(const int16_t* in, uint8_t* dst, int do_two) { // Transpose the two 4x4. VP8Transpose_2_4x4_16b(&shifted0, &shifted1, &shifted2, &shifted3, &T0, &T1, - &T2, &T3); + &T2, &T3); } // Add inverse transform to 'dst' and store. diff --git a/thirdparty/libwebp/dsp/dec_sse41.c b/thirdparty/libwebp/dsp/dec_sse41.c index 8d6aed13e6..4e81ec4d80 100644 --- a/thirdparty/libwebp/dsp/dec_sse41.c +++ b/thirdparty/libwebp/dsp/dec_sse41.c @@ -16,7 +16,7 @@ #if defined(WEBP_USE_SSE41) #include -#include "../dec/vp8i.h" +#include "../dec/vp8i_dec.h" #include "../utils/utils.h" static void HE16(uint8_t* dst) { // horizontal diff --git a/thirdparty/libwebp/dsp/dsp.h b/thirdparty/libwebp/dsp/dsp.h index 1faac27b2b..813fed4a35 100644 --- a/thirdparty/libwebp/dsp/dsp.h +++ b/thirdparty/libwebp/dsp/dsp.h @@ -111,8 +111,7 @@ extern "C" { #define WEBP_UBSAN_IGNORE_UNDEF #define WEBP_UBSAN_IGNORE_UNSIGNED_OVERFLOW -#if !defined(WEBP_FORCE_ALIGNED) && defined(__clang__) && \ - defined(__has_attribute) +#if defined(__clang__) && defined(__has_attribute) #if __has_attribute(no_sanitize) // This macro prevents the undefined behavior sanitizer from reporting // failures. This is only meant to silence unaligned loads on platforms that @@ -133,6 +132,7 @@ extern "C" { typedef enum { kSSE2, kSSE3, + kSlowSSSE3, // special feature for slow SSSE3 architectures kSSE4_1, kAVX, kAVX2, @@ -185,6 +185,11 @@ typedef int (*VP8WMetric)(const uint8_t* pix, const uint8_t* ref, // 4 by 4 symmetric matrix. extern VP8WMetric VP8TDisto4x4, VP8TDisto16x16; +// Compute the average (DC) of four 4x4 blocks. +// Each sub-4x4 block #i sum is stored in dc[i]. +typedef void (*VP8MeanMetric)(const uint8_t* ref, uint32_t dc[4]); +extern VP8MeanMetric VP8Mean16x4; + typedef void (*VP8BlockCopy)(const uint8_t* src, uint8_t* dst); extern VP8BlockCopy VP8Copy4x4; extern VP8BlockCopy VP8Copy16x8; @@ -246,30 +251,37 @@ extern VP8GetResidualCostFunc VP8GetResidualCost; void VP8EncDspCostInit(void); //------------------------------------------------------------------------------ -// SSIM utils +// SSIM / PSNR utils // struct for accumulating statistical moments typedef struct { - double w; // sum(w_i) : sum of weights - double xm, ym; // sum(w_i * x_i), sum(w_i * y_i) - double xxm, xym, yym; // sum(w_i * x_i * x_i), etc. + uint32_t w; // sum(w_i) : sum of weights + uint32_t xm, ym; // sum(w_i * x_i), sum(w_i * y_i) + uint32_t xxm, xym, yym; // sum(w_i * x_i * x_i), etc. } VP8DistoStats; +// Compute the final SSIM value +// The non-clipped version assumes stats->w = (2 * VP8_SSIM_KERNEL + 1)^2. +double VP8SSIMFromStats(const VP8DistoStats* const stats); +double VP8SSIMFromStatsClipped(const VP8DistoStats* const stats); + #define VP8_SSIM_KERNEL 3 // total size of the kernel: 2 * VP8_SSIM_KERNEL + 1 -typedef void (*VP8SSIMAccumulateClippedFunc)(const uint8_t* src1, int stride1, - const uint8_t* src2, int stride2, - int xo, int yo, // center position - int W, int H, // plane dimension - VP8DistoStats* const stats); +typedef double (*VP8SSIMGetClippedFunc)(const uint8_t* src1, int stride1, + const uint8_t* src2, int stride2, + int xo, int yo, // center position + int W, int H); // plane dimension // This version is called with the guarantee that you can load 8 bytes and // 8 rows at offset src1 and src2 -typedef void (*VP8SSIMAccumulateFunc)(const uint8_t* src1, int stride1, - const uint8_t* src2, int stride2, - VP8DistoStats* const stats); +typedef double (*VP8SSIMGetFunc)(const uint8_t* src1, int stride1, + const uint8_t* src2, int stride2); + +extern VP8SSIMGetFunc VP8SSIMGet; // unclipped / unchecked +extern VP8SSIMGetClippedFunc VP8SSIMGetClipped; // with clipping -extern VP8SSIMAccumulateFunc VP8SSIMAccumulate; // unclipped / unchecked -extern VP8SSIMAccumulateClippedFunc VP8SSIMAccumulateClipped; // with clipping +typedef uint32_t (*VP8AccumulateSSEFunc)(const uint8_t* src1, + const uint8_t* src2, int len); +extern VP8AccumulateSSEFunc VP8AccumulateSSE; // must be called before using any of the above directly void VP8SSIMDspInit(void); @@ -416,6 +428,15 @@ extern void WebPConvertARGBToUV_C(const uint32_t* argb, uint8_t* u, uint8_t* v, extern void WebPConvertRGBA32ToUV_C(const uint16_t* rgb, uint8_t* u, uint8_t* v, int width); +// utilities for accurate RGB->YUV conversion +extern uint64_t (*WebPSharpYUVUpdateY)(const uint16_t* src, const uint16_t* ref, + uint16_t* dst, int len); +extern void (*WebPSharpYUVUpdateRGB)(const int16_t* src, const int16_t* ref, + int16_t* dst, int len); +extern void (*WebPSharpYUVFilterRow)(const int16_t* A, const int16_t* B, + int len, + const uint16_t* best_y, uint16_t* out); + // Must be called before using the above. void WebPInitConvertARGBToYUV(void); @@ -488,6 +509,10 @@ extern int (*WebPExtractAlpha)(const uint8_t* argb, int argb_stride, int width, int height, uint8_t* alpha, int alpha_stride); +// Extract the green values from 32b values in argb[] and pack them into alpha[] +// (this is the opposite of WebPDispatchAlphaToGreen). +extern void (*WebPExtractGreen)(const uint32_t* argb, uint8_t* alpha, int size); + // Pre-Multiply operation transforms x into x * A / 255 (where x=Y,R,G or B). // Un-Multiply operation transforms x into x * 255 / A. diff --git a/thirdparty/libwebp/dsp/enc.c b/thirdparty/libwebp/dsp/enc.c index db0e9e70ae..f31bc6de18 100644 --- a/thirdparty/libwebp/dsp/enc.c +++ b/thirdparty/libwebp/dsp/enc.c @@ -15,7 +15,7 @@ #include // for abs() #include "./dsp.h" -#include "../enc/vp8enci.h" +#include "../enc/vp8i_enc.h" static WEBP_INLINE uint8_t clip_8b(int v) { return (!(v & ~0xff)) ? v : (v < 0) ? 0 : 255; @@ -551,6 +551,20 @@ static int SSE4x4(const uint8_t* a, const uint8_t* b) { return GetSSE(a, b, 4, 4); } +static void Mean16x4(const uint8_t* ref, uint32_t dc[4]) { + int k, x, y; + for (k = 0; k < 4; ++k) { + uint32_t avg = 0; + for (y = 0; y < 4; ++y) { + for (x = 0; x < 4; ++x) { + avg += ref[x + y * BPS]; + } + } + dc[k] = avg; + ref += 4; // go to next 4x4 block. + } +} + //------------------------------------------------------------------------------ // Texture distortion // @@ -656,32 +670,6 @@ static int Quantize2Blocks(int16_t in[32], int16_t out[32], return nz; } -static int QuantizeBlockWHT(int16_t in[16], int16_t out[16], - const VP8Matrix* const mtx) { - int n, last = -1; - for (n = 0; n < 16; ++n) { - const int j = kZigzag[n]; - const int sign = (in[j] < 0); - const uint32_t coeff = sign ? -in[j] : in[j]; - assert(mtx->sharpen_[j] == 0); - if (coeff > mtx->zthresh_[j]) { - const uint32_t Q = mtx->q_[j]; - const uint32_t iQ = mtx->iq_[j]; - const uint32_t B = mtx->bias_[j]; - int level = QUANTDIV(coeff, iQ, B); - if (level > MAX_LEVEL) level = MAX_LEVEL; - if (sign) level = -level; - in[j] = level * (int)Q; - out[n] = level; - if (level) last = n; - } else { - out[n] = 0; - in[j] = 0; - } - } - return (last >= 0); -} - //------------------------------------------------------------------------------ // Block copy @@ -703,11 +691,51 @@ static void Copy16x8(const uint8_t* src, uint8_t* dst) { } //------------------------------------------------------------------------------ +// SSIM / PSNR -static void SSIMAccumulateClipped(const uint8_t* src1, int stride1, - const uint8_t* src2, int stride2, - int xo, int yo, int W, int H, - VP8DistoStats* const stats) { +// hat-shaped filter. Sum of coefficients is equal to 16. +static const uint32_t kWeight[2 * VP8_SSIM_KERNEL + 1] = { + 1, 2, 3, 4, 3, 2, 1 +}; +static const uint32_t kWeightSum = 16 * 16; // sum{kWeight}^2 + +static WEBP_INLINE double SSIMCalculation( + const VP8DistoStats* const stats, uint32_t N /*num samples*/) { + const uint32_t w2 = N * N; + const uint32_t C1 = 20 * w2; + const uint32_t C2 = 60 * w2; + const uint32_t C3 = 8 * 8 * w2; // 'dark' limit ~= 6 + const uint64_t xmxm = (uint64_t)stats->xm * stats->xm; + const uint64_t ymym = (uint64_t)stats->ym * stats->ym; + if (xmxm + ymym >= C3) { + const int64_t xmym = (int64_t)stats->xm * stats->ym; + const int64_t sxy = (int64_t)stats->xym * N - xmym; // can be negative + const uint64_t sxx = (uint64_t)stats->xxm * N - xmxm; + const uint64_t syy = (uint64_t)stats->yym * N - ymym; + // we descale by 8 to prevent overflow during the fnum/fden multiply. + const uint64_t num_S = (2 * (uint64_t)(sxy < 0 ? 0 : sxy) + C2) >> 8; + const uint64_t den_S = (sxx + syy + C2) >> 8; + const uint64_t fnum = (2 * xmym + C1) * num_S; + const uint64_t fden = (xmxm + ymym + C1) * den_S; + const double r = (double)fnum / fden; + assert(r >= 0. && r <= 1.0); + return r; + } + return 1.; // area is too dark to contribute meaningfully +} + +double VP8SSIMFromStats(const VP8DistoStats* const stats) { + return SSIMCalculation(stats, kWeightSum); +} + +double VP8SSIMFromStatsClipped(const VP8DistoStats* const stats) { + return SSIMCalculation(stats, stats->w); +} + +static double SSIMGetClipped_C(const uint8_t* src1, int stride1, + const uint8_t* src2, int stride2, + int xo, int yo, int W, int H) { + VP8DistoStats stats = { 0, 0, 0, 0, 0, 0 }; const int ymin = (yo - VP8_SSIM_KERNEL < 0) ? 0 : yo - VP8_SSIM_KERNEL; const int ymax = (yo + VP8_SSIM_KERNEL > H - 1) ? H - 1 : yo + VP8_SSIM_KERNEL; @@ -719,38 +747,61 @@ static void SSIMAccumulateClipped(const uint8_t* src1, int stride1, src2 += ymin * stride2; for (y = ymin; y <= ymax; ++y, src1 += stride1, src2 += stride2) { for (x = xmin; x <= xmax; ++x) { - const int s1 = src1[x]; - const int s2 = src2[x]; - stats->w += 1; - stats->xm += s1; - stats->ym += s2; - stats->xxm += s1 * s1; - stats->xym += s1 * s2; - stats->yym += s2 * s2; + const uint32_t w = kWeight[VP8_SSIM_KERNEL + x - xo] + * kWeight[VP8_SSIM_KERNEL + y - yo]; + const uint32_t s1 = src1[x]; + const uint32_t s2 = src2[x]; + stats.w += w; + stats.xm += w * s1; + stats.ym += w * s2; + stats.xxm += w * s1 * s1; + stats.xym += w * s1 * s2; + stats.yym += w * s2 * s2; } } + return VP8SSIMFromStatsClipped(&stats); } -static void SSIMAccumulate(const uint8_t* src1, int stride1, - const uint8_t* src2, int stride2, - VP8DistoStats* const stats) { +static double SSIMGet_C(const uint8_t* src1, int stride1, + const uint8_t* src2, int stride2) { + VP8DistoStats stats = { 0, 0, 0, 0, 0, 0 }; int x, y; for (y = 0; y <= 2 * VP8_SSIM_KERNEL; ++y, src1 += stride1, src2 += stride2) { for (x = 0; x <= 2 * VP8_SSIM_KERNEL; ++x) { - const int s1 = src1[x]; - const int s2 = src2[x]; - stats->w += 1; - stats->xm += s1; - stats->ym += s2; - stats->xxm += s1 * s1; - stats->xym += s1 * s2; - stats->yym += s2 * s2; + const uint32_t w = kWeight[x] * kWeight[y]; + const uint32_t s1 = src1[x]; + const uint32_t s2 = src2[x]; + stats.xm += w * s1; + stats.ym += w * s2; + stats.xxm += w * s1 * s1; + stats.xym += w * s1 * s2; + stats.yym += w * s2 * s2; } } + return VP8SSIMFromStats(&stats); +} + +//------------------------------------------------------------------------------ + +static uint32_t AccumulateSSE(const uint8_t* src1, + const uint8_t* src2, int len) { + int i; + uint32_t sse2 = 0; + assert(len <= 65535); // to ensure that accumulation fits within uint32_t + for (i = 0; i < len; ++i) { + const int32_t diff = src1[i] - src2[i]; + sse2 += diff * diff; + } + return sse2; } -VP8SSIMAccumulateFunc VP8SSIMAccumulate; -VP8SSIMAccumulateClippedFunc VP8SSIMAccumulateClipped; +//------------------------------------------------------------------------------ + +VP8SSIMGetFunc VP8SSIMGet; +VP8SSIMGetClippedFunc VP8SSIMGetClipped; +VP8AccumulateSSEFunc VP8AccumulateSSE; + +extern void VP8SSIMDspInitSSE2(void); static volatile VP8CPUInfo ssim_last_cpuinfo_used = (VP8CPUInfo)&ssim_last_cpuinfo_used; @@ -758,8 +809,17 @@ static volatile VP8CPUInfo ssim_last_cpuinfo_used = WEBP_TSAN_IGNORE_FUNCTION void VP8SSIMDspInit(void) { if (ssim_last_cpuinfo_used == VP8GetCPUInfo) return; - VP8SSIMAccumulate = SSIMAccumulate; - VP8SSIMAccumulateClipped = SSIMAccumulateClipped; + VP8SSIMGetClipped = SSIMGetClipped_C; + VP8SSIMGet = SSIMGet_C; + + VP8AccumulateSSE = AccumulateSSE; + if (VP8GetCPUInfo != NULL) { +#if defined(WEBP_USE_SSE2) + if (VP8GetCPUInfo(kSSE2)) { + VP8SSIMDspInitSSE2(); + } +#endif + } ssim_last_cpuinfo_used = VP8GetCPUInfo; } @@ -783,6 +843,7 @@ VP8Metric VP8SSE16x8; VP8Metric VP8SSE4x4; VP8WMetric VP8TDisto4x4; VP8WMetric VP8TDisto16x16; +VP8MeanMetric VP8Mean16x4; VP8QuantizeBlock VP8EncQuantizeBlock; VP8Quantize2Blocks VP8EncQuantize2Blocks; VP8QuantizeBlockWHT VP8EncQuantizeBlockWHT; @@ -795,6 +856,7 @@ extern void VP8EncDspInitAVX2(void); extern void VP8EncDspInitNEON(void); extern void VP8EncDspInitMIPS32(void); extern void VP8EncDspInitMIPSdspR2(void); +extern void VP8EncDspInitMSA(void); static volatile VP8CPUInfo enc_last_cpuinfo_used = (VP8CPUInfo)&enc_last_cpuinfo_used; @@ -820,9 +882,10 @@ WEBP_TSAN_IGNORE_FUNCTION void VP8EncDspInit(void) { VP8SSE4x4 = SSE4x4; VP8TDisto4x4 = Disto4x4; VP8TDisto16x16 = Disto16x16; + VP8Mean16x4 = Mean16x4; VP8EncQuantizeBlock = QuantizeBlock; VP8EncQuantize2Blocks = Quantize2Blocks; - VP8EncQuantizeBlockWHT = QuantizeBlockWHT; + VP8EncQuantizeBlockWHT = QuantizeBlock; VP8Copy4x4 = Copy4x4; VP8Copy16x8 = Copy16x8; @@ -857,6 +920,11 @@ WEBP_TSAN_IGNORE_FUNCTION void VP8EncDspInit(void) { if (VP8GetCPUInfo(kMIPSdspR2)) { VP8EncDspInitMIPSdspR2(); } +#endif +#if defined(WEBP_USE_MSA) + if (VP8GetCPUInfo(kMSA)) { + VP8EncDspInitMSA(); + } #endif } enc_last_cpuinfo_used = VP8GetCPUInfo; diff --git a/thirdparty/libwebp/dsp/enc_mips32.c b/thirdparty/libwebp/dsp/enc_mips32.c index fd10143de9..752b14daf6 100644 --- a/thirdparty/libwebp/dsp/enc_mips32.c +++ b/thirdparty/libwebp/dsp/enc_mips32.c @@ -18,8 +18,8 @@ #if defined(WEBP_USE_MIPS32) #include "./mips_macro.h" -#include "../enc/vp8enci.h" -#include "../enc/cost.h" +#include "../enc/vp8i_enc.h" +#include "../enc/cost_enc.h" static const int kC1 = 20091 + (1 << 16); static const int kC2 = 35468; diff --git a/thirdparty/libwebp/dsp/enc_mips_dsp_r2.c b/thirdparty/libwebp/dsp/enc_mips_dsp_r2.c index 7ab96f6800..6c8c1c6acd 100644 --- a/thirdparty/libwebp/dsp/enc_mips_dsp_r2.c +++ b/thirdparty/libwebp/dsp/enc_mips_dsp_r2.c @@ -17,8 +17,8 @@ #if defined(WEBP_USE_MIPS_DSP_R2) #include "./mips_macro.h" -#include "../enc/cost.h" -#include "../enc/vp8enci.h" +#include "../enc/cost_enc.h" +#include "../enc/vp8i_enc.h" static const int kC1 = 20091 + (1 << 16); static const int kC2 = 35468; diff --git a/thirdparty/libwebp/dsp/enc_msa.c b/thirdparty/libwebp/dsp/enc_msa.c new file mode 100644 index 0000000000..909b46d5d9 --- /dev/null +++ b/thirdparty/libwebp/dsp/enc_msa.c @@ -0,0 +1,892 @@ +// Copyright 2016 Google Inc. All Rights Reserved. +// +// Use of this source code is governed by a BSD-style license +// that can be found in the COPYING file in the root of the source +// tree. An additional intellectual property rights grant can be found +// in the file PATENTS. All contributing project authors may +// be found in the AUTHORS file in the root of the source tree. +// ----------------------------------------------------------------------------- +// +// MSA version of encoder dsp functions. +// +// Author: Prashant Patil (prashant.patil@imgtec.com) + +#include "./dsp.h" + +#if defined(WEBP_USE_MSA) + +#include +#include "./msa_macro.h" +#include "../enc/vp8i_enc.h" + +//------------------------------------------------------------------------------ +// Transforms + +#define IDCT_1D_W(in0, in1, in2, in3, out0, out1, out2, out3) do { \ + v4i32 a1_m, b1_m, c1_m, d1_m; \ + const v4i32 cospi8sqrt2minus1 = __msa_fill_w(20091); \ + const v4i32 sinpi8sqrt2 = __msa_fill_w(35468); \ + v4i32 c_tmp1_m = in1 * sinpi8sqrt2; \ + v4i32 c_tmp2_m = in3 * cospi8sqrt2minus1; \ + v4i32 d_tmp1_m = in1 * cospi8sqrt2minus1; \ + v4i32 d_tmp2_m = in3 * sinpi8sqrt2; \ + \ + ADDSUB2(in0, in2, a1_m, b1_m); \ + SRAI_W2_SW(c_tmp1_m, c_tmp2_m, 16); \ + c_tmp2_m = c_tmp2_m + in3; \ + c1_m = c_tmp1_m - c_tmp2_m; \ + SRAI_W2_SW(d_tmp1_m, d_tmp2_m, 16); \ + d_tmp1_m = d_tmp1_m + in1; \ + d1_m = d_tmp1_m + d_tmp2_m; \ + BUTTERFLY_4(a1_m, b1_m, c1_m, d1_m, out0, out1, out2, out3); \ +} while (0) + +static WEBP_INLINE void ITransformOne(const uint8_t* ref, const int16_t* in, + uint8_t* dst) { + v8i16 input0, input1; + v4i32 in0, in1, in2, in3, hz0, hz1, hz2, hz3, vt0, vt1, vt2, vt3; + v4i32 res0, res1, res2, res3; + v16i8 dest0, dest1, dest2, dest3; + const v16i8 zero = { 0 }; + + LD_SH2(in, 8, input0, input1); + UNPCK_SH_SW(input0, in0, in1); + UNPCK_SH_SW(input1, in2, in3); + IDCT_1D_W(in0, in1, in2, in3, hz0, hz1, hz2, hz3); + TRANSPOSE4x4_SW_SW(hz0, hz1, hz2, hz3, hz0, hz1, hz2, hz3); + IDCT_1D_W(hz0, hz1, hz2, hz3, vt0, vt1, vt2, vt3); + SRARI_W4_SW(vt0, vt1, vt2, vt3, 3); + TRANSPOSE4x4_SW_SW(vt0, vt1, vt2, vt3, vt0, vt1, vt2, vt3); + LD_SB4(ref, BPS, dest0, dest1, dest2, dest3); + ILVR_B4_SW(zero, dest0, zero, dest1, zero, dest2, zero, dest3, + res0, res1, res2, res3); + ILVR_H4_SW(zero, res0, zero, res1, zero, res2, zero, res3, + res0, res1, res2, res3); + ADD4(res0, vt0, res1, vt1, res2, vt2, res3, vt3, res0, res1, res2, res3); + CLIP_SW4_0_255(res0, res1, res2, res3); + PCKEV_B2_SW(res0, res1, res2, res3, vt0, vt1); + res0 = (v4i32)__msa_pckev_b((v16i8)vt0, (v16i8)vt1); + ST4x4_UB(res0, res0, 3, 2, 1, 0, dst, BPS); +} + +static void ITransform(const uint8_t* ref, const int16_t* in, uint8_t* dst, + int do_two) { + ITransformOne(ref, in, dst); + if (do_two) { + ITransformOne(ref + 4, in + 16, dst + 4); + } +} + +static void FTransform(const uint8_t* src, const uint8_t* ref, int16_t* out) { + uint64_t out0, out1, out2, out3; + uint32_t in0, in1, in2, in3; + v4i32 tmp0, tmp1, tmp2, tmp3, tmp4, tmp5; + v8i16 t0, t1, t2, t3; + v16u8 srcl0, srcl1, src0, src1; + const v8i16 mask0 = { 0, 4, 8, 12, 1, 5, 9, 13 }; + const v8i16 mask1 = { 3, 7, 11, 15, 2, 6, 10, 14 }; + const v8i16 mask2 = { 4, 0, 5, 1, 6, 2, 7, 3 }; + const v8i16 mask3 = { 0, 4, 1, 5, 2, 6, 3, 7 }; + const v8i16 cnst0 = { 2217, -5352, 2217, -5352, 2217, -5352, 2217, -5352 }; + const v8i16 cnst1 = { 5352, 2217, 5352, 2217, 5352, 2217, 5352, 2217 }; + + LW4(src, BPS, in0, in1, in2, in3); + INSERT_W4_UB(in0, in1, in2, in3, src0); + LW4(ref, BPS, in0, in1, in2, in3); + INSERT_W4_UB(in0, in1, in2, in3, src1); + ILVRL_B2_UB(src0, src1, srcl0, srcl1); + HSUB_UB2_SH(srcl0, srcl1, t0, t1); + VSHF_H2_SH(t0, t1, t0, t1, mask0, mask1, t2, t3); + ADDSUB2(t2, t3, t0, t1); + t0 = SRLI_H(t0, 3); + VSHF_H2_SH(t0, t0, t1, t1, mask2, mask3, t3, t2); + tmp0 = __msa_hadd_s_w(t3, t3); + tmp2 = __msa_hsub_s_w(t3, t3); + FILL_W2_SW(1812, 937, tmp1, tmp3); + DPADD_SH2_SW(t2, t2, cnst0, cnst1, tmp3, tmp1); + SRAI_W2_SW(tmp1, tmp3, 9); + PCKEV_H2_SH(tmp1, tmp0, tmp3, tmp2, t0, t1); + VSHF_H2_SH(t0, t1, t0, t1, mask0, mask1, t2, t3); + ADDSUB2(t2, t3, t0, t1); + VSHF_H2_SH(t0, t0, t1, t1, mask2, mask3, t3, t2); + tmp0 = __msa_hadd_s_w(t3, t3); + tmp2 = __msa_hsub_s_w(t3, t3); + ADDVI_W2_SW(tmp0, 7, tmp2, 7, tmp0, tmp2); + SRAI_W2_SW(tmp0, tmp2, 4); + FILL_W2_SW(12000, 51000, tmp1, tmp3); + DPADD_SH2_SW(t2, t2, cnst0, cnst1, tmp3, tmp1); + SRAI_W2_SW(tmp1, tmp3, 16); + UNPCK_R_SH_SW(t1, tmp4); + tmp5 = __msa_ceqi_w(tmp4, 0); + tmp4 = (v4i32)__msa_nor_v((v16u8)tmp5, (v16u8)tmp5); + tmp5 = __msa_fill_w(1); + tmp5 = (v4i32)__msa_and_v((v16u8)tmp5, (v16u8)tmp4); + tmp1 += tmp5; + PCKEV_H2_SH(tmp1, tmp0, tmp3, tmp2, t0, t1); + out0 = __msa_copy_s_d((v2i64)t0, 0); + out1 = __msa_copy_s_d((v2i64)t0, 1); + out2 = __msa_copy_s_d((v2i64)t1, 0); + out3 = __msa_copy_s_d((v2i64)t1, 1); + SD4(out0, out1, out2, out3, out, 8); +} + +static void FTransformWHT(const int16_t* in, int16_t* out) { + v8i16 in0 = { 0 }; + v8i16 in1 = { 0 }; + v8i16 tmp0, tmp1, tmp2, tmp3; + v8i16 out0, out1; + const v8i16 mask0 = { 0, 1, 2, 3, 8, 9, 10, 11 }; + const v8i16 mask1 = { 4, 5, 6, 7, 12, 13, 14, 15 }; + const v8i16 mask2 = { 0, 4, 8, 12, 1, 5, 9, 13 }; + const v8i16 mask3 = { 3, 7, 11, 15, 2, 6, 10, 14 }; + + in0 = __msa_insert_h(in0, 0, in[ 0]); + in0 = __msa_insert_h(in0, 1, in[ 64]); + in0 = __msa_insert_h(in0, 2, in[128]); + in0 = __msa_insert_h(in0, 3, in[192]); + in0 = __msa_insert_h(in0, 4, in[ 16]); + in0 = __msa_insert_h(in0, 5, in[ 80]); + in0 = __msa_insert_h(in0, 6, in[144]); + in0 = __msa_insert_h(in0, 7, in[208]); + in1 = __msa_insert_h(in1, 0, in[ 48]); + in1 = __msa_insert_h(in1, 1, in[112]); + in1 = __msa_insert_h(in1, 2, in[176]); + in1 = __msa_insert_h(in1, 3, in[240]); + in1 = __msa_insert_h(in1, 4, in[ 32]); + in1 = __msa_insert_h(in1, 5, in[ 96]); + in1 = __msa_insert_h(in1, 6, in[160]); + in1 = __msa_insert_h(in1, 7, in[224]); + ADDSUB2(in0, in1, tmp0, tmp1); + VSHF_H2_SH(tmp0, tmp1, tmp0, tmp1, mask0, mask1, tmp2, tmp3); + ADDSUB2(tmp2, tmp3, tmp0, tmp1); + VSHF_H2_SH(tmp0, tmp1, tmp0, tmp1, mask2, mask3, in0, in1); + ADDSUB2(in0, in1, tmp0, tmp1); + VSHF_H2_SH(tmp0, tmp1, tmp0, tmp1, mask0, mask1, tmp2, tmp3); + ADDSUB2(tmp2, tmp3, out0, out1); + SRAI_H2_SH(out0, out1, 1); + ST_SH2(out0, out1, out, 8); +} + +static int TTransform(const uint8_t* in, const uint16_t* w) { + int sum; + uint32_t in0_m, in1_m, in2_m, in3_m; + v16i8 src0; + v8i16 in0, in1, tmp0, tmp1, tmp2, tmp3; + v4i32 dst0, dst1; + const v16i8 zero = { 0 }; + const v8i16 mask0 = { 0, 1, 2, 3, 8, 9, 10, 11 }; + const v8i16 mask1 = { 4, 5, 6, 7, 12, 13, 14, 15 }; + const v8i16 mask2 = { 0, 4, 8, 12, 1, 5, 9, 13 }; + const v8i16 mask3 = { 3, 7, 11, 15, 2, 6, 10, 14 }; + + LW4(in, BPS, in0_m, in1_m, in2_m, in3_m); + INSERT_W4_SB(in0_m, in1_m, in2_m, in3_m, src0); + ILVRL_B2_SH(zero, src0, tmp0, tmp1); + VSHF_H2_SH(tmp0, tmp1, tmp0, tmp1, mask2, mask3, in0, in1); + ADDSUB2(in0, in1, tmp0, tmp1); + VSHF_H2_SH(tmp0, tmp1, tmp0, tmp1, mask0, mask1, tmp2, tmp3); + ADDSUB2(tmp2, tmp3, tmp0, tmp1); + VSHF_H2_SH(tmp0, tmp1, tmp0, tmp1, mask2, mask3, in0, in1); + ADDSUB2(in0, in1, tmp0, tmp1); + VSHF_H2_SH(tmp0, tmp1, tmp0, tmp1, mask0, mask1, tmp2, tmp3); + ADDSUB2(tmp2, tmp3, tmp0, tmp1); + tmp0 = __msa_add_a_h(tmp0, (v8i16)zero); + tmp1 = __msa_add_a_h(tmp1, (v8i16)zero); + LD_SH2(w, 8, tmp2, tmp3); + DOTP_SH2_SW(tmp0, tmp1, tmp2, tmp3, dst0, dst1); + dst0 = dst0 + dst1; + sum = HADD_SW_S32(dst0); + return sum; +} + +static int Disto4x4(const uint8_t* const a, const uint8_t* const b, + const uint16_t* const w) { + const int sum1 = TTransform(a, w); + const int sum2 = TTransform(b, w); + return abs(sum2 - sum1) >> 5; +} + +static int Disto16x16(const uint8_t* const a, const uint8_t* const b, + const uint16_t* const w) { + int D = 0; + int x, y; + for (y = 0; y < 16 * BPS; y += 4 * BPS) { + for (x = 0; x < 16; x += 4) { + D += Disto4x4(a + x + y, b + x + y, w); + } + } + return D; +} + +//------------------------------------------------------------------------------ +// Histogram + +static void CollectHistogram(const uint8_t* ref, const uint8_t* pred, + int start_block, int end_block, + VP8Histogram* const histo) { + int j; + int distribution[MAX_COEFF_THRESH + 1] = { 0 }; + for (j = start_block; j < end_block; ++j) { + int16_t out[16]; + VP8FTransform(ref + VP8DspScan[j], pred + VP8DspScan[j], out); + { + int k; + v8i16 coeff0, coeff1; + const v8i16 zero = { 0 }; + const v8i16 max_coeff_thr = __msa_ldi_h(MAX_COEFF_THRESH); + LD_SH2(&out[0], 8, coeff0, coeff1); + coeff0 = __msa_add_a_h(coeff0, zero); + coeff1 = __msa_add_a_h(coeff1, zero); + SRAI_H2_SH(coeff0, coeff1, 3); + coeff0 = __msa_min_s_h(coeff0, max_coeff_thr); + coeff1 = __msa_min_s_h(coeff1, max_coeff_thr); + ST_SH2(coeff0, coeff1, &out[0], 8); + for (k = 0; k < 16; ++k) { + ++distribution[out[k]]; + } + } + } + VP8SetHistogramData(distribution, histo); +} + +//------------------------------------------------------------------------------ +// Intra predictions + +// luma 4x4 prediction + +#define DST(x, y) dst[(x) + (y) * BPS] +#define AVG3(a, b, c) (((a) + 2 * (b) + (c) + 2) >> 2) +#define AVG2(a, b) (((a) + (b) + 1) >> 1) + +static WEBP_INLINE void VE4(uint8_t* dst, const uint8_t* top) { // vertical + const uint64_t val_m = LD(top - 1); + const v16u8 A = (v16u8)__msa_insert_d((v2i64)A, 0, val_m); + const v16u8 B = SLDI_UB(A, A, 1); + const v16u8 C = SLDI_UB(A, A, 2); + const v16u8 AC = __msa_ave_u_b(A, C); + const v16u8 B2 = __msa_ave_u_b(B, B); + const v16u8 R = __msa_aver_u_b(AC, B2); + const uint32_t out = __msa_copy_s_w((v4i32)R, 0); + SW4(out, out, out, out, dst, BPS); +} + +static WEBP_INLINE void HE4(uint8_t* dst, const uint8_t* top) { // horizontal + const int X = top[-1]; + const int I = top[-2]; + const int J = top[-3]; + const int K = top[-4]; + const int L = top[-5]; + WebPUint32ToMem(dst + 0 * BPS, 0x01010101U * AVG3(X, I, J)); + WebPUint32ToMem(dst + 1 * BPS, 0x01010101U * AVG3(I, J, K)); + WebPUint32ToMem(dst + 2 * BPS, 0x01010101U * AVG3(J, K, L)); + WebPUint32ToMem(dst + 3 * BPS, 0x01010101U * AVG3(K, L, L)); +} + +static WEBP_INLINE void DC4(uint8_t* dst, const uint8_t* top) { + uint32_t dc = 4; + int i; + for (i = 0; i < 4; ++i) dc += top[i] + top[-5 + i]; + dc >>= 3; + dc = dc | (dc << 8) | (dc << 16) | (dc << 24); + SW4(dc, dc, dc, dc, dst, BPS); +} + +static WEBP_INLINE void RD4(uint8_t* dst, const uint8_t* top) { + const uint64_t val_m = LD(top - 5); + const v16u8 A1 = (v16u8)__msa_insert_d((v2i64)A1, 0, val_m); + const v16u8 A = (v16u8)__msa_insert_b((v16i8)A1, 8, top[3]); + const v16u8 B = SLDI_UB(A, A, 1); + const v16u8 C = SLDI_UB(A, A, 2); + const v16u8 AC = __msa_ave_u_b(A, C); + const v16u8 B2 = __msa_ave_u_b(B, B); + const v16u8 R0 = __msa_aver_u_b(AC, B2); + const v16u8 R1 = SLDI_UB(R0, R0, 1); + const v16u8 R2 = SLDI_UB(R1, R1, 1); + const v16u8 R3 = SLDI_UB(R2, R2, 1); + const uint32_t val0 = __msa_copy_s_w((v4i32)R0, 0); + const uint32_t val1 = __msa_copy_s_w((v4i32)R1, 0); + const uint32_t val2 = __msa_copy_s_w((v4i32)R2, 0); + const uint32_t val3 = __msa_copy_s_w((v4i32)R3, 0); + SW4(val3, val2, val1, val0, dst, BPS); +} + +static WEBP_INLINE void LD4(uint8_t* dst, const uint8_t* top) { + const uint64_t val_m = LD(top); + const v16u8 A = (v16u8)__msa_insert_d((v2i64)A, 0, val_m); + const v16u8 B = SLDI_UB(A, A, 1); + const v16u8 C1 = SLDI_UB(A, A, 2); + const v16u8 C = (v16u8)__msa_insert_b((v16i8)C1, 6, top[7]); + const v16u8 AC = __msa_ave_u_b(A, C); + const v16u8 B2 = __msa_ave_u_b(B, B); + const v16u8 R0 = __msa_aver_u_b(AC, B2); + const v16u8 R1 = SLDI_UB(R0, R0, 1); + const v16u8 R2 = SLDI_UB(R1, R1, 1); + const v16u8 R3 = SLDI_UB(R2, R2, 1); + const uint32_t val0 = __msa_copy_s_w((v4i32)R0, 0); + const uint32_t val1 = __msa_copy_s_w((v4i32)R1, 0); + const uint32_t val2 = __msa_copy_s_w((v4i32)R2, 0); + const uint32_t val3 = __msa_copy_s_w((v4i32)R3, 0); + SW4(val0, val1, val2, val3, dst, BPS); +} + +static WEBP_INLINE void VR4(uint8_t* dst, const uint8_t* top) { + const int X = top[-1]; + const int I = top[-2]; + const int J = top[-3]; + const int K = top[-4]; + const int A = top[0]; + const int B = top[1]; + const int C = top[2]; + const int D = top[3]; + DST(0, 0) = DST(1, 2) = AVG2(X, A); + DST(1, 0) = DST(2, 2) = AVG2(A, B); + DST(2, 0) = DST(3, 2) = AVG2(B, C); + DST(3, 0) = AVG2(C, D); + DST(0, 3) = AVG3(K, J, I); + DST(0, 2) = AVG3(J, I, X); + DST(0, 1) = DST(1, 3) = AVG3(I, X, A); + DST(1, 1) = DST(2, 3) = AVG3(X, A, B); + DST(2, 1) = DST(3, 3) = AVG3(A, B, C); + DST(3, 1) = AVG3(B, C, D); +} + +static WEBP_INLINE void VL4(uint8_t* dst, const uint8_t* top) { + const int A = top[0]; + const int B = top[1]; + const int C = top[2]; + const int D = top[3]; + const int E = top[4]; + const int F = top[5]; + const int G = top[6]; + const int H = top[7]; + DST(0, 0) = AVG2(A, B); + DST(1, 0) = DST(0, 2) = AVG2(B, C); + DST(2, 0) = DST(1, 2) = AVG2(C, D); + DST(3, 0) = DST(2, 2) = AVG2(D, E); + DST(0, 1) = AVG3(A, B, C); + DST(1, 1) = DST(0, 3) = AVG3(B, C, D); + DST(2, 1) = DST(1, 3) = AVG3(C, D, E); + DST(3, 1) = DST(2, 3) = AVG3(D, E, F); + DST(3, 2) = AVG3(E, F, G); + DST(3, 3) = AVG3(F, G, H); +} + +static WEBP_INLINE void HU4(uint8_t* dst, const uint8_t* top) { + const int I = top[-2]; + const int J = top[-3]; + const int K = top[-4]; + const int L = top[-5]; + DST(0, 0) = AVG2(I, J); + DST(2, 0) = DST(0, 1) = AVG2(J, K); + DST(2, 1) = DST(0, 2) = AVG2(K, L); + DST(1, 0) = AVG3(I, J, K); + DST(3, 0) = DST(1, 1) = AVG3(J, K, L); + DST(3, 1) = DST(1, 2) = AVG3(K, L, L); + DST(3, 2) = DST(2, 2) = + DST(0, 3) = DST(1, 3) = DST(2, 3) = DST(3, 3) = L; +} + +static WEBP_INLINE void HD4(uint8_t* dst, const uint8_t* top) { + const int X = top[-1]; + const int I = top[-2]; + const int J = top[-3]; + const int K = top[-4]; + const int L = top[-5]; + const int A = top[0]; + const int B = top[1]; + const int C = top[2]; + DST(0, 0) = DST(2, 1) = AVG2(I, X); + DST(0, 1) = DST(2, 2) = AVG2(J, I); + DST(0, 2) = DST(2, 3) = AVG2(K, J); + DST(0, 3) = AVG2(L, K); + DST(3, 0) = AVG3(A, B, C); + DST(2, 0) = AVG3(X, A, B); + DST(1, 0) = DST(3, 1) = AVG3(I, X, A); + DST(1, 1) = DST(3, 2) = AVG3(J, I, X); + DST(1, 2) = DST(3, 3) = AVG3(K, J, I); + DST(1, 3) = AVG3(L, K, J); +} + +static WEBP_INLINE void TM4(uint8_t* dst, const uint8_t* top) { + const v16i8 zero = { 0 }; + const v8i16 TL = (v8i16)__msa_fill_h(top[-1]); + const v8i16 L0 = (v8i16)__msa_fill_h(top[-2]); + const v8i16 L1 = (v8i16)__msa_fill_h(top[-3]); + const v8i16 L2 = (v8i16)__msa_fill_h(top[-4]); + const v8i16 L3 = (v8i16)__msa_fill_h(top[-5]); + const v16u8 T1 = LD_UB(top); + const v8i16 T = (v8i16)__msa_ilvr_b(zero, (v16i8)T1); + const v8i16 d = T - TL; + v8i16 r0, r1, r2, r3; + ADD4(d, L0, d, L1, d, L2, d, L3, r0, r1, r2, r3); + CLIP_SH4_0_255(r0, r1, r2, r3); + PCKEV_ST4x4_UB(r0, r1, r2, r3, dst, BPS); +} + +#undef DST +#undef AVG3 +#undef AVG2 + +static void Intra4Preds(uint8_t* dst, const uint8_t* top) { + DC4(I4DC4 + dst, top); + TM4(I4TM4 + dst, top); + VE4(I4VE4 + dst, top); + HE4(I4HE4 + dst, top); + RD4(I4RD4 + dst, top); + VR4(I4VR4 + dst, top); + LD4(I4LD4 + dst, top); + VL4(I4VL4 + dst, top); + HD4(I4HD4 + dst, top); + HU4(I4HU4 + dst, top); +} + +// luma 16x16 prediction + +#define STORE16x16(out, dst) do { \ + ST_UB8(out, out, out, out, out, out, out, out, dst + 0 * BPS, BPS); \ + ST_UB8(out, out, out, out, out, out, out, out, dst + 8 * BPS, BPS); \ +} while (0) + +static WEBP_INLINE void VerticalPred16x16(uint8_t* dst, const uint8_t* top) { + if (top != NULL) { + const v16u8 out = LD_UB(top); + STORE16x16(out, dst); + } else { + const v16u8 out = (v16u8)__msa_fill_b(0x7f); + STORE16x16(out, dst); + } +} + +static WEBP_INLINE void HorizontalPred16x16(uint8_t* dst, + const uint8_t* left) { + if (left != NULL) { + int j; + for (j = 0; j < 16; j += 4) { + const v16u8 L0 = (v16u8)__msa_fill_b(left[0]); + const v16u8 L1 = (v16u8)__msa_fill_b(left[1]); + const v16u8 L2 = (v16u8)__msa_fill_b(left[2]); + const v16u8 L3 = (v16u8)__msa_fill_b(left[3]); + ST_UB4(L0, L1, L2, L3, dst, BPS); + dst += 4 * BPS; + left += 4; + } + } else { + const v16u8 out = (v16u8)__msa_fill_b(0x81); + STORE16x16(out, dst); + } +} + +static WEBP_INLINE void TrueMotion16x16(uint8_t* dst, const uint8_t* left, + const uint8_t* top) { + if (left != NULL) { + if (top != NULL) { + int j; + v8i16 d1, d2; + const v16i8 zero = { 0 }; + const v8i16 TL = (v8i16)__msa_fill_h(left[-1]); + const v16u8 T = LD_UB(top); + ILVRL_B2_SH(zero, T, d1, d2); + SUB2(d1, TL, d2, TL, d1, d2); + for (j = 0; j < 16; j += 4) { + v16i8 t0, t1, t2, t3; + v8i16 r0, r1, r2, r3, r4, r5, r6, r7; + const v8i16 L0 = (v8i16)__msa_fill_h(left[j + 0]); + const v8i16 L1 = (v8i16)__msa_fill_h(left[j + 1]); + const v8i16 L2 = (v8i16)__msa_fill_h(left[j + 2]); + const v8i16 L3 = (v8i16)__msa_fill_h(left[j + 3]); + ADD4(d1, L0, d1, L1, d1, L2, d1, L3, r0, r1, r2, r3); + ADD4(d2, L0, d2, L1, d2, L2, d2, L3, r4, r5, r6, r7); + CLIP_SH4_0_255(r0, r1, r2, r3); + CLIP_SH4_0_255(r4, r5, r6, r7); + PCKEV_B4_SB(r4, r0, r5, r1, r6, r2, r7, r3, t0, t1, t2, t3); + ST_SB4(t0, t1, t2, t3, dst, BPS); + dst += 4 * BPS; + } + } else { + HorizontalPred16x16(dst, left); + } + } else { + if (top != NULL) { + VerticalPred16x16(dst, top); + } else { + const v16u8 out = (v16u8)__msa_fill_b(0x81); + STORE16x16(out, dst); + } + } +} + +static WEBP_INLINE void DCMode16x16(uint8_t* dst, const uint8_t* left, + const uint8_t* top) { + int DC; + v16u8 out; + if (top != NULL && left != NULL) { + const v16u8 rtop = LD_UB(top); + const v8u16 dctop = __msa_hadd_u_h(rtop, rtop); + const v16u8 rleft = LD_UB(left); + const v8u16 dcleft = __msa_hadd_u_h(rleft, rleft); + const v8u16 dctemp = dctop + dcleft; + DC = HADD_UH_U32(dctemp); + DC = (DC + 16) >> 5; + } else if (left != NULL) { // left but no top + const v16u8 rleft = LD_UB(left); + const v8u16 dcleft = __msa_hadd_u_h(rleft, rleft); + DC = HADD_UH_U32(dcleft); + DC = (DC + DC + 16) >> 5; + } else if (top != NULL) { // top but no left + const v16u8 rtop = LD_UB(top); + const v8u16 dctop = __msa_hadd_u_h(rtop, rtop); + DC = HADD_UH_U32(dctop); + DC = (DC + DC + 16) >> 5; + } else { // no top, no left, nothing. + DC = 0x80; + } + out = (v16u8)__msa_fill_b(DC); + STORE16x16(out, dst); +} + +static void Intra16Preds(uint8_t* dst, + const uint8_t* left, const uint8_t* top) { + DCMode16x16(I16DC16 + dst, left, top); + VerticalPred16x16(I16VE16 + dst, top); + HorizontalPred16x16(I16HE16 + dst, left); + TrueMotion16x16(I16TM16 + dst, left, top); +} + +// Chroma 8x8 prediction + +#define CALC_DC8(in, out) do { \ + const v8u16 temp0 = __msa_hadd_u_h(in, in); \ + const v4u32 temp1 = __msa_hadd_u_w(temp0, temp0); \ + const v2i64 temp2 = (v2i64)__msa_hadd_u_d(temp1, temp1); \ + const v2i64 temp3 = __msa_splati_d(temp2, 1); \ + const v2i64 temp4 = temp3 + temp2; \ + const v16i8 temp5 = (v16i8)__msa_srari_d(temp4, 4); \ + const v2i64 temp6 = (v2i64)__msa_splati_b(temp5, 0); \ + out = __msa_copy_s_d(temp6, 0); \ +} while (0) + +#define STORE8x8(out, dst) do { \ + SD4(out, out, out, out, dst + 0 * BPS, BPS); \ + SD4(out, out, out, out, dst + 4 * BPS, BPS); \ +} while (0) + +static WEBP_INLINE void VerticalPred8x8(uint8_t* dst, const uint8_t* top) { + if (top != NULL) { + const uint64_t out = LD(top); + STORE8x8(out, dst); + } else { + const uint64_t out = 0x7f7f7f7f7f7f7f7fULL; + STORE8x8(out, dst); + } +} + +static WEBP_INLINE void HorizontalPred8x8(uint8_t* dst, const uint8_t* left) { + if (left != NULL) { + int j; + for (j = 0; j < 8; j += 4) { + const v16u8 L0 = (v16u8)__msa_fill_b(left[0]); + const v16u8 L1 = (v16u8)__msa_fill_b(left[1]); + const v16u8 L2 = (v16u8)__msa_fill_b(left[2]); + const v16u8 L3 = (v16u8)__msa_fill_b(left[3]); + const uint64_t out0 = __msa_copy_s_d((v2i64)L0, 0); + const uint64_t out1 = __msa_copy_s_d((v2i64)L1, 0); + const uint64_t out2 = __msa_copy_s_d((v2i64)L2, 0); + const uint64_t out3 = __msa_copy_s_d((v2i64)L3, 0); + SD4(out0, out1, out2, out3, dst, BPS); + dst += 4 * BPS; + left += 4; + } + } else { + const uint64_t out = 0x8181818181818181ULL; + STORE8x8(out, dst); + } +} + +static WEBP_INLINE void TrueMotion8x8(uint8_t* dst, const uint8_t* left, + const uint8_t* top) { + if (left != NULL) { + if (top != NULL) { + int j; + const v8i16 TL = (v8i16)__msa_fill_h(left[-1]); + const v16u8 T1 = LD_UB(top); + const v16i8 zero = { 0 }; + const v8i16 T = (v8i16)__msa_ilvr_b(zero, (v16i8)T1); + const v8i16 d = T - TL; + for (j = 0; j < 8; j += 4) { + uint64_t out0, out1, out2, out3; + v16i8 t0, t1; + v8i16 r0 = (v8i16)__msa_fill_h(left[j + 0]); + v8i16 r1 = (v8i16)__msa_fill_h(left[j + 1]); + v8i16 r2 = (v8i16)__msa_fill_h(left[j + 2]); + v8i16 r3 = (v8i16)__msa_fill_h(left[j + 3]); + ADD4(d, r0, d, r1, d, r2, d, r3, r0, r1, r2, r3); + CLIP_SH4_0_255(r0, r1, r2, r3); + PCKEV_B2_SB(r1, r0, r3, r2, t0, t1); + out0 = __msa_copy_s_d((v2i64)t0, 0); + out1 = __msa_copy_s_d((v2i64)t0, 1); + out2 = __msa_copy_s_d((v2i64)t1, 0); + out3 = __msa_copy_s_d((v2i64)t1, 1); + SD4(out0, out1, out2, out3, dst, BPS); + dst += 4 * BPS; + } + } else { + HorizontalPred8x8(dst, left); + } + } else { + if (top != NULL) { + VerticalPred8x8(dst, top); + } else { + const uint64_t out = 0x8181818181818181ULL; + STORE8x8(out, dst); + } + } +} + +static WEBP_INLINE void DCMode8x8(uint8_t* dst, const uint8_t* left, + const uint8_t* top) { + uint64_t out; + v16u8 src; + if (top != NULL && left != NULL) { + const uint64_t left_m = LD(left); + const uint64_t top_m = LD(top); + INSERT_D2_UB(left_m, top_m, src); + CALC_DC8(src, out); + } else if (left != NULL) { // left but no top + const uint64_t left_m = LD(left); + INSERT_D2_UB(left_m, left_m, src); + CALC_DC8(src, out); + } else if (top != NULL) { // top but no left + const uint64_t top_m = LD(top); + INSERT_D2_UB(top_m, top_m, src); + CALC_DC8(src, out); + } else { // no top, no left, nothing. + src = (v16u8)__msa_fill_b(0x80); + out = __msa_copy_s_d((v2i64)src, 0); + } + STORE8x8(out, dst); +} + +static void IntraChromaPreds(uint8_t* dst, const uint8_t* left, + const uint8_t* top) { + // U block + DCMode8x8(C8DC8 + dst, left, top); + VerticalPred8x8(C8VE8 + dst, top); + HorizontalPred8x8(C8HE8 + dst, left); + TrueMotion8x8(C8TM8 + dst, left, top); + // V block + dst += 8; + if (top != NULL) top += 8; + if (left != NULL) left += 16; + DCMode8x8(C8DC8 + dst, left, top); + VerticalPred8x8(C8VE8 + dst, top); + HorizontalPred8x8(C8HE8 + dst, left); + TrueMotion8x8(C8TM8 + dst, left, top); +} + +//------------------------------------------------------------------------------ +// Metric + +#define PACK_DOTP_UB4_SW(in0, in1, in2, in3, out0, out1, out2, out3) do { \ + v16u8 tmp0, tmp1; \ + v8i16 tmp2, tmp3; \ + ILVRL_B2_UB(in0, in1, tmp0, tmp1); \ + HSUB_UB2_SH(tmp0, tmp1, tmp2, tmp3); \ + DOTP_SH2_SW(tmp2, tmp3, tmp2, tmp3, out0, out1); \ + ILVRL_B2_UB(in2, in3, tmp0, tmp1); \ + HSUB_UB2_SH(tmp0, tmp1, tmp2, tmp3); \ + DOTP_SH2_SW(tmp2, tmp3, tmp2, tmp3, out2, out3); \ +} while (0) + +#define PACK_DPADD_UB4_SW(in0, in1, in2, in3, out0, out1, out2, out3) do { \ + v16u8 tmp0, tmp1; \ + v8i16 tmp2, tmp3; \ + ILVRL_B2_UB(in0, in1, tmp0, tmp1); \ + HSUB_UB2_SH(tmp0, tmp1, tmp2, tmp3); \ + DPADD_SH2_SW(tmp2, tmp3, tmp2, tmp3, out0, out1); \ + ILVRL_B2_UB(in2, in3, tmp0, tmp1); \ + HSUB_UB2_SH(tmp0, tmp1, tmp2, tmp3); \ + DPADD_SH2_SW(tmp2, tmp3, tmp2, tmp3, out2, out3); \ +} while (0) + +static int SSE16x16(const uint8_t* a, const uint8_t* b) { + uint32_t sum; + v16u8 src0, src1, src2, src3, src4, src5, src6, src7; + v16u8 ref0, ref1, ref2, ref3, ref4, ref5, ref6, ref7; + v4i32 out0, out1, out2, out3; + + LD_UB8(a, BPS, src0, src1, src2, src3, src4, src5, src6, src7); + LD_UB8(b, BPS, ref0, ref1, ref2, ref3, ref4, ref5, ref6, ref7); + PACK_DOTP_UB4_SW(src0, ref0, src1, ref1, out0, out1, out2, out3); + PACK_DPADD_UB4_SW(src2, ref2, src3, ref3, out0, out1, out2, out3); + PACK_DPADD_UB4_SW(src4, ref4, src5, ref5, out0, out1, out2, out3); + PACK_DPADD_UB4_SW(src6, ref6, src7, ref7, out0, out1, out2, out3); + a += 8 * BPS; + b += 8 * BPS; + LD_UB8(a, BPS, src0, src1, src2, src3, src4, src5, src6, src7); + LD_UB8(b, BPS, ref0, ref1, ref2, ref3, ref4, ref5, ref6, ref7); + PACK_DPADD_UB4_SW(src0, ref0, src1, ref1, out0, out1, out2, out3); + PACK_DPADD_UB4_SW(src2, ref2, src3, ref3, out0, out1, out2, out3); + PACK_DPADD_UB4_SW(src4, ref4, src5, ref5, out0, out1, out2, out3); + PACK_DPADD_UB4_SW(src6, ref6, src7, ref7, out0, out1, out2, out3); + out0 += out1; + out2 += out3; + out0 += out2; + sum = HADD_SW_S32(out0); + return sum; +} + +static int SSE16x8(const uint8_t* a, const uint8_t* b) { + uint32_t sum; + v16u8 src0, src1, src2, src3, src4, src5, src6, src7; + v16u8 ref0, ref1, ref2, ref3, ref4, ref5, ref6, ref7; + v4i32 out0, out1, out2, out3; + + LD_UB8(a, BPS, src0, src1, src2, src3, src4, src5, src6, src7); + LD_UB8(b, BPS, ref0, ref1, ref2, ref3, ref4, ref5, ref6, ref7); + PACK_DOTP_UB4_SW(src0, ref0, src1, ref1, out0, out1, out2, out3); + PACK_DPADD_UB4_SW(src2, ref2, src3, ref3, out0, out1, out2, out3); + PACK_DPADD_UB4_SW(src4, ref4, src5, ref5, out0, out1, out2, out3); + PACK_DPADD_UB4_SW(src6, ref6, src7, ref7, out0, out1, out2, out3); + out0 += out1; + out2 += out3; + out0 += out2; + sum = HADD_SW_S32(out0); + return sum; +} + +static int SSE8x8(const uint8_t* a, const uint8_t* b) { + uint32_t sum; + v16u8 src0, src1, src2, src3, src4, src5, src6, src7; + v16u8 ref0, ref1, ref2, ref3, ref4, ref5, ref6, ref7; + v16u8 t0, t1, t2, t3; + v4i32 out0, out1, out2, out3; + + LD_UB8(a, BPS, src0, src1, src2, src3, src4, src5, src6, src7); + LD_UB8(b, BPS, ref0, ref1, ref2, ref3, ref4, ref5, ref6, ref7); + ILVR_B4_UB(src0, src1, src2, src3, ref0, ref1, ref2, ref3, t0, t1, t2, t3); + PACK_DOTP_UB4_SW(t0, t2, t1, t3, out0, out1, out2, out3); + ILVR_B4_UB(src4, src5, src6, src7, ref4, ref5, ref6, ref7, t0, t1, t2, t3); + PACK_DPADD_UB4_SW(t0, t2, t1, t3, out0, out1, out2, out3); + out0 += out1; + out2 += out3; + out0 += out2; + sum = HADD_SW_S32(out0); + return sum; +} + +static int SSE4x4(const uint8_t* a, const uint8_t* b) { + uint32_t sum = 0; + uint32_t src0, src1, src2, src3, ref0, ref1, ref2, ref3; + v16u8 src, ref, tmp0, tmp1; + v8i16 diff0, diff1; + v4i32 out0, out1; + + LW4(a, BPS, src0, src1, src2, src3); + LW4(b, BPS, ref0, ref1, ref2, ref3); + INSERT_W4_UB(src0, src1, src2, src3, src); + INSERT_W4_UB(ref0, ref1, ref2, ref3, ref); + ILVRL_B2_UB(src, ref, tmp0, tmp1); + HSUB_UB2_SH(tmp0, tmp1, diff0, diff1); + DOTP_SH2_SW(diff0, diff1, diff0, diff1, out0, out1); + out0 += out1; + sum = HADD_SW_S32(out0); + return sum; +} + +//------------------------------------------------------------------------------ +// Quantization + +static int QuantizeBlock(int16_t in[16], int16_t out[16], + const VP8Matrix* const mtx) { + int sum; + v8i16 in0, in1, sh0, sh1, out0, out1; + v8i16 tmp0, tmp1, tmp2, tmp3, tmp4, tmp5, sign0, sign1; + v4i32 s0, s1, s2, s3, b0, b1, b2, b3, t0, t1, t2, t3; + const v8i16 zero = { 0 }; + const v8i16 zigzag0 = { 0, 1, 4, 8, 5, 2, 3, 6 }; + const v8i16 zigzag1 = { 9, 12, 13, 10, 7, 11, 14, 15 }; + const v8i16 maxlevel = __msa_fill_h(MAX_LEVEL); + + LD_SH2(&in[0], 8, in0, in1); + LD_SH2(&mtx->sharpen_[0], 8, sh0, sh1); + tmp4 = __msa_add_a_h(in0, zero); + tmp5 = __msa_add_a_h(in1, zero); + ILVRL_H2_SH(sh0, tmp4, tmp0, tmp1); + ILVRL_H2_SH(sh1, tmp5, tmp2, tmp3); + HADD_SH4_SW(tmp0, tmp1, tmp2, tmp3, s0, s1, s2, s3); + sign0 = (in0 < zero); + sign1 = (in1 < zero); // sign + LD_SH2(&mtx->iq_[0], 8, tmp0, tmp1); // iq + ILVRL_H2_SW(zero, tmp0, t0, t1); + ILVRL_H2_SW(zero, tmp1, t2, t3); + LD_SW4(&mtx->bias_[0], 4, b0, b1, b2, b3); // bias + MUL4(t0, s0, t1, s1, t2, s2, t3, s3, t0, t1, t2, t3); + ADD4(b0, t0, b1, t1, b2, t2, b3, t3, b0, b1, b2, b3); + SRAI_W4_SW(b0, b1, b2, b3, 17); + PCKEV_H2_SH(b1, b0, b3, b2, tmp2, tmp3); + tmp0 = (tmp2 > maxlevel); + tmp1 = (tmp3 > maxlevel); + tmp2 = (v8i16)__msa_bmnz_v((v16u8)tmp2, (v16u8)maxlevel, (v16u8)tmp0); + tmp3 = (v8i16)__msa_bmnz_v((v16u8)tmp3, (v16u8)maxlevel, (v16u8)tmp1); + SUB2(0, tmp2, 0, tmp3, tmp0, tmp1); + tmp2 = (v8i16)__msa_bmnz_v((v16u8)tmp2, (v16u8)tmp0, (v16u8)sign0); + tmp3 = (v8i16)__msa_bmnz_v((v16u8)tmp3, (v16u8)tmp1, (v16u8)sign1); + LD_SW4(&mtx->zthresh_[0], 4, t0, t1, t2, t3); // zthresh + t0 = (s0 > t0); + t1 = (s1 > t1); + t2 = (s2 > t2); + t3 = (s3 > t3); + PCKEV_H2_SH(t1, t0, t3, t2, tmp0, tmp1); + tmp4 = (v8i16)__msa_bmnz_v((v16u8)zero, (v16u8)tmp2, (v16u8)tmp0); + tmp5 = (v8i16)__msa_bmnz_v((v16u8)zero, (v16u8)tmp3, (v16u8)tmp1); + LD_SH2(&mtx->q_[0], 8, tmp0, tmp1); + MUL2(tmp4, tmp0, tmp5, tmp1, in0, in1); + VSHF_H2_SH(tmp4, tmp5, tmp4, tmp5, zigzag0, zigzag1, out0, out1); + ST_SH2(in0, in1, &in[0], 8); + ST_SH2(out0, out1, &out[0], 8); + out0 = __msa_add_a_h(out0, out1); + sum = HADD_SH_S32(out0); + return (sum > 0); +} + +static int Quantize2Blocks(int16_t in[32], int16_t out[32], + const VP8Matrix* const mtx) { + int nz; + nz = VP8EncQuantizeBlock(in + 0 * 16, out + 0 * 16, mtx) << 0; + nz |= VP8EncQuantizeBlock(in + 1 * 16, out + 1 * 16, mtx) << 1; + return nz; +} + +//------------------------------------------------------------------------------ +// Entry point + +extern void VP8EncDspInitMSA(void); + +WEBP_TSAN_IGNORE_FUNCTION void VP8EncDspInitMSA(void) { + VP8ITransform = ITransform; + VP8FTransform = FTransform; + VP8FTransformWHT = FTransformWHT; + + VP8TDisto4x4 = Disto4x4; + VP8TDisto16x16 = Disto16x16; + VP8CollectHistogram = CollectHistogram; + + VP8EncPredLuma4 = Intra4Preds; + VP8EncPredLuma16 = Intra16Preds; + VP8EncPredChroma8 = IntraChromaPreds; + + VP8SSE16x16 = SSE16x16; + VP8SSE16x8 = SSE16x8; + VP8SSE8x8 = SSE8x8; + VP8SSE4x4 = SSE4x4; + + VP8EncQuantizeBlock = QuantizeBlock; + VP8EncQuantize2Blocks = Quantize2Blocks; + VP8EncQuantizeBlockWHT = QuantizeBlock; +} + +#else // !WEBP_USE_MSA + +WEBP_DSP_INIT_STUB(VP8EncDspInitMSA) + +#endif // WEBP_USE_MSA diff --git a/thirdparty/libwebp/dsp/enc_neon.c b/thirdparty/libwebp/dsp/enc_neon.c index 46f6bf9a33..6a078d632d 100644 --- a/thirdparty/libwebp/dsp/enc_neon.c +++ b/thirdparty/libwebp/dsp/enc_neon.c @@ -18,7 +18,7 @@ #include #include "./neon.h" -#include "../enc/vp8enci.h" +#include "../enc/vp8i_enc.h" //------------------------------------------------------------------------------ // Transforms (Paragraph 14.4) @@ -746,9 +746,14 @@ static WEBP_INLINE void AccumulateSSE16(const uint8_t* const a, const uint8x16_t a0 = vld1q_u8(a); const uint8x16_t b0 = vld1q_u8(b); const uint8x16_t abs_diff = vabdq_u8(a0, b0); - uint16x8_t prod = vmull_u8(vget_low_u8(abs_diff), vget_low_u8(abs_diff)); - prod = vmlal_u8(prod, vget_high_u8(abs_diff), vget_high_u8(abs_diff)); - *sum = vpadalq_u16(*sum, prod); // pair-wise add and accumulate + const uint16x8_t prod1 = vmull_u8(vget_low_u8(abs_diff), + vget_low_u8(abs_diff)); + const uint16x8_t prod2 = vmull_u8(vget_high_u8(abs_diff), + vget_high_u8(abs_diff)); + /* pair-wise adds and widen */ + const uint32x4_t sum1 = vpaddlq_u16(prod1); + const uint32x4_t sum2 = vpaddlq_u16(prod2); + *sum = vaddq_u32(*sum, vaddq_u32(sum1, sum2)); } // Horizontal sum of all four uint32_t values in 'sum'. @@ -758,7 +763,7 @@ static int SumToInt(uint32x4_t sum) { return (int)sum3; } -static int SSE16x16(const uint8_t* a, const uint8_t* b) { +static int SSE16x16_NEON(const uint8_t* a, const uint8_t* b) { uint32x4_t sum = vdupq_n_u32(0); int y; for (y = 0; y < 16; ++y) { @@ -767,7 +772,7 @@ static int SSE16x16(const uint8_t* a, const uint8_t* b) { return SumToInt(sum); } -static int SSE16x8(const uint8_t* a, const uint8_t* b) { +static int SSE16x8_NEON(const uint8_t* a, const uint8_t* b) { uint32x4_t sum = vdupq_n_u32(0); int y; for (y = 0; y < 8; ++y) { @@ -776,7 +781,7 @@ static int SSE16x8(const uint8_t* a, const uint8_t* b) { return SumToInt(sum); } -static int SSE8x8(const uint8_t* a, const uint8_t* b) { +static int SSE8x8_NEON(const uint8_t* a, const uint8_t* b) { uint32x4_t sum = vdupq_n_u32(0); int y; for (y = 0; y < 8; ++y) { @@ -789,13 +794,18 @@ static int SSE8x8(const uint8_t* a, const uint8_t* b) { return SumToInt(sum); } -static int SSE4x4(const uint8_t* a, const uint8_t* b) { +static int SSE4x4_NEON(const uint8_t* a, const uint8_t* b) { const uint8x16_t a0 = Load4x4(a); const uint8x16_t b0 = Load4x4(b); const uint8x16_t abs_diff = vabdq_u8(a0, b0); - uint16x8_t prod = vmull_u8(vget_low_u8(abs_diff), vget_low_u8(abs_diff)); - prod = vmlal_u8(prod, vget_high_u8(abs_diff), vget_high_u8(abs_diff)); - return SumToInt(vpaddlq_u16(prod)); + const uint16x8_t prod1 = vmull_u8(vget_low_u8(abs_diff), + vget_low_u8(abs_diff)); + const uint16x8_t prod2 = vmull_u8(vget_high_u8(abs_diff), + vget_high_u8(abs_diff)); + /* pair-wise adds and widen */ + const uint32x4_t sum1 = vpaddlq_u16(prod1); + const uint32x4_t sum2 = vpaddlq_u16(prod2); + return SumToInt(vaddq_u32(sum1, sum2)); } //------------------------------------------------------------------------------ @@ -903,10 +913,12 @@ WEBP_TSAN_IGNORE_FUNCTION void VP8EncDspInitNEON(void) { VP8TDisto4x4 = Disto4x4; VP8TDisto16x16 = Disto16x16; VP8CollectHistogram = CollectHistogram; - VP8SSE16x16 = SSE16x16; - VP8SSE16x8 = SSE16x8; - VP8SSE8x8 = SSE8x8; - VP8SSE4x4 = SSE4x4; + + VP8SSE16x16 = SSE16x16_NEON; + VP8SSE16x8 = SSE16x8_NEON; + VP8SSE8x8 = SSE8x8_NEON; + VP8SSE4x4 = SSE4x4_NEON; + #if !defined(WORK_AROUND_GCC) VP8EncQuantizeBlock = QuantizeBlock; VP8EncQuantize2Blocks = Quantize2Blocks; diff --git a/thirdparty/libwebp/dsp/enc_sse2.c b/thirdparty/libwebp/dsp/enc_sse2.c index 4a2e3ce14f..2026a74c91 100644 --- a/thirdparty/libwebp/dsp/enc_sse2.c +++ b/thirdparty/libwebp/dsp/enc_sse2.c @@ -14,12 +14,13 @@ #include "./dsp.h" #if defined(WEBP_USE_SSE2) +#include #include // for abs() #include #include "./common_sse2.h" -#include "../enc/cost.h" -#include "../enc/vp8enci.h" +#include "../enc/cost_enc.h" +#include "../enc/vp8i_enc.h" //------------------------------------------------------------------------------ // Transforms (Paragraph 14.4) @@ -139,7 +140,7 @@ static void ITransform(const uint8_t* ref, const int16_t* in, uint8_t* dst, // Transpose the two 4x4. VP8Transpose_2_4x4_16b(&shifted0, &shifted1, &shifted2, &shifted3, &T0, &T1, - &T2, &T3); + &T2, &T3); } // Add inverse transform to 'ref' and store. @@ -250,25 +251,11 @@ static void FTransformPass2(const __m128i* const v01, const __m128i* const v32, const __m128i k51000 = _mm_set1_epi32(51000); // Same operations are done on the (0,3) and (1,2) pairs. - // a0 = v0 + v3 - // a1 = v1 + v2 // a3 = v0 - v3 // a2 = v1 - v2 - const __m128i a01 = _mm_add_epi16(*v01, *v32); const __m128i a32 = _mm_sub_epi16(*v01, *v32); - const __m128i a11 = _mm_unpackhi_epi64(a01, a01); const __m128i a22 = _mm_unpackhi_epi64(a32, a32); - const __m128i a01_plus_7 = _mm_add_epi16(a01, seven); - // d0 = (a0 + a1 + 7) >> 4; - // d2 = (a0 - a1 + 7) >> 4; - const __m128i c0 = _mm_add_epi16(a01_plus_7, a11); - const __m128i c2 = _mm_sub_epi16(a01_plus_7, a11); - const __m128i d0 = _mm_srai_epi16(c0, 4); - const __m128i d2 = _mm_srai_epi16(c2, 4); - - // f1 = ((b3 * 5352 + b2 * 2217 + 12000) >> 16) - // f3 = ((b3 * 2217 - b2 * 5352 + 51000) >> 16) const __m128i b23 = _mm_unpacklo_epi16(a22, a32); const __m128i c1 = _mm_madd_epi16(b23, k5352_2217); const __m128i c3 = _mm_madd_epi16(b23, k2217_5352); @@ -276,14 +263,28 @@ static void FTransformPass2(const __m128i* const v01, const __m128i* const v32, const __m128i d3 = _mm_add_epi32(c3, k51000); const __m128i e1 = _mm_srai_epi32(d1, 16); const __m128i e3 = _mm_srai_epi32(d3, 16); + // f1 = ((b3 * 5352 + b2 * 2217 + 12000) >> 16) + // f3 = ((b3 * 2217 - b2 * 5352 + 51000) >> 16) const __m128i f1 = _mm_packs_epi32(e1, e1); const __m128i f3 = _mm_packs_epi32(e3, e3); - // f1 = f1 + (a3 != 0); + // g1 = f1 + (a3 != 0); // The compare will return (0xffff, 0) for (==0, !=0). To turn that into the // desired (0, 1), we add one earlier through k12000_plus_one. - // -> f1 = f1 + 1 - (a3 == 0) + // -> g1 = f1 + 1 - (a3 == 0) const __m128i g1 = _mm_add_epi16(f1, _mm_cmpeq_epi16(a32, zero)); + // a0 = v0 + v3 + // a1 = v1 + v2 + const __m128i a01 = _mm_add_epi16(*v01, *v32); + const __m128i a01_plus_7 = _mm_add_epi16(a01, seven); + const __m128i a11 = _mm_unpackhi_epi64(a01, a01); + const __m128i c0 = _mm_add_epi16(a01_plus_7, a11); + const __m128i c2 = _mm_sub_epi16(a01_plus_7, a11); + // d0 = (a0 + a1 + 7) >> 4; + // d2 = (a0 - a1 + 7) >> 4; + const __m128i d0 = _mm_srai_epi16(c0, 4); + const __m128i d2 = _mm_srai_epi16(c2, 4); + const __m128i d0_g1 = _mm_unpacklo_epi64(d0, g1); const __m128i d2_f3 = _mm_unpacklo_epi64(d2, f3); _mm_storeu_si128((__m128i*)&out[0], d0_g1); @@ -1045,6 +1046,37 @@ static int SSE4x4(const uint8_t* a, const uint8_t* b) { return (tmp[3] + tmp[2] + tmp[1] + tmp[0]); } +//------------------------------------------------------------------------------ + +static void Mean16x4(const uint8_t* ref, uint32_t dc[4]) { + const __m128i mask = _mm_set1_epi16(0x00ff); + const __m128i a0 = _mm_loadu_si128((const __m128i*)&ref[BPS * 0]); + const __m128i a1 = _mm_loadu_si128((const __m128i*)&ref[BPS * 1]); + const __m128i a2 = _mm_loadu_si128((const __m128i*)&ref[BPS * 2]); + const __m128i a3 = _mm_loadu_si128((const __m128i*)&ref[BPS * 3]); + const __m128i b0 = _mm_srli_epi16(a0, 8); // hi byte + const __m128i b1 = _mm_srli_epi16(a1, 8); + const __m128i b2 = _mm_srli_epi16(a2, 8); + const __m128i b3 = _mm_srli_epi16(a3, 8); + const __m128i c0 = _mm_and_si128(a0, mask); // lo byte + const __m128i c1 = _mm_and_si128(a1, mask); + const __m128i c2 = _mm_and_si128(a2, mask); + const __m128i c3 = _mm_and_si128(a3, mask); + const __m128i d0 = _mm_add_epi32(b0, c0); + const __m128i d1 = _mm_add_epi32(b1, c1); + const __m128i d2 = _mm_add_epi32(b2, c2); + const __m128i d3 = _mm_add_epi32(b3, c3); + const __m128i e0 = _mm_add_epi32(d0, d1); + const __m128i e1 = _mm_add_epi32(d2, d3); + const __m128i f0 = _mm_add_epi32(e0, e1); + uint16_t tmp[8]; + _mm_storeu_si128((__m128i*)tmp, f0); + dc[0] = tmp[0] + tmp[1]; + dc[1] = tmp[2] + tmp[3]; + dc[2] = tmp[4] + tmp[5]; + dc[3] = tmp[6] + tmp[7]; +} + //------------------------------------------------------------------------------ // Texture distortion // @@ -1331,10 +1363,122 @@ WEBP_TSAN_IGNORE_FUNCTION void VP8EncDspInitSSE2(void) { VP8SSE4x4 = SSE4x4; VP8TDisto4x4 = Disto4x4; VP8TDisto16x16 = Disto16x16; + VP8Mean16x4 = Mean16x4; +} + +//------------------------------------------------------------------------------ +// SSIM / PSNR entry point (TODO(skal): move to its own file later) + +static uint32_t AccumulateSSE_SSE2(const uint8_t* src1, + const uint8_t* src2, int len) { + int i = 0; + uint32_t sse2 = 0; + if (len >= 16) { + const int limit = len - 32; + int32_t tmp[4]; + __m128i sum1; + __m128i sum = _mm_setzero_si128(); + __m128i a0 = _mm_loadu_si128((const __m128i*)&src1[i]); + __m128i b0 = _mm_loadu_si128((const __m128i*)&src2[i]); + i += 16; + while (i <= limit) { + const __m128i a1 = _mm_loadu_si128((const __m128i*)&src1[i]); + const __m128i b1 = _mm_loadu_si128((const __m128i*)&src2[i]); + __m128i sum2; + i += 16; + SubtractAndAccumulate(a0, b0, &sum1); + sum = _mm_add_epi32(sum, sum1); + a0 = _mm_loadu_si128((const __m128i*)&src1[i]); + b0 = _mm_loadu_si128((const __m128i*)&src2[i]); + i += 16; + SubtractAndAccumulate(a1, b1, &sum2); + sum = _mm_add_epi32(sum, sum2); + } + SubtractAndAccumulate(a0, b0, &sum1); + sum = _mm_add_epi32(sum, sum1); + _mm_storeu_si128((__m128i*)tmp, sum); + sse2 += (tmp[3] + tmp[2] + tmp[1] + tmp[0]); + } + + for (; i < len; ++i) { + const int32_t diff = src1[i] - src2[i]; + sse2 += diff * diff; + } + return sse2; +} + +static uint32_t HorizontalAdd16b(const __m128i* const m) { + uint16_t tmp[8]; + const __m128i a = _mm_srli_si128(*m, 8); + const __m128i b = _mm_add_epi16(*m, a); + _mm_storeu_si128((__m128i*)tmp, b); + return (uint32_t)tmp[3] + tmp[2] + tmp[1] + tmp[0]; +} + +static uint32_t HorizontalAdd32b(const __m128i* const m) { + const __m128i a = _mm_srli_si128(*m, 8); + const __m128i b = _mm_add_epi32(*m, a); + const __m128i c = _mm_add_epi32(b, _mm_srli_si128(b, 4)); + return (uint32_t)_mm_cvtsi128_si32(c); +} + +static const uint16_t kWeight[] = { 1, 2, 3, 4, 3, 2, 1, 0 }; + +#define ACCUMULATE_ROW(WEIGHT) do { \ + /* compute row weight (Wx * Wy) */ \ + const __m128i Wy = _mm_set1_epi16((WEIGHT)); \ + const __m128i W = _mm_mullo_epi16(Wx, Wy); \ + /* process 8 bytes at a time (7 bytes, actually) */ \ + const __m128i a0 = _mm_loadl_epi64((const __m128i*)src1); \ + const __m128i b0 = _mm_loadl_epi64((const __m128i*)src2); \ + /* convert to 16b and multiply by weight */ \ + const __m128i a1 = _mm_unpacklo_epi8(a0, zero); \ + const __m128i b1 = _mm_unpacklo_epi8(b0, zero); \ + const __m128i wa1 = _mm_mullo_epi16(a1, W); \ + const __m128i wb1 = _mm_mullo_epi16(b1, W); \ + /* accumulate */ \ + xm = _mm_add_epi16(xm, wa1); \ + ym = _mm_add_epi16(ym, wb1); \ + xxm = _mm_add_epi32(xxm, _mm_madd_epi16(a1, wa1)); \ + xym = _mm_add_epi32(xym, _mm_madd_epi16(a1, wb1)); \ + yym = _mm_add_epi32(yym, _mm_madd_epi16(b1, wb1)); \ + src1 += stride1; \ + src2 += stride2; \ +} while (0) + +static double SSIMGet_SSE2(const uint8_t* src1, int stride1, + const uint8_t* src2, int stride2) { + VP8DistoStats stats; + const __m128i zero = _mm_setzero_si128(); + __m128i xm = zero, ym = zero; // 16b accums + __m128i xxm = zero, yym = zero, xym = zero; // 32b accum + const __m128i Wx = _mm_loadu_si128((const __m128i*)kWeight); + assert(2 * VP8_SSIM_KERNEL + 1 == 7); + ACCUMULATE_ROW(1); + ACCUMULATE_ROW(2); + ACCUMULATE_ROW(3); + ACCUMULATE_ROW(4); + ACCUMULATE_ROW(3); + ACCUMULATE_ROW(2); + ACCUMULATE_ROW(1); + stats.xm = HorizontalAdd16b(&xm); + stats.ym = HorizontalAdd16b(&ym); + stats.xxm = HorizontalAdd32b(&xxm); + stats.xym = HorizontalAdd32b(&xym); + stats.yym = HorizontalAdd32b(&yym); + return VP8SSIMFromStats(&stats); +} + +extern void VP8SSIMDspInitSSE2(void); + +WEBP_TSAN_IGNORE_FUNCTION void VP8SSIMDspInitSSE2(void) { + VP8AccumulateSSE = AccumulateSSE_SSE2; + VP8SSIMGet = SSIMGet_SSE2; } #else // !WEBP_USE_SSE2 WEBP_DSP_INIT_STUB(VP8EncDspInitSSE2) +WEBP_DSP_INIT_STUB(VP8SSIMDspInitSSE2) #endif // WEBP_USE_SSE2 diff --git a/thirdparty/libwebp/dsp/enc_sse41.c b/thirdparty/libwebp/dsp/enc_sse41.c index a1783901a6..e32086d9fd 100644 --- a/thirdparty/libwebp/dsp/enc_sse41.c +++ b/thirdparty/libwebp/dsp/enc_sse41.c @@ -18,7 +18,7 @@ #include // for abs() #include "./common_sse2.h" -#include "../enc/vp8enci.h" +#include "../enc/vp8i_enc.h" //------------------------------------------------------------------------------ // Compute susceptibility based on DCT-coeff histograms. diff --git a/thirdparty/libwebp/dsp/filters.c b/thirdparty/libwebp/dsp/filters.c index 9f04faf0cb..65f34aad1f 100644 --- a/thirdparty/libwebp/dsp/filters.c +++ b/thirdparty/libwebp/dsp/filters.c @@ -227,6 +227,8 @@ WebPFilterFunc WebPFilters[WEBP_FILTER_LAST]; WebPUnfilterFunc WebPUnfilters[WEBP_FILTER_LAST]; extern void VP8FiltersInitMIPSdspR2(void); +extern void VP8FiltersInitMSA(void); +extern void VP8FiltersInitNEON(void); extern void VP8FiltersInitSSE2(void); static volatile VP8CPUInfo filters_last_cpuinfo_used = @@ -251,10 +253,20 @@ WEBP_TSAN_IGNORE_FUNCTION void VP8FiltersInit(void) { VP8FiltersInitSSE2(); } #endif +#if defined(WEBP_USE_NEON) + if (VP8GetCPUInfo(kNEON)) { + VP8FiltersInitNEON(); + } +#endif #if defined(WEBP_USE_MIPS_DSP_R2) if (VP8GetCPUInfo(kMIPSdspR2)) { VP8FiltersInitMIPSdspR2(); } +#endif +#if defined(WEBP_USE_MSA) + if (VP8GetCPUInfo(kMSA)) { + VP8FiltersInitMSA(); + } #endif } filters_last_cpuinfo_used = VP8GetCPUInfo; diff --git a/thirdparty/libwebp/dsp/filters_msa.c b/thirdparty/libwebp/dsp/filters_msa.c new file mode 100644 index 0000000000..4b8922d0bc --- /dev/null +++ b/thirdparty/libwebp/dsp/filters_msa.c @@ -0,0 +1,202 @@ +// Copyright 2016 Google Inc. All Rights Reserved. +// +// Use of this source code is governed by a BSD-style license +// that can be found in the COPYING file in the root of the source +// tree. An additional intellectual property rights grant can be found +// in the file PATENTS. All contributing project authors may +// be found in the AUTHORS file in the root of the source tree. +// ----------------------------------------------------------------------------- +// +// MSA variant of alpha filters +// +// Author: Prashant Patil (prashant.patil@imgtec.com) + +#include "./dsp.h" + +#if defined(WEBP_USE_MSA) + +#include "./msa_macro.h" + +#include + +static WEBP_INLINE void PredictLineInverse0(const uint8_t* src, + const uint8_t* pred, + uint8_t* dst, int length) { + v16u8 src0, pred0, dst0; + assert(length >= 0); + while (length >= 32) { + v16u8 src1, pred1, dst1; + LD_UB2(src, 16, src0, src1); + LD_UB2(pred, 16, pred0, pred1); + SUB2(src0, pred0, src1, pred1, dst0, dst1); + ST_UB2(dst0, dst1, dst, 16); + src += 32; + pred += 32; + dst += 32; + length -= 32; + } + if (length > 0) { + int i; + if (length >= 16) { + src0 = LD_UB(src); + pred0 = LD_UB(pred); + dst0 = src0 - pred0; + ST_UB(dst0, dst); + src += 16; + pred += 16; + dst += 16; + length -= 16; + } + for (i = 0; i < length; i++) { + dst[i] = src[i] - pred[i]; + } + } +} + +//------------------------------------------------------------------------------ +// Helpful macro. + +#define SANITY_CHECK(in, out) \ + assert(in != NULL); \ + assert(out != NULL); \ + assert(width > 0); \ + assert(height > 0); \ + assert(stride >= width); + +//------------------------------------------------------------------------------ +// Horrizontal filter + +static void HorizontalFilter(const uint8_t* data, int width, int height, + int stride, uint8_t* filtered_data) { + const uint8_t* preds = data; + const uint8_t* in = data; + uint8_t* out = filtered_data; + int row = 1; + SANITY_CHECK(in, out); + + // Leftmost pixel is the same as input for topmost scanline. + out[0] = in[0]; + PredictLineInverse0(in + 1, preds, out + 1, width - 1); + preds += stride; + in += stride; + out += stride; + // Filter line-by-line. + while (row < height) { + // Leftmost pixel is predicted from above. + PredictLineInverse0(in, preds - stride, out, 1); + PredictLineInverse0(in + 1, preds, out + 1, width - 1); + ++row; + preds += stride; + in += stride; + out += stride; + } +} + +//------------------------------------------------------------------------------ +// Gradient filter + +static WEBP_INLINE void PredictLineGradient(const uint8_t* pinput, + const uint8_t* ppred, + uint8_t* poutput, int stride, + int size) { + int w; + const v16i8 zero = { 0 }; + while (size >= 16) { + v16u8 pred0, dst0; + v8i16 a0, a1, b0, b1, c0, c1; + const v16u8 tmp0 = LD_UB(ppred - 1); + const v16u8 tmp1 = LD_UB(ppred - stride); + const v16u8 tmp2 = LD_UB(ppred - stride - 1); + const v16u8 src0 = LD_UB(pinput); + ILVRL_B2_SH(zero, tmp0, a0, a1); + ILVRL_B2_SH(zero, tmp1, b0, b1); + ILVRL_B2_SH(zero, tmp2, c0, c1); + ADD2(a0, b0, a1, b1, a0, a1); + SUB2(a0, c0, a1, c1, a0, a1); + CLIP_SH2_0_255(a0, a1); + pred0 = (v16u8)__msa_pckev_b((v16i8)a1, (v16i8)a0); + dst0 = src0 - pred0; + ST_UB(dst0, poutput); + ppred += 16; + pinput += 16; + poutput += 16; + size -= 16; + } + for (w = 0; w < size; ++w) { + const int pred = ppred[w - 1] + ppred[w - stride] - ppred[w - stride - 1]; + poutput[w] = pinput[w] - (pred < 0 ? 0 : pred > 255 ? 255 : pred); + } +} + + +static void GradientFilter(const uint8_t* data, int width, int height, + int stride, uint8_t* filtered_data) { + const uint8_t* in = data; + const uint8_t* preds = data; + uint8_t* out = filtered_data; + int row = 1; + SANITY_CHECK(in, out); + + // left prediction for top scan-line + out[0] = in[0]; + PredictLineInverse0(in + 1, preds, out + 1, width - 1); + preds += stride; + in += stride; + out += stride; + // Filter line-by-line. + while (row < height) { + out[0] = in[0] - preds[- stride]; + PredictLineGradient(preds + 1, in + 1, out + 1, stride, width - 1); + ++row; + preds += stride; + in += stride; + out += stride; + } +} + +//------------------------------------------------------------------------------ +// Vertical filter + +static void VerticalFilter(const uint8_t* data, int width, int height, + int stride, uint8_t* filtered_data) { + const uint8_t* in = data; + const uint8_t* preds = data; + uint8_t* out = filtered_data; + int row = 1; + SANITY_CHECK(in, out); + + // Very first top-left pixel is copied. + out[0] = in[0]; + // Rest of top scan-line is left-predicted. + PredictLineInverse0(in + 1, preds, out + 1, width - 1); + in += stride; + out += stride; + + // Filter line-by-line. + while (row < height) { + PredictLineInverse0(in, preds, out, width); + ++row; + preds += stride; + in += stride; + out += stride; + } +} + +#undef SANITY_CHECK + +//------------------------------------------------------------------------------ +// Entry point + +extern void VP8FiltersInitMSA(void); + +WEBP_TSAN_IGNORE_FUNCTION void VP8FiltersInitMSA(void) { + WebPFilters[WEBP_FILTER_HORIZONTAL] = HorizontalFilter; + WebPFilters[WEBP_FILTER_VERTICAL] = VerticalFilter; + WebPFilters[WEBP_FILTER_GRADIENT] = GradientFilter; +} + +#else // !WEBP_USE_MSA + +WEBP_DSP_INIT_STUB(VP8FiltersInitMSA) + +#endif // WEBP_USE_MSA diff --git a/thirdparty/libwebp/dsp/filters_neon.c b/thirdparty/libwebp/dsp/filters_neon.c new file mode 100644 index 0000000000..4d6e50cc76 --- /dev/null +++ b/thirdparty/libwebp/dsp/filters_neon.c @@ -0,0 +1,327 @@ +// Copyright 2017 Google Inc. All Rights Reserved. +// +// Use of this source code is governed by a BSD-style license +// that can be found in the COPYING file in the root of the source +// tree. An additional intellectual property rights grant can be found +// in the file PATENTS. All contributing project authors may +// be found in the AUTHORS file in the root of the source tree. +// ----------------------------------------------------------------------------- +// +// NEON variant of alpha filters +// +// Author: Skal (pascal.massimino@gmail.com) + +#include "./dsp.h" + +#if defined(WEBP_USE_NEON) + +#include +#include "./neon.h" + +//------------------------------------------------------------------------------ +// Helpful macros. + +# define SANITY_CHECK(in, out) \ + assert(in != NULL); \ + assert(out != NULL); \ + assert(width > 0); \ + assert(height > 0); \ + assert(stride >= width); \ + assert(row >= 0 && num_rows > 0 && row + num_rows <= height); \ + (void)height; // Silence unused warning. + +// load eight u8 and widen to s16 +#define U8_TO_S16(A) vreinterpretq_s16_u16(vmovl_u8(A)) +#define LOAD_U8_TO_S16(A) U8_TO_S16(vld1_u8(A)) + +// shift left or right by N byte, inserting zeros +#define SHIFT_RIGHT_N_Q(A, N) vextq_u8((A), zero, (N)) +#define SHIFT_LEFT_N_Q(A, N) vextq_u8(zero, (A), (16 - (N)) % 16) + +// rotate left by N bytes +#define ROTATE_LEFT_N(A, N) vext_u8((A), (A), (N)) +// rotate right by N bytes +#define ROTATE_RIGHT_N(A, N) vext_u8((A), (A), (8 - (N)) % 8) + +static void PredictLine_NEON(const uint8_t* src, const uint8_t* pred, + uint8_t* dst, int length) { + int i; + assert(length >= 0); + for (i = 0; i + 16 <= length; i += 16) { + const uint8x16_t A = vld1q_u8(&src[i]); + const uint8x16_t B = vld1q_u8(&pred[i]); + const uint8x16_t C = vsubq_u8(A, B); + vst1q_u8(&dst[i], C); + } + for (; i < length; ++i) dst[i] = src[i] - pred[i]; +} + +// Special case for left-based prediction (when preds==dst-1 or preds==src-1). +static void PredictLineLeft_NEON(const uint8_t* src, uint8_t* dst, int length) { + PredictLine_NEON(src, src - 1, dst, length); +} + +//------------------------------------------------------------------------------ +// Horizontal filter. + +static WEBP_INLINE void DoHorizontalFilter_NEON(const uint8_t* in, + int width, int height, + int stride, + int row, int num_rows, + uint8_t* out) { + const size_t start_offset = row * stride; + const int last_row = row + num_rows; + SANITY_CHECK(in, out); + in += start_offset; + out += start_offset; + + if (row == 0) { + // Leftmost pixel is the same as input for topmost scanline. + out[0] = in[0]; + PredictLineLeft_NEON(in + 1, out + 1, width - 1); + row = 1; + in += stride; + out += stride; + } + + // Filter line-by-line. + while (row < last_row) { + // Leftmost pixel is predicted from above. + out[0] = in[0] - in[-stride]; + PredictLineLeft_NEON(in + 1, out + 1, width - 1); + ++row; + in += stride; + out += stride; + } +} + +static void HorizontalFilter_NEON(const uint8_t* data, int width, int height, + int stride, uint8_t* filtered_data) { + DoHorizontalFilter_NEON(data, width, height, stride, 0, height, + filtered_data); +} + +//------------------------------------------------------------------------------ +// Vertical filter. + +static WEBP_INLINE void DoVerticalFilter_NEON(const uint8_t* in, + int width, int height, int stride, + int row, int num_rows, + uint8_t* out) { + const size_t start_offset = row * stride; + const int last_row = row + num_rows; + SANITY_CHECK(in, out); + in += start_offset; + out += start_offset; + + if (row == 0) { + // Very first top-left pixel is copied. + out[0] = in[0]; + // Rest of top scan-line is left-predicted. + PredictLineLeft_NEON(in + 1, out + 1, width - 1); + row = 1; + in += stride; + out += stride; + } + + // Filter line-by-line. + while (row < last_row) { + PredictLine_NEON(in, in - stride, out, width); + ++row; + in += stride; + out += stride; + } +} + +static void VerticalFilter_NEON(const uint8_t* data, int width, int height, + int stride, uint8_t* filtered_data) { + DoVerticalFilter_NEON(data, width, height, stride, 0, height, + filtered_data); +} + +//------------------------------------------------------------------------------ +// Gradient filter. + +static WEBP_INLINE int GradientPredictor_C(uint8_t a, uint8_t b, uint8_t c) { + const int g = a + b - c; + return ((g & ~0xff) == 0) ? g : (g < 0) ? 0 : 255; // clip to 8bit +} + +static void GradientPredictDirect_NEON(const uint8_t* const row, + const uint8_t* const top, + uint8_t* const out, int length) { + int i; + for (i = 0; i + 8 <= length; i += 8) { + const uint8x8_t A = vld1_u8(&row[i - 1]); + const uint8x8_t B = vld1_u8(&top[i + 0]); + const int16x8_t C = vreinterpretq_s16_u16(vaddl_u8(A, B)); + const int16x8_t D = LOAD_U8_TO_S16(&top[i - 1]); + const uint8x8_t E = vqmovun_s16(vsubq_s16(C, D)); + const uint8x8_t F = vld1_u8(&row[i + 0]); + vst1_u8(&out[i], vsub_u8(F, E)); + } + for (; i < length; ++i) { + out[i] = row[i] - GradientPredictor_C(row[i - 1], top[i], top[i - 1]); + } +} + +static WEBP_INLINE void DoGradientFilter_NEON(const uint8_t* in, + int width, int height, + int stride, + int row, int num_rows, + uint8_t* out) { + const size_t start_offset = row * stride; + const int last_row = row + num_rows; + SANITY_CHECK(in, out); + in += start_offset; + out += start_offset; + + // left prediction for top scan-line + if (row == 0) { + out[0] = in[0]; + PredictLineLeft_NEON(in + 1, out + 1, width - 1); + row = 1; + in += stride; + out += stride; + } + + // Filter line-by-line. + while (row < last_row) { + out[0] = in[0] - in[-stride]; + GradientPredictDirect_NEON(in + 1, in + 1 - stride, out + 1, width - 1); + ++row; + in += stride; + out += stride; + } +} + +static void GradientFilter_NEON(const uint8_t* data, int width, int height, + int stride, uint8_t* filtered_data) { + DoGradientFilter_NEON(data, width, height, stride, 0, height, + filtered_data); +} + +#undef SANITY_CHECK + +//------------------------------------------------------------------------------ +// Inverse transforms + +static void HorizontalUnfilter_NEON(const uint8_t* prev, const uint8_t* in, + uint8_t* out, int width) { + int i; + const uint8x16_t zero = vdupq_n_u8(0); + uint8x16_t last; + out[0] = in[0] + (prev == NULL ? 0 : prev[0]); + if (width <= 1) return; + last = vsetq_lane_u8(out[0], zero, 0); + for (i = 1; i + 16 <= width; i += 16) { + const uint8x16_t A0 = vld1q_u8(&in[i]); + const uint8x16_t A1 = vaddq_u8(A0, last); + const uint8x16_t A2 = SHIFT_LEFT_N_Q(A1, 1); + const uint8x16_t A3 = vaddq_u8(A1, A2); + const uint8x16_t A4 = SHIFT_LEFT_N_Q(A3, 2); + const uint8x16_t A5 = vaddq_u8(A3, A4); + const uint8x16_t A6 = SHIFT_LEFT_N_Q(A5, 4); + const uint8x16_t A7 = vaddq_u8(A5, A6); + const uint8x16_t A8 = SHIFT_LEFT_N_Q(A7, 8); + const uint8x16_t A9 = vaddq_u8(A7, A8); + vst1q_u8(&out[i], A9); + last = SHIFT_RIGHT_N_Q(A9, 15); + } + for (; i < width; ++i) out[i] = in[i] + out[i - 1]; +} + +static void VerticalUnfilter_NEON(const uint8_t* prev, const uint8_t* in, + uint8_t* out, int width) { + if (prev == NULL) { + HorizontalUnfilter_NEON(NULL, in, out, width); + } else { + int i; + assert(width >= 0); + for (i = 0; i + 16 <= width; i += 16) { + const uint8x16_t A = vld1q_u8(&in[i]); + const uint8x16_t B = vld1q_u8(&prev[i]); + const uint8x16_t C = vaddq_u8(A, B); + vst1q_u8(&out[i], C); + } + for (; i < width; ++i) out[i] = in[i] + prev[i]; + } +} + +// GradientUnfilter_NEON is correct but slower than the C-version, +// at least on ARM64. For armv7, it's a wash. +// So best is to disable it for now, but keep the idea around... +// #define USE_GRADIENT_UNFILTER + +#if defined(USE_GRADIENT_UNFILTER) +#define GRAD_PROCESS_LANE(L) do { \ + const uint8x8_t tmp1 = ROTATE_RIGHT_N(pred, 1); /* rotate predictor in */ \ + const int16x8_t tmp2 = vaddq_s16(BC, U8_TO_S16(tmp1)); \ + const uint8x8_t delta = vqmovun_s16(tmp2); \ + pred = vadd_u8(D, delta); \ + out = vext_u8(out, ROTATE_LEFT_N(pred, (L)), 1); \ +} while (0) + +static void GradientPredictInverse_NEON(const uint8_t* const in, + const uint8_t* const top, + uint8_t* const row, int length) { + if (length > 0) { + int i; + uint8x8_t pred = vdup_n_u8(row[-1]); // left sample + uint8x8_t out = vdup_n_u8(0); + for (i = 0; i + 8 <= length; i += 8) { + const int16x8_t B = LOAD_U8_TO_S16(&top[i + 0]); + const int16x8_t C = LOAD_U8_TO_S16(&top[i - 1]); + const int16x8_t BC = vsubq_s16(B, C); // unclipped gradient basis B - C + const uint8x8_t D = vld1_u8(&in[i]); // base input + GRAD_PROCESS_LANE(0); + GRAD_PROCESS_LANE(1); + GRAD_PROCESS_LANE(2); + GRAD_PROCESS_LANE(3); + GRAD_PROCESS_LANE(4); + GRAD_PROCESS_LANE(5); + GRAD_PROCESS_LANE(6); + GRAD_PROCESS_LANE(7); + vst1_u8(&row[i], out); + } + for (; i < length; ++i) { + row[i] = in[i] + GradientPredictor_C(row[i - 1], top[i], top[i - 1]); + } + } +} +#undef GRAD_PROCESS_LANE + +static void GradientUnfilter_NEON(const uint8_t* prev, const uint8_t* in, + uint8_t* out, int width) { + if (prev == NULL) { + HorizontalUnfilter_NEON(NULL, in, out, width); + } else { + out[0] = in[0] + prev[0]; // predict from above + GradientPredictInverse_NEON(in + 1, prev + 1, out + 1, width - 1); + } +} + +#endif // USE_GRADIENT_UNFILTER + +//------------------------------------------------------------------------------ +// Entry point + +extern void VP8FiltersInitNEON(void); + +WEBP_TSAN_IGNORE_FUNCTION void VP8FiltersInitNEON(void) { + WebPUnfilters[WEBP_FILTER_HORIZONTAL] = HorizontalUnfilter_NEON; + WebPUnfilters[WEBP_FILTER_VERTICAL] = VerticalUnfilter_NEON; +#if defined(USE_GRADIENT_UNFILTER) + WebPUnfilters[WEBP_FILTER_GRADIENT] = GradientUnfilter_NEON; +#endif + + WebPFilters[WEBP_FILTER_HORIZONTAL] = HorizontalFilter_NEON; + WebPFilters[WEBP_FILTER_VERTICAL] = VerticalFilter_NEON; + WebPFilters[WEBP_FILTER_GRADIENT] = GradientFilter_NEON; +} + +#else // !WEBP_USE_NEON + +WEBP_DSP_INIT_STUB(VP8FiltersInitNEON) + +#endif // WEBP_USE_NEON diff --git a/thirdparty/libwebp/dsp/lossless.c b/thirdparty/libwebp/dsp/lossless.c index af913efccb..20d18f6ecd 100644 --- a/thirdparty/libwebp/dsp/lossless.c +++ b/thirdparty/libwebp/dsp/lossless.c @@ -17,20 +17,16 @@ #include #include -#include "../dec/vp8li.h" -#include "../utils/endian_inl.h" +#include "../dec/vp8li_dec.h" +#include "../utils/endian_inl_utils.h" #include "./lossless.h" +#include "./lossless_common.h" #define MAX_DIFF_COST (1e30f) //------------------------------------------------------------------------------ // Image transforms. -// In-place sum of each component with mod 256. -static WEBP_INLINE void AddPixelsEq(uint32_t* a, uint32_t b) { - *a = VP8LAddPixels(*a, b); -} - static WEBP_INLINE uint32_t Average2(uint32_t a0, uint32_t a1) { return (((a0 ^ a1) & 0xfefefefeu) >> 1) + (a0 & a1); } @@ -171,21 +167,41 @@ static uint32_t Predictor13(uint32_t left, const uint32_t* const top) { return pred; } +GENERATE_PREDICTOR_ADD(Predictor0, PredictorAdd0) +static void PredictorAdd1(const uint32_t* in, const uint32_t* upper, + int num_pixels, uint32_t* out) { + int i; + uint32_t left = out[-1]; + for (i = 0; i < num_pixels; ++i) { + out[i] = left = VP8LAddPixels(in[i], left); + } + (void)upper; +} +GENERATE_PREDICTOR_ADD(Predictor2, PredictorAdd2) +GENERATE_PREDICTOR_ADD(Predictor3, PredictorAdd3) +GENERATE_PREDICTOR_ADD(Predictor4, PredictorAdd4) +GENERATE_PREDICTOR_ADD(Predictor5, PredictorAdd5) +GENERATE_PREDICTOR_ADD(Predictor6, PredictorAdd6) +GENERATE_PREDICTOR_ADD(Predictor7, PredictorAdd7) +GENERATE_PREDICTOR_ADD(Predictor8, PredictorAdd8) +GENERATE_PREDICTOR_ADD(Predictor9, PredictorAdd9) +GENERATE_PREDICTOR_ADD(Predictor10, PredictorAdd10) +GENERATE_PREDICTOR_ADD(Predictor11, PredictorAdd11) +GENERATE_PREDICTOR_ADD(Predictor12, PredictorAdd12) +GENERATE_PREDICTOR_ADD(Predictor13, PredictorAdd13) + //------------------------------------------------------------------------------ // Inverse prediction. static void PredictorInverseTransform(const VP8LTransform* const transform, - int y_start, int y_end, uint32_t* data) { + int y_start, int y_end, + const uint32_t* in, uint32_t* out) { const int width = transform->xsize_; if (y_start == 0) { // First Row follows the L (mode=1) mode. - int x; - const uint32_t pred0 = Predictor0(data[-1], NULL); - AddPixelsEq(data, pred0); - for (x = 1; x < width; ++x) { - const uint32_t pred1 = Predictor1(data[x - 1], NULL); - AddPixelsEq(data + x, pred1); - } - data += width; + PredictorAdd0(in, NULL, 1, out); + PredictorAdd1(in + 1, NULL, width - 1, out + 1); + in += width; + out += width; ++y_start; } @@ -193,36 +209,26 @@ static void PredictorInverseTransform(const VP8LTransform* const transform, int y = y_start; const int tile_width = 1 << transform->bits_; const int mask = tile_width - 1; - const int safe_width = width & ~mask; const int tiles_per_row = VP8LSubSampleSize(width, transform->bits_); const uint32_t* pred_mode_base = transform->data_ + (y >> transform->bits_) * tiles_per_row; while (y < y_end) { - const uint32_t pred2 = Predictor2(data[-1], data - width); const uint32_t* pred_mode_src = pred_mode_base; - VP8LPredictorFunc pred_func; int x = 1; - int t = 1; // First pixel follows the T (mode=2) mode. - AddPixelsEq(data, pred2); + PredictorAdd2(in, out - width, 1, out); // .. the rest: - while (x < safe_width) { - pred_func = VP8LPredictors[((*pred_mode_src++) >> 8) & 0xf]; - for (; t < tile_width; ++t, ++x) { - const uint32_t pred = pred_func(data[x - 1], data + x - width); - AddPixelsEq(data + x, pred); - } - t = 0; - } - if (x < width) { - pred_func = VP8LPredictors[((*pred_mode_src++) >> 8) & 0xf]; - for (; x < width; ++x) { - const uint32_t pred = pred_func(data[x - 1], data + x - width); - AddPixelsEq(data + x, pred); - } + while (x < width) { + const VP8LPredictorAddSubFunc pred_func = + VP8LPredictorsAdd[((*pred_mode_src++) >> 8) & 0xf]; + int x_end = (x & ~mask) + tile_width; + if (x_end > width) x_end = width; + pred_func(in + x, out + x - width, x_end - x, out + x); + x = x_end; } - data += width; + in += width; + out += width; ++y; if ((y & mask) == 0) { // Use the same mask, since tiles are squares. pred_mode_base += tiles_per_row; @@ -233,21 +239,22 @@ static void PredictorInverseTransform(const VP8LTransform* const transform, // Add green to blue and red channels (i.e. perform the inverse transform of // 'subtract green'). -void VP8LAddGreenToBlueAndRed_C(uint32_t* data, int num_pixels) { +void VP8LAddGreenToBlueAndRed_C(const uint32_t* src, int num_pixels, + uint32_t* dst) { int i; for (i = 0; i < num_pixels; ++i) { - const uint32_t argb = data[i]; + const uint32_t argb = src[i]; const uint32_t green = ((argb >> 8) & 0xff); uint32_t red_blue = (argb & 0x00ff00ffu); red_blue += (green << 16) | green; red_blue &= 0x00ff00ffu; - data[i] = (argb & 0xff00ff00u) | red_blue; + dst[i] = (argb & 0xff00ff00u) | red_blue; } } -static WEBP_INLINE uint32_t ColorTransformDelta(int8_t color_pred, - int8_t color) { - return (uint32_t)((int)(color_pred) * color) >> 5; +static WEBP_INLINE int ColorTransformDelta(int8_t color_pred, + int8_t color) { + return ((int)color_pred * color) >> 5; } static WEBP_INLINE void ColorCodeToMultipliers(uint32_t color_code, @@ -257,27 +264,29 @@ static WEBP_INLINE void ColorCodeToMultipliers(uint32_t color_code, m->red_to_blue_ = (color_code >> 16) & 0xff; } -void VP8LTransformColorInverse_C(const VP8LMultipliers* const m, uint32_t* data, - int num_pixels) { +void VP8LTransformColorInverse_C(const VP8LMultipliers* const m, + const uint32_t* src, int num_pixels, + uint32_t* dst) { int i; for (i = 0; i < num_pixels; ++i) { - const uint32_t argb = data[i]; + const uint32_t argb = src[i]; const uint32_t green = argb >> 8; const uint32_t red = argb >> 16; - uint32_t new_red = red; - uint32_t new_blue = argb; + int new_red = red; + int new_blue = argb; new_red += ColorTransformDelta(m->green_to_red_, green); new_red &= 0xff; new_blue += ColorTransformDelta(m->green_to_blue_, green); new_blue += ColorTransformDelta(m->red_to_blue_, new_red); new_blue &= 0xff; - data[i] = (argb & 0xff00ff00u) | (new_red << 16) | (new_blue); + dst[i] = (argb & 0xff00ff00u) | (new_red << 16) | (new_blue); } } // Color space inverse transform. static void ColorSpaceInverseTransform(const VP8LTransform* const transform, - int y_start, int y_end, uint32_t* data) { + int y_start, int y_end, + const uint32_t* src, uint32_t* dst) { const int width = transform->xsize_; const int tile_width = 1 << transform->bits_; const int mask = tile_width - 1; @@ -291,17 +300,19 @@ static void ColorSpaceInverseTransform(const VP8LTransform* const transform, while (y < y_end) { const uint32_t* pred = pred_row; VP8LMultipliers m = { 0, 0, 0 }; - const uint32_t* const data_safe_end = data + safe_width; - const uint32_t* const data_end = data + width; - while (data < data_safe_end) { + const uint32_t* const src_safe_end = src + safe_width; + const uint32_t* const src_end = src + width; + while (src < src_safe_end) { ColorCodeToMultipliers(*pred++, &m); - VP8LTransformColorInverse(&m, data, tile_width); - data += tile_width; + VP8LTransformColorInverse(&m, src, tile_width, dst); + src += tile_width; + dst += tile_width; } - if (data < data_end) { // Left-overs using C-version. + if (src < src_end) { // Left-overs using C-version. ColorCodeToMultipliers(*pred++, &m); - VP8LTransformColorInverse(&m, data, remaining_width); - data += remaining_width; + VP8LTransformColorInverse(&m, src, remaining_width, dst); + src += remaining_width; + dst += remaining_width; } ++y; if ((y & mask) == 0) pred_row += tiles_per_row; @@ -366,10 +377,10 @@ void VP8LInverseTransform(const VP8LTransform* const transform, assert(row_end <= transform->ysize_); switch (transform->type_) { case SUBTRACT_GREEN: - VP8LAddGreenToBlueAndRed(out, (row_end - row_start) * width); + VP8LAddGreenToBlueAndRed(in, (row_end - row_start) * width, out); break; case PREDICTOR_TRANSFORM: - PredictorInverseTransform(transform, row_start, row_end, out); + PredictorInverseTransform(transform, row_start, row_end, in, out); if (row_end != transform->ysize_) { // The last predicted row in this iteration will be the top-pred row // for the first row in next iteration. @@ -378,7 +389,7 @@ void VP8LInverseTransform(const VP8LTransform* const transform, } break; case CROSS_COLOR_TRANSFORM: - ColorSpaceInverseTransform(transform, row_start, row_end, out); + ColorSpaceInverseTransform(transform, row_start, row_end, in, out); break; case COLOR_INDEXING_TRANSFORM: if (in == out && transform->bits_ > 0) { @@ -555,10 +566,15 @@ void VP8LConvertFromBGRA(const uint32_t* const in_data, int num_pixels, //------------------------------------------------------------------------------ -VP8LProcessBlueAndRedFunc VP8LAddGreenToBlueAndRed; +VP8LProcessDecBlueAndRedFunc VP8LAddGreenToBlueAndRed; +VP8LPredictorAddSubFunc VP8LPredictorsAdd[16]; VP8LPredictorFunc VP8LPredictors[16]; -VP8LTransformColorFunc VP8LTransformColorInverse; +// exposed plain-C implementations +VP8LPredictorAddSubFunc VP8LPredictorsAdd_C[16]; +VP8LPredictorFunc VP8LPredictors_C[16]; + +VP8LTransformColorInverseFunc VP8LTransformColorInverse; VP8LConvertFunc VP8LConvertBGRAToRGB; VP8LConvertFunc VP8LConvertBGRAToRGBA; @@ -572,29 +588,37 @@ VP8LMapAlphaFunc VP8LMapColor8b; extern void VP8LDspInitSSE2(void); extern void VP8LDspInitNEON(void); extern void VP8LDspInitMIPSdspR2(void); +extern void VP8LDspInitMSA(void); static volatile VP8CPUInfo lossless_last_cpuinfo_used = (VP8CPUInfo)&lossless_last_cpuinfo_used; +#define COPY_PREDICTOR_ARRAY(IN, OUT) do { \ + (OUT)[0] = IN##0; \ + (OUT)[1] = IN##1; \ + (OUT)[2] = IN##2; \ + (OUT)[3] = IN##3; \ + (OUT)[4] = IN##4; \ + (OUT)[5] = IN##5; \ + (OUT)[6] = IN##6; \ + (OUT)[7] = IN##7; \ + (OUT)[8] = IN##8; \ + (OUT)[9] = IN##9; \ + (OUT)[10] = IN##10; \ + (OUT)[11] = IN##11; \ + (OUT)[12] = IN##12; \ + (OUT)[13] = IN##13; \ + (OUT)[14] = IN##0; /* <- padding security sentinels*/ \ + (OUT)[15] = IN##0; \ +} while (0); + WEBP_TSAN_IGNORE_FUNCTION void VP8LDspInit(void) { if (lossless_last_cpuinfo_used == VP8GetCPUInfo) return; - VP8LPredictors[0] = Predictor0; - VP8LPredictors[1] = Predictor1; - VP8LPredictors[2] = Predictor2; - VP8LPredictors[3] = Predictor3; - VP8LPredictors[4] = Predictor4; - VP8LPredictors[5] = Predictor5; - VP8LPredictors[6] = Predictor6; - VP8LPredictors[7] = Predictor7; - VP8LPredictors[8] = Predictor8; - VP8LPredictors[9] = Predictor9; - VP8LPredictors[10] = Predictor10; - VP8LPredictors[11] = Predictor11; - VP8LPredictors[12] = Predictor12; - VP8LPredictors[13] = Predictor13; - VP8LPredictors[14] = Predictor0; // <- padding security sentinels - VP8LPredictors[15] = Predictor0; + COPY_PREDICTOR_ARRAY(Predictor, VP8LPredictors) + COPY_PREDICTOR_ARRAY(Predictor, VP8LPredictors_C) + COPY_PREDICTOR_ARRAY(PredictorAdd, VP8LPredictorsAdd) + COPY_PREDICTOR_ARRAY(PredictorAdd, VP8LPredictorsAdd_C) VP8LAddGreenToBlueAndRed = VP8LAddGreenToBlueAndRed_C; @@ -625,9 +649,15 @@ WEBP_TSAN_IGNORE_FUNCTION void VP8LDspInit(void) { if (VP8GetCPUInfo(kMIPSdspR2)) { VP8LDspInitMIPSdspR2(); } +#endif +#if defined(WEBP_USE_MSA) + if (VP8GetCPUInfo(kMSA)) { + VP8LDspInitMSA(); + } #endif } lossless_last_cpuinfo_used = VP8GetCPUInfo; } +#undef COPY_PREDICTOR_ARRAY //------------------------------------------------------------------------------ diff --git a/thirdparty/libwebp/dsp/lossless.h b/thirdparty/libwebp/dsp/lossless.h index 9f0d7a25b7..352a54e509 100644 --- a/thirdparty/libwebp/dsp/lossless.h +++ b/thirdparty/libwebp/dsp/lossless.h @@ -18,7 +18,7 @@ #include "../webp/types.h" #include "../webp/decode.h" -#include "../enc/histogram.h" +#include "../enc/histogram_enc.h" #include "../utils/utils.h" #ifdef __cplusplus @@ -26,7 +26,7 @@ extern "C" { #endif #ifdef WEBP_EXPERIMENTAL_FEATURES -#include "../enc/delta_palettization.h" +#include "../enc/delta_palettization_enc.h" #endif // WEBP_EXPERIMENTAL_FEATURES //------------------------------------------------------------------------------ @@ -34,9 +34,17 @@ extern "C" { typedef uint32_t (*VP8LPredictorFunc)(uint32_t left, const uint32_t* const top); extern VP8LPredictorFunc VP8LPredictors[16]; - -typedef void (*VP8LProcessBlueAndRedFunc)(uint32_t* argb_data, int num_pixels); -extern VP8LProcessBlueAndRedFunc VP8LAddGreenToBlueAndRed; +extern VP8LPredictorFunc VP8LPredictors_C[16]; +// These Add/Sub function expects upper[-1] and out[-1] to be readable. +typedef void (*VP8LPredictorAddSubFunc)(const uint32_t* in, + const uint32_t* upper, int num_pixels, + uint32_t* out); +extern VP8LPredictorAddSubFunc VP8LPredictorsAdd[16]; +extern VP8LPredictorAddSubFunc VP8LPredictorsAdd_C[16]; + +typedef void (*VP8LProcessDecBlueAndRedFunc)(const uint32_t* src, + int num_pixels, uint32_t* dst); +extern VP8LProcessDecBlueAndRedFunc VP8LAddGreenToBlueAndRed; typedef struct { // Note: the members are uint8_t, so that any negative values are @@ -45,9 +53,10 @@ typedef struct { uint8_t green_to_blue_; uint8_t red_to_blue_; } VP8LMultipliers; -typedef void (*VP8LTransformColorFunc)(const VP8LMultipliers* const m, - uint32_t* argb_data, int num_pixels); -extern VP8LTransformColorFunc VP8LTransformColorInverse; +typedef void (*VP8LTransformColorInverseFunc)(const VP8LMultipliers* const m, + const uint32_t* src, + int num_pixels, uint32_t* dst); +extern VP8LTransformColorInverseFunc VP8LTransformColorInverse; struct VP8LTransform; // Defined in dec/vp8li.h. @@ -72,23 +81,6 @@ extern VP8LConvertFunc VP8LConvertBGRAToBGR; void VP8LConvertFromBGRA(const uint32_t* const in_data, int num_pixels, WEBP_CSP_MODE out_colorspace, uint8_t* const rgba); -// color mapping related functions. -static WEBP_INLINE uint32_t VP8GetARGBIndex(uint32_t idx) { - return (idx >> 8) & 0xff; -} - -static WEBP_INLINE uint8_t VP8GetAlphaIndex(uint8_t idx) { - return idx; -} - -static WEBP_INLINE uint32_t VP8GetARGBValue(uint32_t val) { - return val; -} - -static WEBP_INLINE uint8_t VP8GetAlphaValue(uint32_t val) { - return (val >> 8) & 0xff; -} - typedef void (*VP8LMapARGBFunc)(const uint32_t* src, const uint32_t* const color_map, uint32_t* dst, int y_start, @@ -110,7 +102,8 @@ void VP8LColorIndexInverseTransformAlpha( // Expose some C-only fallback functions void VP8LTransformColorInverse_C(const VP8LMultipliers* const m, - uint32_t* data, int num_pixels); + const uint32_t* src, int num_pixels, + uint32_t* dst); void VP8LConvertBGRAToRGB_C(const uint32_t* src, int num_pixels, uint8_t* dst); void VP8LConvertBGRAToRGBA_C(const uint32_t* src, int num_pixels, uint8_t* dst); @@ -119,7 +112,8 @@ void VP8LConvertBGRAToRGBA4444_C(const uint32_t* src, void VP8LConvertBGRAToRGB565_C(const uint32_t* src, int num_pixels, uint8_t* dst); void VP8LConvertBGRAToBGR_C(const uint32_t* src, int num_pixels, uint8_t* dst); -void VP8LAddGreenToBlueAndRed_C(uint32_t* data, int num_pixels); +void VP8LAddGreenToBlueAndRed_C(const uint32_t* src, int num_pixels, + uint32_t* dst); // Must be called before calling any of the above methods. void VP8LDspInit(void); @@ -127,7 +121,10 @@ void VP8LDspInit(void); //------------------------------------------------------------------------------ // Encoding -extern VP8LProcessBlueAndRedFunc VP8LSubtractGreenFromBlueAndRed; +typedef void (*VP8LProcessEncBlueAndRedFunc)(uint32_t* dst, int num_pixels); +extern VP8LProcessEncBlueAndRedFunc VP8LSubtractGreenFromBlueAndRed; +typedef void (*VP8LTransformColorFunc)(const VP8LMultipliers* const m, + uint32_t* const dst, int num_pixels); extern VP8LTransformColorFunc VP8LTransformColor; typedef void (*VP8LCollectColorBlueTransformsFunc)( const uint32_t* argb, int stride, @@ -153,62 +150,8 @@ void VP8LCollectColorBlueTransforms_C(const uint32_t* argb, int stride, int green_to_blue, int red_to_blue, int histo[]); -//------------------------------------------------------------------------------ -// Image transforms. - -void VP8LResidualImage(int width, int height, int bits, int low_effort, - uint32_t* const argb, uint32_t* const argb_scratch, - uint32_t* const image, int near_lossless, int exact, - int used_subtract_green); - -void VP8LColorSpaceTransform(int width, int height, int bits, int quality, - uint32_t* const argb, uint32_t* image); - -//------------------------------------------------------------------------------ -// Misc methods. - -// Computes sampled size of 'size' when sampling using 'sampling bits'. -static WEBP_INLINE uint32_t VP8LSubSampleSize(uint32_t size, - uint32_t sampling_bits) { - return (size + (1 << sampling_bits) - 1) >> sampling_bits; -} - -// Converts near lossless quality into max number of bits shaved off. -static WEBP_INLINE int VP8LNearLosslessBits(int near_lossless_quality) { - // 100 -> 0 - // 80..99 -> 1 - // 60..79 -> 2 - // 40..59 -> 3 - // 20..39 -> 4 - // 0..19 -> 5 - return 5 - near_lossless_quality / 20; -} - -// ----------------------------------------------------------------------------- -// Faster logarithm for integers. Small values use a look-up table. - -// The threshold till approximate version of log_2 can be used. -// Practically, we can get rid of the call to log() as the two values match to -// very high degree (the ratio of these two is 0.99999x). -// Keeping a high threshold for now. -#define APPROX_LOG_WITH_CORRECTION_MAX 65536 -#define APPROX_LOG_MAX 4096 -#define LOG_2_RECIPROCAL 1.44269504088896338700465094007086 -#define LOG_LOOKUP_IDX_MAX 256 -extern const float kLog2Table[LOG_LOOKUP_IDX_MAX]; -extern const float kSLog2Table[LOG_LOOKUP_IDX_MAX]; -typedef float (*VP8LFastLog2SlowFunc)(uint32_t v); - -extern VP8LFastLog2SlowFunc VP8LFastLog2Slow; -extern VP8LFastLog2SlowFunc VP8LFastSLog2Slow; - -static WEBP_INLINE float VP8LFastLog2(uint32_t v) { - return (v < LOG_LOOKUP_IDX_MAX) ? kLog2Table[v] : VP8LFastLog2Slow(v); -} -// Fast calculation of v * log2(v) for integer input. -static WEBP_INLINE float VP8LFastSLog2(uint32_t v) { - return (v < LOG_LOOKUP_IDX_MAX) ? kSLog2Table[v] : VP8LFastSLog2Slow(v); -} +extern VP8LPredictorAddSubFunc VP8LPredictorsSub[16]; +extern VP8LPredictorAddSubFunc VP8LPredictorsSub_C[16]; // ----------------------------------------------------------------------------- // Huffman-cost related functions. @@ -228,11 +171,6 @@ typedef struct { // small struct to hold counters int streaks[2][2]; // [zero/non-zero][streak<3 / streak>=3] } VP8LStreaks; -typedef VP8LStreaks (*VP8LCostCombinedCountFunc)(const uint32_t* X, - const uint32_t* Y, int length); - -extern VP8LCostCombinedCountFunc VP8LHuffmanCostCombinedCount; - typedef struct { // small struct to hold bit entropy results double entropy; // entropy uint32_t sum; // sum of the population @@ -246,26 +184,20 @@ void VP8LBitEntropyInit(VP8LBitEntropy* const entropy); // Get the combined symbol bit entropy and Huffman cost stats for the // distributions 'X' and 'Y'. Those results can then be refined according to // codec specific heuristics. -void VP8LGetCombinedEntropyUnrefined(const uint32_t* const X, - const uint32_t* const Y, int length, - VP8LBitEntropy* const bit_entropy, - VP8LStreaks* const stats); +typedef void (*VP8LGetCombinedEntropyUnrefinedFunc)( + const uint32_t X[], const uint32_t Y[], int length, + VP8LBitEntropy* const bit_entropy, VP8LStreaks* const stats); +extern VP8LGetCombinedEntropyUnrefinedFunc VP8LGetCombinedEntropyUnrefined; + // Get the entropy for the distribution 'X'. -void VP8LGetEntropyUnrefined(const uint32_t* const X, int length, - VP8LBitEntropy* const bit_entropy, - VP8LStreaks* const stats); +typedef void (*VP8LGetEntropyUnrefinedFunc)(const uint32_t X[], int length, + VP8LBitEntropy* const bit_entropy, + VP8LStreaks* const stats); +extern VP8LGetEntropyUnrefinedFunc VP8LGetEntropyUnrefined; void VP8LBitsEntropyUnrefined(const uint32_t* const array, int n, VP8LBitEntropy* const entropy); -typedef void (*GetEntropyUnrefinedHelperFunc)(uint32_t val, int i, - uint32_t* const val_prev, - int* const i_prev, - VP8LBitEntropy* const bit_entropy, - VP8LStreaks* const stats); -// Internal function used by VP8LGet*EntropyUnrefined. -extern GetEntropyUnrefinedHelperFunc VP8LGetEntropyUnrefinedHelper; - typedef void (*VP8LHistogramAddFunc)(const VP8LHistogram* const a, const VP8LHistogram* const b, VP8LHistogram* const out); @@ -279,86 +211,11 @@ typedef int (*VP8LVectorMismatchFunc)(const uint32_t* const array1, // Returns the first index where array1 and array2 are different. extern VP8LVectorMismatchFunc VP8LVectorMismatch; -static WEBP_INLINE int VP8LBitsLog2Ceiling(uint32_t n) { - const int log_floor = BitsLog2Floor(n); - if (n == (n & ~(n - 1))) // zero or a power of two. - return log_floor; - else - return log_floor + 1; -} - -// Splitting of distance and length codes into prefixes and -// extra bits. The prefixes are encoded with an entropy code -// while the extra bits are stored just as normal bits. -static WEBP_INLINE void VP8LPrefixEncodeBitsNoLUT(int distance, int* const code, - int* const extra_bits) { - const int highest_bit = BitsLog2Floor(--distance); - const int second_highest_bit = (distance >> (highest_bit - 1)) & 1; - *extra_bits = highest_bit - 1; - *code = 2 * highest_bit + second_highest_bit; -} - -static WEBP_INLINE void VP8LPrefixEncodeNoLUT(int distance, int* const code, - int* const extra_bits, - int* const extra_bits_value) { - const int highest_bit = BitsLog2Floor(--distance); - const int second_highest_bit = (distance >> (highest_bit - 1)) & 1; - *extra_bits = highest_bit - 1; - *extra_bits_value = distance & ((1 << *extra_bits) - 1); - *code = 2 * highest_bit + second_highest_bit; -} - -#define PREFIX_LOOKUP_IDX_MAX 512 -typedef struct { - int8_t code_; - int8_t extra_bits_; -} VP8LPrefixCode; - -// These tables are derived using VP8LPrefixEncodeNoLUT. -extern const VP8LPrefixCode kPrefixEncodeCode[PREFIX_LOOKUP_IDX_MAX]; -extern const uint8_t kPrefixEncodeExtraBitsValue[PREFIX_LOOKUP_IDX_MAX]; -static WEBP_INLINE void VP8LPrefixEncodeBits(int distance, int* const code, - int* const extra_bits) { - if (distance < PREFIX_LOOKUP_IDX_MAX) { - const VP8LPrefixCode prefix_code = kPrefixEncodeCode[distance]; - *code = prefix_code.code_; - *extra_bits = prefix_code.extra_bits_; - } else { - VP8LPrefixEncodeBitsNoLUT(distance, code, extra_bits); - } -} - -static WEBP_INLINE void VP8LPrefixEncode(int distance, int* const code, - int* const extra_bits, - int* const extra_bits_value) { - if (distance < PREFIX_LOOKUP_IDX_MAX) { - const VP8LPrefixCode prefix_code = kPrefixEncodeCode[distance]; - *code = prefix_code.code_; - *extra_bits = prefix_code.extra_bits_; - *extra_bits_value = kPrefixEncodeExtraBitsValue[distance]; - } else { - VP8LPrefixEncodeNoLUT(distance, code, extra_bits, extra_bits_value); - } -} - -// Sum of each component, mod 256. -static WEBP_INLINE uint32_t VP8LAddPixels(uint32_t a, uint32_t b) { - const uint32_t alpha_and_green = (a & 0xff00ff00u) + (b & 0xff00ff00u); - const uint32_t red_and_blue = (a & 0x00ff00ffu) + (b & 0x00ff00ffu); - return (alpha_and_green & 0xff00ff00u) | (red_and_blue & 0x00ff00ffu); -} - -// Difference of each component, mod 256. -static WEBP_INLINE uint32_t VP8LSubPixels(uint32_t a, uint32_t b) { - const uint32_t alpha_and_green = - 0x00ff00ffu + (a & 0xff00ff00u) - (b & 0xff00ff00u); - const uint32_t red_and_blue = - 0xff00ff00u + (a & 0x00ff00ffu) - (b & 0x00ff00ffu); - return (alpha_and_green & 0xff00ff00u) | (red_and_blue & 0x00ff00ffu); -} - -void VP8LBundleColorMap(const uint8_t* const row, int width, - int xbits, uint32_t* const dst); +typedef void (*VP8LBundleColorMapFunc)(const uint8_t* const row, int width, + int xbits, uint32_t* dst); +extern VP8LBundleColorMapFunc VP8LBundleColorMap; +void VP8LBundleColorMap_C(const uint8_t* const row, int width, int xbits, + uint32_t* dst); // Must be called before calling any of the above methods. void VP8LEncDspInit(void); diff --git a/thirdparty/libwebp/dsp/lossless_common.h b/thirdparty/libwebp/dsp/lossless_common.h new file mode 100644 index 0000000000..c40f711208 --- /dev/null +++ b/thirdparty/libwebp/dsp/lossless_common.h @@ -0,0 +1,210 @@ +// Copyright 2012 Google Inc. All Rights Reserved. +// +// Use of this source code is governed by a BSD-style license +// that can be found in the COPYING file in the root of the source +// tree. An additional intellectual property rights grant can be found +// in the file PATENTS. All contributing project authors may +// be found in the AUTHORS file in the root of the source tree. +// ----------------------------------------------------------------------------- +// +// Image transforms and color space conversion methods for lossless decoder. +// +// Authors: Vikas Arora (vikaas.arora@gmail.com) +// Jyrki Alakuijala (jyrki@google.com) +// Vincent Rabaud (vrabaud@google.com) + +#ifndef WEBP_DSP_LOSSLESS_COMMON_H_ +#define WEBP_DSP_LOSSLESS_COMMON_H_ + +#include "../webp/types.h" + +#include "../utils/utils.h" + +#ifdef __cplusplus +extern "C" { +#endif + +//------------------------------------------------------------------------------ +// Decoding + +// color mapping related functions. +static WEBP_INLINE uint32_t VP8GetARGBIndex(uint32_t idx) { + return (idx >> 8) & 0xff; +} + +static WEBP_INLINE uint8_t VP8GetAlphaIndex(uint8_t idx) { + return idx; +} + +static WEBP_INLINE uint32_t VP8GetARGBValue(uint32_t val) { + return val; +} + +static WEBP_INLINE uint8_t VP8GetAlphaValue(uint32_t val) { + return (val >> 8) & 0xff; +} + +//------------------------------------------------------------------------------ +// Misc methods. + +// Computes sampled size of 'size' when sampling using 'sampling bits'. +static WEBP_INLINE uint32_t VP8LSubSampleSize(uint32_t size, + uint32_t sampling_bits) { + return (size + (1 << sampling_bits) - 1) >> sampling_bits; +} + +// Converts near lossless quality into max number of bits shaved off. +static WEBP_INLINE int VP8LNearLosslessBits(int near_lossless_quality) { + // 100 -> 0 + // 80..99 -> 1 + // 60..79 -> 2 + // 40..59 -> 3 + // 20..39 -> 4 + // 0..19 -> 5 + return 5 - near_lossless_quality / 20; +} + +// ----------------------------------------------------------------------------- +// Faster logarithm for integers. Small values use a look-up table. + +// The threshold till approximate version of log_2 can be used. +// Practically, we can get rid of the call to log() as the two values match to +// very high degree (the ratio of these two is 0.99999x). +// Keeping a high threshold for now. +#define APPROX_LOG_WITH_CORRECTION_MAX 65536 +#define APPROX_LOG_MAX 4096 +#define LOG_2_RECIPROCAL 1.44269504088896338700465094007086 +#define LOG_LOOKUP_IDX_MAX 256 +extern const float kLog2Table[LOG_LOOKUP_IDX_MAX]; +extern const float kSLog2Table[LOG_LOOKUP_IDX_MAX]; +typedef float (*VP8LFastLog2SlowFunc)(uint32_t v); + +extern VP8LFastLog2SlowFunc VP8LFastLog2Slow; +extern VP8LFastLog2SlowFunc VP8LFastSLog2Slow; + +static WEBP_INLINE float VP8LFastLog2(uint32_t v) { + return (v < LOG_LOOKUP_IDX_MAX) ? kLog2Table[v] : VP8LFastLog2Slow(v); +} +// Fast calculation of v * log2(v) for integer input. +static WEBP_INLINE float VP8LFastSLog2(uint32_t v) { + return (v < LOG_LOOKUP_IDX_MAX) ? kSLog2Table[v] : VP8LFastSLog2Slow(v); +} + +// ----------------------------------------------------------------------------- +// PrefixEncode() + +static WEBP_INLINE int VP8LBitsLog2Ceiling(uint32_t n) { + const int log_floor = BitsLog2Floor(n); + if (n == (n & ~(n - 1))) { // zero or a power of two. + return log_floor; + } + return log_floor + 1; +} + +// Splitting of distance and length codes into prefixes and +// extra bits. The prefixes are encoded with an entropy code +// while the extra bits are stored just as normal bits. +static WEBP_INLINE void VP8LPrefixEncodeBitsNoLUT(int distance, int* const code, + int* const extra_bits) { + const int highest_bit = BitsLog2Floor(--distance); + const int second_highest_bit = (distance >> (highest_bit - 1)) & 1; + *extra_bits = highest_bit - 1; + *code = 2 * highest_bit + second_highest_bit; +} + +static WEBP_INLINE void VP8LPrefixEncodeNoLUT(int distance, int* const code, + int* const extra_bits, + int* const extra_bits_value) { + const int highest_bit = BitsLog2Floor(--distance); + const int second_highest_bit = (distance >> (highest_bit - 1)) & 1; + *extra_bits = highest_bit - 1; + *extra_bits_value = distance & ((1 << *extra_bits) - 1); + *code = 2 * highest_bit + second_highest_bit; +} + +#define PREFIX_LOOKUP_IDX_MAX 512 +typedef struct { + int8_t code_; + int8_t extra_bits_; +} VP8LPrefixCode; + +// These tables are derived using VP8LPrefixEncodeNoLUT. +extern const VP8LPrefixCode kPrefixEncodeCode[PREFIX_LOOKUP_IDX_MAX]; +extern const uint8_t kPrefixEncodeExtraBitsValue[PREFIX_LOOKUP_IDX_MAX]; +static WEBP_INLINE void VP8LPrefixEncodeBits(int distance, int* const code, + int* const extra_bits) { + if (distance < PREFIX_LOOKUP_IDX_MAX) { + const VP8LPrefixCode prefix_code = kPrefixEncodeCode[distance]; + *code = prefix_code.code_; + *extra_bits = prefix_code.extra_bits_; + } else { + VP8LPrefixEncodeBitsNoLUT(distance, code, extra_bits); + } +} + +static WEBP_INLINE void VP8LPrefixEncode(int distance, int* const code, + int* const extra_bits, + int* const extra_bits_value) { + if (distance < PREFIX_LOOKUP_IDX_MAX) { + const VP8LPrefixCode prefix_code = kPrefixEncodeCode[distance]; + *code = prefix_code.code_; + *extra_bits = prefix_code.extra_bits_; + *extra_bits_value = kPrefixEncodeExtraBitsValue[distance]; + } else { + VP8LPrefixEncodeNoLUT(distance, code, extra_bits, extra_bits_value); + } +} + +// Sum of each component, mod 256. +static WEBP_UBSAN_IGNORE_UNSIGNED_OVERFLOW WEBP_INLINE +uint32_t VP8LAddPixels(uint32_t a, uint32_t b) { + const uint32_t alpha_and_green = (a & 0xff00ff00u) + (b & 0xff00ff00u); + const uint32_t red_and_blue = (a & 0x00ff00ffu) + (b & 0x00ff00ffu); + return (alpha_and_green & 0xff00ff00u) | (red_and_blue & 0x00ff00ffu); +} + +// Difference of each component, mod 256. +static WEBP_UBSAN_IGNORE_UNSIGNED_OVERFLOW WEBP_INLINE +uint32_t VP8LSubPixels(uint32_t a, uint32_t b) { + const uint32_t alpha_and_green = + 0x00ff00ffu + (a & 0xff00ff00u) - (b & 0xff00ff00u); + const uint32_t red_and_blue = + 0xff00ff00u + (a & 0x00ff00ffu) - (b & 0x00ff00ffu); + return (alpha_and_green & 0xff00ff00u) | (red_and_blue & 0x00ff00ffu); +} + +//------------------------------------------------------------------------------ +// Transform-related functions use din both encoding and decoding. + +// Macros used to create a batch predictor that iteratively uses a +// one-pixel predictor. + +// The predictor is added to the output pixel (which +// is therefore considered as a residual) to get the final prediction. +#define GENERATE_PREDICTOR_ADD(PREDICTOR, PREDICTOR_ADD) \ +static void PREDICTOR_ADD(const uint32_t* in, const uint32_t* upper, \ + int num_pixels, uint32_t* out) { \ + int x; \ + for (x = 0; x < num_pixels; ++x) { \ + const uint32_t pred = (PREDICTOR)(out[x - 1], upper + x); \ + out[x] = VP8LAddPixels(in[x], pred); \ + } \ +} + +// It subtracts the prediction from the input pixel and stores the residual +// in the output pixel. +#define GENERATE_PREDICTOR_SUB(PREDICTOR, PREDICTOR_SUB) \ +static void PREDICTOR_SUB(const uint32_t* in, const uint32_t* upper, \ + int num_pixels, uint32_t* out) { \ + int x; \ + for (x = 0; x < num_pixels; ++x) { \ + const uint32_t pred = (PREDICTOR)(in[x - 1], upper + x); \ + out[x] = VP8LSubPixels(in[x], pred); \ + } \ +} + +#ifdef __cplusplus +} // extern "C" +#endif + +#endif // WEBP_DSP_LOSSLESS_COMMON_H_ diff --git a/thirdparty/libwebp/dsp/lossless_enc.c b/thirdparty/libwebp/dsp/lossless_enc.c index 256f6f5f8b..4e46fbab8b 100644 --- a/thirdparty/libwebp/dsp/lossless_enc.c +++ b/thirdparty/libwebp/dsp/lossless_enc.c @@ -17,16 +17,12 @@ #include #include -#include "../dec/vp8li.h" -#include "../utils/endian_inl.h" +#include "../dec/vp8li_dec.h" +#include "../utils/endian_inl_utils.h" #include "./lossless.h" +#include "./lossless_common.h" #include "./yuv.h" -#define MAX_DIFF_COST (1e30f) - -static const int kPredLowEffort = 11; -static const uint32_t kMaskAlpha = 0xff000000; - // lookup table for small values of log2(int) const float kLog2Table[LOG_LOOKUP_IDX_MAX] = { 0.0000000000000000f, 0.0000000000000000f, @@ -380,26 +376,9 @@ static float FastLog2Slow(uint32_t v) { } } -// Mostly used to reduce code size + readability -static WEBP_INLINE int GetMin(int a, int b) { return (a > b) ? b : a; } -static WEBP_INLINE int GetMax(int a, int b) { return (a < b) ? b : a; } - //------------------------------------------------------------------------------ // Methods to calculate Entropy (Shannon). -static float PredictionCostSpatial(const int counts[256], int weight_0, - double exp_val) { - const int significant_symbols = 256 >> 4; - const double exp_decay_factor = 0.6; - double bits = weight_0 * counts[0]; - int i; - for (i = 1; i < significant_symbols; ++i) { - bits += exp_val * (counts[i] + counts[256 - i]); - exp_val *= exp_decay_factor; - } - return (float)(-0.1 * bits); -} - // Compute the combined Shanon's entropy for distribution {X} and {X+Y} static float CombinedShannonEntropy(const int X[256], const int Y[256]) { int i; @@ -422,18 +401,6 @@ static float CombinedShannonEntropy(const int X[256], const int Y[256]) { return (float)retval; } -static float PredictionCostSpatialHistogram(const int accumulated[4][256], - const int tile[4][256]) { - int i; - double retval = 0; - for (i = 0; i < 4; ++i) { - const double kExpValue = 0.94; - retval += PredictionCostSpatial(tile[i], 1, kExpValue); - retval += VP8LCombinedShannonEntropy(tile[i], accumulated[i]); - } - return (float)retval; -} - void VP8LBitEntropyInit(VP8LBitEntropy* const entropy) { entropy->entropy = 0.; entropy->sum = 0; @@ -486,9 +453,9 @@ static WEBP_INLINE void GetEntropyUnrefinedHelper( *i_prev = i; } -void VP8LGetEntropyUnrefined(const uint32_t* const X, int length, - VP8LBitEntropy* const bit_entropy, - VP8LStreaks* const stats) { +static void GetEntropyUnrefined(const uint32_t X[], int length, + VP8LBitEntropy* const bit_entropy, + VP8LStreaks* const stats) { int i; int i_prev = 0; uint32_t x_prev = X[0]; @@ -499,18 +466,18 @@ void VP8LGetEntropyUnrefined(const uint32_t* const X, int length, for (i = 1; i < length; ++i) { const uint32_t x = X[i]; if (x != x_prev) { - VP8LGetEntropyUnrefinedHelper(x, i, &x_prev, &i_prev, bit_entropy, stats); + GetEntropyUnrefinedHelper(x, i, &x_prev, &i_prev, bit_entropy, stats); } } - VP8LGetEntropyUnrefinedHelper(0, i, &x_prev, &i_prev, bit_entropy, stats); + GetEntropyUnrefinedHelper(0, i, &x_prev, &i_prev, bit_entropy, stats); bit_entropy->entropy += VP8LFastSLog2(bit_entropy->sum); } -void VP8LGetCombinedEntropyUnrefined(const uint32_t* const X, - const uint32_t* const Y, int length, - VP8LBitEntropy* const bit_entropy, - VP8LStreaks* const stats) { +static void GetCombinedEntropyUnrefined(const uint32_t X[], const uint32_t Y[], + int length, + VP8LBitEntropy* const bit_entropy, + VP8LStreaks* const stats) { int i = 1; int i_prev = 0; uint32_t xy_prev = X[0] + Y[0]; @@ -521,439 +488,29 @@ void VP8LGetCombinedEntropyUnrefined(const uint32_t* const X, for (i = 1; i < length; ++i) { const uint32_t xy = X[i] + Y[i]; if (xy != xy_prev) { - VP8LGetEntropyUnrefinedHelper(xy, i, &xy_prev, &i_prev, bit_entropy, - stats); + GetEntropyUnrefinedHelper(xy, i, &xy_prev, &i_prev, bit_entropy, stats); } } - VP8LGetEntropyUnrefinedHelper(0, i, &xy_prev, &i_prev, bit_entropy, stats); + GetEntropyUnrefinedHelper(0, i, &xy_prev, &i_prev, bit_entropy, stats); bit_entropy->entropy += VP8LFastSLog2(bit_entropy->sum); } -static WEBP_INLINE void UpdateHisto(int histo_argb[4][256], uint32_t argb) { - ++histo_argb[0][argb >> 24]; - ++histo_argb[1][(argb >> 16) & 0xff]; - ++histo_argb[2][(argb >> 8) & 0xff]; - ++histo_argb[3][argb & 0xff]; -} - //------------------------------------------------------------------------------ -static WEBP_INLINE uint32_t Predict(VP8LPredictorFunc pred_func, - int x, int y, - const uint32_t* current_row, - const uint32_t* upper_row) { - if (y == 0) { - return (x == 0) ? ARGB_BLACK : current_row[x - 1]; // Left. - } else if (x == 0) { - return upper_row[x]; // Top. - } else { - return pred_func(current_row[x - 1], upper_row + x); - } -} - -static int MaxDiffBetweenPixels(uint32_t p1, uint32_t p2) { - const int diff_a = abs((int)(p1 >> 24) - (int)(p2 >> 24)); - const int diff_r = abs((int)((p1 >> 16) & 0xff) - (int)((p2 >> 16) & 0xff)); - const int diff_g = abs((int)((p1 >> 8) & 0xff) - (int)((p2 >> 8) & 0xff)); - const int diff_b = abs((int)(p1 & 0xff) - (int)(p2 & 0xff)); - return GetMax(GetMax(diff_a, diff_r), GetMax(diff_g, diff_b)); -} - -static int MaxDiffAroundPixel(uint32_t current, uint32_t up, uint32_t down, - uint32_t left, uint32_t right) { - const int diff_up = MaxDiffBetweenPixels(current, up); - const int diff_down = MaxDiffBetweenPixels(current, down); - const int diff_left = MaxDiffBetweenPixels(current, left); - const int diff_right = MaxDiffBetweenPixels(current, right); - return GetMax(GetMax(diff_up, diff_down), GetMax(diff_left, diff_right)); -} - -static uint32_t AddGreenToBlueAndRed(uint32_t argb) { - const uint32_t green = (argb >> 8) & 0xff; - uint32_t red_blue = argb & 0x00ff00ffu; - red_blue += (green << 16) | green; - red_blue &= 0x00ff00ffu; - return (argb & 0xff00ff00u) | red_blue; -} - -static void MaxDiffsForRow(int width, int stride, const uint32_t* const argb, - uint8_t* const max_diffs, int used_subtract_green) { - uint32_t current, up, down, left, right; - int x; - if (width <= 2) return; - current = argb[0]; - right = argb[1]; - if (used_subtract_green) { - current = AddGreenToBlueAndRed(current); - right = AddGreenToBlueAndRed(right); - } - // max_diffs[0] and max_diffs[width - 1] are never used. - for (x = 1; x < width - 1; ++x) { - up = argb[-stride + x]; - down = argb[stride + x]; - left = current; - current = right; - right = argb[x + 1]; - if (used_subtract_green) { - up = AddGreenToBlueAndRed(up); - down = AddGreenToBlueAndRed(down); - right = AddGreenToBlueAndRed(right); - } - max_diffs[x] = MaxDiffAroundPixel(current, up, down, left, right); - } -} - -// Quantize the difference between the actual component value and its prediction -// to a multiple of quantization, working modulo 256, taking care not to cross -// a boundary (inclusive upper limit). -static uint8_t NearLosslessComponent(uint8_t value, uint8_t predict, - uint8_t boundary, int quantization) { - const int residual = (value - predict) & 0xff; - const int boundary_residual = (boundary - predict) & 0xff; - const int lower = residual & ~(quantization - 1); - const int upper = lower + quantization; - // Resolve ties towards a value closer to the prediction (i.e. towards lower - // if value comes after prediction and towards upper otherwise). - const int bias = ((boundary - value) & 0xff) < boundary_residual; - if (residual - lower < upper - residual + bias) { - // lower is closer to residual than upper. - if (residual > boundary_residual && lower <= boundary_residual) { - // Halve quantization step to avoid crossing boundary. This midpoint is - // on the same side of boundary as residual because midpoint >= residual - // (since lower is closer than upper) and residual is above the boundary. - return lower + (quantization >> 1); - } - return lower; - } else { - // upper is closer to residual than lower. - if (residual <= boundary_residual && upper > boundary_residual) { - // Halve quantization step to avoid crossing boundary. This midpoint is - // on the same side of boundary as residual because midpoint <= residual - // (since upper is closer than lower) and residual is below the boundary. - return lower + (quantization >> 1); - } - return upper & 0xff; - } -} - -// Quantize every component of the difference between the actual pixel value and -// its prediction to a multiple of a quantization (a power of 2, not larger than -// max_quantization which is a power of 2, smaller than max_diff). Take care if -// value and predict have undergone subtract green, which means that red and -// blue are represented as offsets from green. -static uint32_t NearLossless(uint32_t value, uint32_t predict, - int max_quantization, int max_diff, - int used_subtract_green) { - int quantization; - uint8_t new_green = 0; - uint8_t green_diff = 0; - uint8_t a, r, g, b; - if (max_diff <= 2) { - return VP8LSubPixels(value, predict); - } - quantization = max_quantization; - while (quantization >= max_diff) { - quantization >>= 1; - } - if ((value >> 24) == 0 || (value >> 24) == 0xff) { - // Preserve transparency of fully transparent or fully opaque pixels. - a = ((value >> 24) - (predict >> 24)) & 0xff; - } else { - a = NearLosslessComponent(value >> 24, predict >> 24, 0xff, quantization); - } - g = NearLosslessComponent((value >> 8) & 0xff, (predict >> 8) & 0xff, 0xff, - quantization); - if (used_subtract_green) { - // The green offset will be added to red and blue components during decoding - // to obtain the actual red and blue values. - new_green = ((predict >> 8) + g) & 0xff; - // The amount by which green has been adjusted during quantization. It is - // subtracted from red and blue for compensation, to avoid accumulating two - // quantization errors in them. - green_diff = (new_green - (value >> 8)) & 0xff; - } - r = NearLosslessComponent(((value >> 16) - green_diff) & 0xff, - (predict >> 16) & 0xff, 0xff - new_green, - quantization); - b = NearLosslessComponent((value - green_diff) & 0xff, predict & 0xff, - 0xff - new_green, quantization); - return ((uint32_t)a << 24) | ((uint32_t)r << 16) | ((uint32_t)g << 8) | b; -} - -// Returns the difference between the pixel and its prediction. In case of a -// lossy encoding, updates the source image to avoid propagating the deviation -// further to pixels which depend on the current pixel for their predictions. -static WEBP_INLINE uint32_t GetResidual(int width, int height, - uint32_t* const upper_row, - uint32_t* const current_row, - const uint8_t* const max_diffs, - int mode, VP8LPredictorFunc pred_func, - int x, int y, int max_quantization, - int exact, int used_subtract_green) { - const uint32_t predict = Predict(pred_func, x, y, current_row, upper_row); - uint32_t residual; - if (max_quantization == 1 || mode == 0 || y == 0 || y == height - 1 || - x == 0 || x == width - 1) { - residual = VP8LSubPixels(current_row[x], predict); - } else { - residual = NearLossless(current_row[x], predict, max_quantization, - max_diffs[x], used_subtract_green); - // Update the source image. - current_row[x] = VP8LAddPixels(predict, residual); - // x is never 0 here so we do not need to update upper_row like below. - } - if (!exact && (current_row[x] & kMaskAlpha) == 0) { - // If alpha is 0, cleanup RGB. We can choose the RGB values of the residual - // for best compression. The prediction of alpha itself can be non-zero and - // must be kept though. We choose RGB of the residual to be 0. - residual &= kMaskAlpha; - // Update the source image. - current_row[x] = predict & ~kMaskAlpha; - // The prediction for the rightmost pixel in a row uses the leftmost pixel - // in that row as its top-right context pixel. Hence if we change the - // leftmost pixel of current_row, the corresponding change must be applied - // to upper_row as well where top-right context is being read from. - if (x == 0 && y != 0) upper_row[width] = current_row[0]; - } - return residual; -} - -// Returns best predictor and updates the accumulated histogram. -// If max_quantization > 1, assumes that near lossless processing will be -// applied, quantizing residuals to multiples of quantization levels up to -// max_quantization (the actual quantization level depends on smoothness near -// the given pixel). -static int GetBestPredictorForTile(int width, int height, - int tile_x, int tile_y, int bits, - int accumulated[4][256], - uint32_t* const argb_scratch, - const uint32_t* const argb, - int max_quantization, - int exact, int used_subtract_green) { - const int kNumPredModes = 14; - const int start_x = tile_x << bits; - const int start_y = tile_y << bits; - const int tile_size = 1 << bits; - const int max_y = GetMin(tile_size, height - start_y); - const int max_x = GetMin(tile_size, width - start_x); - // Whether there exist columns just outside the tile. - const int have_left = (start_x > 0); - const int have_right = (max_x < width - start_x); - // Position and size of the strip covering the tile and adjacent columns if - // they exist. - const int context_start_x = start_x - have_left; - const int context_width = max_x + have_left + have_right; - // The width of upper_row and current_row is one pixel larger than image width - // to allow the top right pixel to point to the leftmost pixel of the next row - // when at the right edge. - uint32_t* upper_row = argb_scratch; - uint32_t* current_row = upper_row + width + 1; - uint8_t* const max_diffs = (uint8_t*)(current_row + width + 1); - float best_diff = MAX_DIFF_COST; - int best_mode = 0; - int mode; - int histo_stack_1[4][256]; - int histo_stack_2[4][256]; - // Need pointers to be able to swap arrays. - int (*histo_argb)[256] = histo_stack_1; - int (*best_histo)[256] = histo_stack_2; - int i, j; - - for (mode = 0; mode < kNumPredModes; ++mode) { - const VP8LPredictorFunc pred_func = VP8LPredictors[mode]; - float cur_diff; - int relative_y; - memset(histo_argb, 0, sizeof(histo_stack_1)); - if (start_y > 0) { - // Read the row above the tile which will become the first upper_row. - // Include a pixel to the left if it exists; include a pixel to the right - // in all cases (wrapping to the leftmost pixel of the next row if it does - // not exist). - memcpy(current_row + context_start_x, - argb + (start_y - 1) * width + context_start_x, - sizeof(*argb) * (max_x + have_left + 1)); - } - for (relative_y = 0; relative_y < max_y; ++relative_y) { - const int y = start_y + relative_y; - int relative_x; - uint32_t* tmp = upper_row; - upper_row = current_row; - current_row = tmp; - // Read current_row. Include a pixel to the left if it exists; include a - // pixel to the right in all cases except at the bottom right corner of - // the image (wrapping to the leftmost pixel of the next row if it does - // not exist in the current row). - memcpy(current_row + context_start_x, - argb + y * width + context_start_x, - sizeof(*argb) * (max_x + have_left + (y + 1 < height))); - if (max_quantization > 1 && y >= 1 && y + 1 < height) { - MaxDiffsForRow(context_width, width, argb + y * width + context_start_x, - max_diffs + context_start_x, used_subtract_green); - } - - for (relative_x = 0; relative_x < max_x; ++relative_x) { - const int x = start_x + relative_x; - UpdateHisto(histo_argb, - GetResidual(width, height, upper_row, current_row, - max_diffs, mode, pred_func, x, y, - max_quantization, exact, used_subtract_green)); - } - } - cur_diff = PredictionCostSpatialHistogram( - (const int (*)[256])accumulated, (const int (*)[256])histo_argb); - if (cur_diff < best_diff) { - int (*tmp)[256] = histo_argb; - histo_argb = best_histo; - best_histo = tmp; - best_diff = cur_diff; - best_mode = mode; - } - } - - for (i = 0; i < 4; i++) { - for (j = 0; j < 256; j++) { - accumulated[i][j] += best_histo[i][j]; - } - } - - return best_mode; -} - -// Converts pixels of the image to residuals with respect to predictions. -// If max_quantization > 1, applies near lossless processing, quantizing -// residuals to multiples of quantization levels up to max_quantization -// (the actual quantization level depends on smoothness near the given pixel). -static void CopyImageWithPrediction(int width, int height, - int bits, uint32_t* const modes, - uint32_t* const argb_scratch, - uint32_t* const argb, - int low_effort, int max_quantization, - int exact, int used_subtract_green) { - const int tiles_per_row = VP8LSubSampleSize(width, bits); - const int mask = (1 << bits) - 1; - // The width of upper_row and current_row is one pixel larger than image width - // to allow the top right pixel to point to the leftmost pixel of the next row - // when at the right edge. - uint32_t* upper_row = argb_scratch; - uint32_t* current_row = upper_row + width + 1; - uint8_t* current_max_diffs = (uint8_t*)(current_row + width + 1); - uint8_t* lower_max_diffs = current_max_diffs + width; - int y; - int mode = 0; - VP8LPredictorFunc pred_func = NULL; - - for (y = 0; y < height; ++y) { - int x; - uint32_t* const tmp32 = upper_row; - upper_row = current_row; - current_row = tmp32; - memcpy(current_row, argb + y * width, - sizeof(*argb) * (width + (y + 1 < height))); - - if (low_effort) { - for (x = 0; x < width; ++x) { - const uint32_t predict = Predict(VP8LPredictors[kPredLowEffort], x, y, - current_row, upper_row); - argb[y * width + x] = VP8LSubPixels(current_row[x], predict); - } - } else { - if (max_quantization > 1) { - // Compute max_diffs for the lower row now, because that needs the - // contents of argb for the current row, which we will overwrite with - // residuals before proceeding with the next row. - uint8_t* const tmp8 = current_max_diffs; - current_max_diffs = lower_max_diffs; - lower_max_diffs = tmp8; - if (y + 2 < height) { - MaxDiffsForRow(width, width, argb + (y + 1) * width, lower_max_diffs, - used_subtract_green); - } - } - for (x = 0; x < width; ++x) { - if ((x & mask) == 0) { - mode = (modes[(y >> bits) * tiles_per_row + (x >> bits)] >> 8) & 0xff; - pred_func = VP8LPredictors[mode]; - } - argb[y * width + x] = GetResidual( - width, height, upper_row, current_row, current_max_diffs, mode, - pred_func, x, y, max_quantization, exact, used_subtract_green); - } - } - } -} - -// Finds the best predictor for each tile, and converts the image to residuals -// with respect to predictions. If near_lossless_quality < 100, applies -// near lossless processing, shaving off more bits of residuals for lower -// qualities. -void VP8LResidualImage(int width, int height, int bits, int low_effort, - uint32_t* const argb, uint32_t* const argb_scratch, - uint32_t* const image, int near_lossless_quality, - int exact, int used_subtract_green) { - const int tiles_per_row = VP8LSubSampleSize(width, bits); - const int tiles_per_col = VP8LSubSampleSize(height, bits); - int tile_y; - int histo[4][256]; - const int max_quantization = 1 << VP8LNearLosslessBits(near_lossless_quality); - if (low_effort) { - int i; - for (i = 0; i < tiles_per_row * tiles_per_col; ++i) { - image[i] = ARGB_BLACK | (kPredLowEffort << 8); - } - } else { - memset(histo, 0, sizeof(histo)); - for (tile_y = 0; tile_y < tiles_per_col; ++tile_y) { - int tile_x; - for (tile_x = 0; tile_x < tiles_per_row; ++tile_x) { - const int pred = GetBestPredictorForTile(width, height, tile_x, tile_y, - bits, histo, argb_scratch, argb, max_quantization, exact, - used_subtract_green); - image[tile_y * tiles_per_row + tile_x] = ARGB_BLACK | (pred << 8); - } - } - } - - CopyImageWithPrediction(width, height, bits, image, argb_scratch, argb, - low_effort, max_quantization, exact, - used_subtract_green); -} - void VP8LSubtractGreenFromBlueAndRed_C(uint32_t* argb_data, int num_pixels) { int i; for (i = 0; i < num_pixels; ++i) { - const uint32_t argb = argb_data[i]; - const uint32_t green = (argb >> 8) & 0xff; + const int argb = argb_data[i]; + const int green = (argb >> 8) & 0xff; const uint32_t new_r = (((argb >> 16) & 0xff) - green) & 0xff; - const uint32_t new_b = ((argb & 0xff) - green) & 0xff; - argb_data[i] = (argb & 0xff00ff00) | (new_r << 16) | new_b; + const uint32_t new_b = (((argb >> 0) & 0xff) - green) & 0xff; + argb_data[i] = (argb & 0xff00ff00u) | (new_r << 16) | new_b; } } -static WEBP_INLINE void MultipliersClear(VP8LMultipliers* const m) { - m->green_to_red_ = 0; - m->green_to_blue_ = 0; - m->red_to_blue_ = 0; -} - -static WEBP_INLINE uint32_t ColorTransformDelta(int8_t color_pred, - int8_t color) { - return (uint32_t)((int)(color_pred) * color) >> 5; -} - -static WEBP_INLINE void ColorCodeToMultipliers(uint32_t color_code, - VP8LMultipliers* const m) { - m->green_to_red_ = (color_code >> 0) & 0xff; - m->green_to_blue_ = (color_code >> 8) & 0xff; - m->red_to_blue_ = (color_code >> 16) & 0xff; -} - -static WEBP_INLINE uint32_t MultipliersToColorCode( - const VP8LMultipliers* const m) { - return 0xff000000u | - ((uint32_t)(m->red_to_blue_) << 16) | - ((uint32_t)(m->green_to_blue_) << 8) | - m->green_to_red_; +static WEBP_INLINE int ColorTransformDelta(int8_t color_pred, int8_t color) { + return ((int)color_pred * color) >> 5; } void VP8LTransformColor_C(const VP8LMultipliers* const m, uint32_t* data, @@ -963,8 +520,8 @@ void VP8LTransformColor_C(const VP8LMultipliers* const m, uint32_t* data, const uint32_t argb = data[i]; const uint32_t green = argb >> 8; const uint32_t red = argb >> 16; - uint32_t new_red = red; - uint32_t new_blue = argb; + int new_red = red; + int new_blue = argb; new_red -= ColorTransformDelta(m->green_to_red_, green); new_red &= 0xff; new_blue -= ColorTransformDelta(m->green_to_blue_, green); @@ -977,7 +534,7 @@ void VP8LTransformColor_C(const VP8LMultipliers* const m, uint32_t* data, static WEBP_INLINE uint8_t TransformColorRed(uint8_t green_to_red, uint32_t argb) { const uint32_t green = argb >> 8; - uint32_t new_red = argb >> 16; + int new_red = argb >> 16; new_red -= ColorTransformDelta(green_to_red, green); return (new_red & 0xff); } @@ -993,15 +550,6 @@ static WEBP_INLINE uint8_t TransformColorBlue(uint8_t green_to_blue, return (new_blue & 0xff); } -static float PredictionCostCrossColor(const int accumulated[256], - const int counts[256]) { - // Favor low entropy, locally and globally. - // Favor small absolute values for PredictionCostSpatial - static const double kExpValue = 2.4; - return VP8LCombinedShannonEntropy(counts, accumulated) + - PredictionCostSpatial(counts, 3, kExpValue); -} - void VP8LCollectColorRedTransforms_C(const uint32_t* argb, int stride, int tile_width, int tile_height, int green_to_red, int histo[]) { @@ -1014,59 +562,6 @@ void VP8LCollectColorRedTransforms_C(const uint32_t* argb, int stride, } } -static float GetPredictionCostCrossColorRed( - const uint32_t* argb, int stride, int tile_width, int tile_height, - VP8LMultipliers prev_x, VP8LMultipliers prev_y, int green_to_red, - const int accumulated_red_histo[256]) { - int histo[256] = { 0 }; - float cur_diff; - - VP8LCollectColorRedTransforms(argb, stride, tile_width, tile_height, - green_to_red, histo); - - cur_diff = PredictionCostCrossColor(accumulated_red_histo, histo); - if ((uint8_t)green_to_red == prev_x.green_to_red_) { - cur_diff -= 3; // favor keeping the areas locally similar - } - if ((uint8_t)green_to_red == prev_y.green_to_red_) { - cur_diff -= 3; // favor keeping the areas locally similar - } - if (green_to_red == 0) { - cur_diff -= 3; - } - return cur_diff; -} - -static void GetBestGreenToRed( - const uint32_t* argb, int stride, int tile_width, int tile_height, - VP8LMultipliers prev_x, VP8LMultipliers prev_y, int quality, - const int accumulated_red_histo[256], VP8LMultipliers* const best_tx) { - const int kMaxIters = 4 + ((7 * quality) >> 8); // in range [4..6] - int green_to_red_best = 0; - int iter, offset; - float best_diff = GetPredictionCostCrossColorRed( - argb, stride, tile_width, tile_height, prev_x, prev_y, - green_to_red_best, accumulated_red_histo); - for (iter = 0; iter < kMaxIters; ++iter) { - // ColorTransformDelta is a 3.5 bit fixed point, so 32 is equal to - // one in color computation. Having initial delta here as 1 is sufficient - // to explore the range of (-2, 2). - const int delta = 32 >> iter; - // Try a negative and a positive delta from the best known value. - for (offset = -delta; offset <= delta; offset += 2 * delta) { - const int green_to_red_cur = offset + green_to_red_best; - const float cur_diff = GetPredictionCostCrossColorRed( - argb, stride, tile_width, tile_height, prev_x, prev_y, - green_to_red_cur, accumulated_red_histo); - if (cur_diff < best_diff) { - best_diff = cur_diff; - green_to_red_best = green_to_red_cur; - } - } - } - best_tx->green_to_red_ = green_to_red_best; -} - void VP8LCollectColorBlueTransforms_C(const uint32_t* argb, int stride, int tile_width, int tile_height, int green_to_blue, int red_to_blue, @@ -1080,187 +575,6 @@ void VP8LCollectColorBlueTransforms_C(const uint32_t* argb, int stride, } } -static float GetPredictionCostCrossColorBlue( - const uint32_t* argb, int stride, int tile_width, int tile_height, - VP8LMultipliers prev_x, VP8LMultipliers prev_y, - int green_to_blue, int red_to_blue, const int accumulated_blue_histo[256]) { - int histo[256] = { 0 }; - float cur_diff; - - VP8LCollectColorBlueTransforms(argb, stride, tile_width, tile_height, - green_to_blue, red_to_blue, histo); - - cur_diff = PredictionCostCrossColor(accumulated_blue_histo, histo); - if ((uint8_t)green_to_blue == prev_x.green_to_blue_) { - cur_diff -= 3; // favor keeping the areas locally similar - } - if ((uint8_t)green_to_blue == prev_y.green_to_blue_) { - cur_diff -= 3; // favor keeping the areas locally similar - } - if ((uint8_t)red_to_blue == prev_x.red_to_blue_) { - cur_diff -= 3; // favor keeping the areas locally similar - } - if ((uint8_t)red_to_blue == prev_y.red_to_blue_) { - cur_diff -= 3; // favor keeping the areas locally similar - } - if (green_to_blue == 0) { - cur_diff -= 3; - } - if (red_to_blue == 0) { - cur_diff -= 3; - } - return cur_diff; -} - -#define kGreenRedToBlueNumAxis 8 -#define kGreenRedToBlueMaxIters 7 -static void GetBestGreenRedToBlue( - const uint32_t* argb, int stride, int tile_width, int tile_height, - VP8LMultipliers prev_x, VP8LMultipliers prev_y, int quality, - const int accumulated_blue_histo[256], - VP8LMultipliers* const best_tx) { - const int8_t offset[kGreenRedToBlueNumAxis][2] = - {{0, -1}, {0, 1}, {-1, 0}, {1, 0}, {-1, -1}, {-1, 1}, {1, -1}, {1, 1}}; - const int8_t delta_lut[kGreenRedToBlueMaxIters] = { 16, 16, 8, 4, 2, 2, 2 }; - const int iters = - (quality < 25) ? 1 : (quality > 50) ? kGreenRedToBlueMaxIters : 4; - int green_to_blue_best = 0; - int red_to_blue_best = 0; - int iter; - // Initial value at origin: - float best_diff = GetPredictionCostCrossColorBlue( - argb, stride, tile_width, tile_height, prev_x, prev_y, - green_to_blue_best, red_to_blue_best, accumulated_blue_histo); - for (iter = 0; iter < iters; ++iter) { - const int delta = delta_lut[iter]; - int axis; - for (axis = 0; axis < kGreenRedToBlueNumAxis; ++axis) { - const int green_to_blue_cur = - offset[axis][0] * delta + green_to_blue_best; - const int red_to_blue_cur = offset[axis][1] * delta + red_to_blue_best; - const float cur_diff = GetPredictionCostCrossColorBlue( - argb, stride, tile_width, tile_height, prev_x, prev_y, - green_to_blue_cur, red_to_blue_cur, accumulated_blue_histo); - if (cur_diff < best_diff) { - best_diff = cur_diff; - green_to_blue_best = green_to_blue_cur; - red_to_blue_best = red_to_blue_cur; - } - if (quality < 25 && iter == 4) { - // Only axis aligned diffs for lower quality. - break; // next iter. - } - } - if (delta == 2 && green_to_blue_best == 0 && red_to_blue_best == 0) { - // Further iterations would not help. - break; // out of iter-loop. - } - } - best_tx->green_to_blue_ = green_to_blue_best; - best_tx->red_to_blue_ = red_to_blue_best; -} -#undef kGreenRedToBlueMaxIters -#undef kGreenRedToBlueNumAxis - -static VP8LMultipliers GetBestColorTransformForTile( - int tile_x, int tile_y, int bits, - VP8LMultipliers prev_x, - VP8LMultipliers prev_y, - int quality, int xsize, int ysize, - const int accumulated_red_histo[256], - const int accumulated_blue_histo[256], - const uint32_t* const argb) { - const int max_tile_size = 1 << bits; - const int tile_y_offset = tile_y * max_tile_size; - const int tile_x_offset = tile_x * max_tile_size; - const int all_x_max = GetMin(tile_x_offset + max_tile_size, xsize); - const int all_y_max = GetMin(tile_y_offset + max_tile_size, ysize); - const int tile_width = all_x_max - tile_x_offset; - const int tile_height = all_y_max - tile_y_offset; - const uint32_t* const tile_argb = argb + tile_y_offset * xsize - + tile_x_offset; - VP8LMultipliers best_tx; - MultipliersClear(&best_tx); - - GetBestGreenToRed(tile_argb, xsize, tile_width, tile_height, - prev_x, prev_y, quality, accumulated_red_histo, &best_tx); - GetBestGreenRedToBlue(tile_argb, xsize, tile_width, tile_height, - prev_x, prev_y, quality, accumulated_blue_histo, - &best_tx); - return best_tx; -} - -static void CopyTileWithColorTransform(int xsize, int ysize, - int tile_x, int tile_y, - int max_tile_size, - VP8LMultipliers color_transform, - uint32_t* argb) { - const int xscan = GetMin(max_tile_size, xsize - tile_x); - int yscan = GetMin(max_tile_size, ysize - tile_y); - argb += tile_y * xsize + tile_x; - while (yscan-- > 0) { - VP8LTransformColor(&color_transform, argb, xscan); - argb += xsize; - } -} - -void VP8LColorSpaceTransform(int width, int height, int bits, int quality, - uint32_t* const argb, uint32_t* image) { - const int max_tile_size = 1 << bits; - const int tile_xsize = VP8LSubSampleSize(width, bits); - const int tile_ysize = VP8LSubSampleSize(height, bits); - int accumulated_red_histo[256] = { 0 }; - int accumulated_blue_histo[256] = { 0 }; - int tile_x, tile_y; - VP8LMultipliers prev_x, prev_y; - MultipliersClear(&prev_y); - MultipliersClear(&prev_x); - for (tile_y = 0; tile_y < tile_ysize; ++tile_y) { - for (tile_x = 0; tile_x < tile_xsize; ++tile_x) { - int y; - const int tile_x_offset = tile_x * max_tile_size; - const int tile_y_offset = tile_y * max_tile_size; - const int all_x_max = GetMin(tile_x_offset + max_tile_size, width); - const int all_y_max = GetMin(tile_y_offset + max_tile_size, height); - const int offset = tile_y * tile_xsize + tile_x; - if (tile_y != 0) { - ColorCodeToMultipliers(image[offset - tile_xsize], &prev_y); - } - prev_x = GetBestColorTransformForTile(tile_x, tile_y, bits, - prev_x, prev_y, - quality, width, height, - accumulated_red_histo, - accumulated_blue_histo, - argb); - image[offset] = MultipliersToColorCode(&prev_x); - CopyTileWithColorTransform(width, height, tile_x_offset, tile_y_offset, - max_tile_size, prev_x, argb); - - // Gather accumulated histogram data. - for (y = tile_y_offset; y < all_y_max; ++y) { - int ix = y * width + tile_x_offset; - const int ix_end = ix + all_x_max - tile_x_offset; - for (; ix < ix_end; ++ix) { - const uint32_t pix = argb[ix]; - if (ix >= 2 && - pix == argb[ix - 2] && - pix == argb[ix - 1]) { - continue; // repeated pixels are handled by backward references - } - if (ix >= width + 2 && - argb[ix - 2] == argb[ix - width - 2] && - argb[ix - 1] == argb[ix - width - 1] && - pix == argb[ix - width]) { - continue; // repeated pixels are handled by backward references - } - ++accumulated_red_histo[(pix >> 16) & 0xff]; - ++accumulated_blue_histo[(pix >> 0) & 0xff]; - } - } - } - } -} - //------------------------------------------------------------------------------ static int VectorMismatch(const uint32_t* const array1, @@ -1274,8 +588,8 @@ static int VectorMismatch(const uint32_t* const array1, } // Bundles multiple (1, 2, 4 or 8) pixels into a single pixel. -void VP8LBundleColorMap(const uint8_t* const row, int width, - int xbits, uint32_t* const dst) { +void VP8LBundleColorMap_C(const uint8_t* const row, int width, int xbits, + uint32_t* dst) { int x; if (xbits > 0) { const int bit_depth = 1 << (3 - xbits); @@ -1350,8 +664,172 @@ static void HistogramAdd(const VP8LHistogram* const a, } //------------------------------------------------------------------------------ +// Image transforms. -VP8LProcessBlueAndRedFunc VP8LSubtractGreenFromBlueAndRed; +static WEBP_INLINE uint32_t Average2(uint32_t a0, uint32_t a1) { + return (((a0 ^ a1) & 0xfefefefeu) >> 1) + (a0 & a1); +} + +static WEBP_INLINE uint32_t Average3(uint32_t a0, uint32_t a1, uint32_t a2) { + return Average2(Average2(a0, a2), a1); +} + +static WEBP_INLINE uint32_t Average4(uint32_t a0, uint32_t a1, + uint32_t a2, uint32_t a3) { + return Average2(Average2(a0, a1), Average2(a2, a3)); +} + +static WEBP_INLINE uint32_t Clip255(uint32_t a) { + if (a < 256) { + return a; + } + // return 0, when a is a negative integer. + // return 255, when a is positive. + return ~a >> 24; +} + +static WEBP_INLINE int AddSubtractComponentFull(int a, int b, int c) { + return Clip255(a + b - c); +} + +static WEBP_INLINE uint32_t ClampedAddSubtractFull(uint32_t c0, uint32_t c1, + uint32_t c2) { + const int a = AddSubtractComponentFull(c0 >> 24, c1 >> 24, c2 >> 24); + const int r = AddSubtractComponentFull((c0 >> 16) & 0xff, + (c1 >> 16) & 0xff, + (c2 >> 16) & 0xff); + const int g = AddSubtractComponentFull((c0 >> 8) & 0xff, + (c1 >> 8) & 0xff, + (c2 >> 8) & 0xff); + const int b = AddSubtractComponentFull(c0 & 0xff, c1 & 0xff, c2 & 0xff); + return ((uint32_t)a << 24) | (r << 16) | (g << 8) | b; +} + +static WEBP_INLINE int AddSubtractComponentHalf(int a, int b) { + return Clip255(a + (a - b) / 2); +} + +static WEBP_INLINE uint32_t ClampedAddSubtractHalf(uint32_t c0, uint32_t c1, + uint32_t c2) { + const uint32_t ave = Average2(c0, c1); + const int a = AddSubtractComponentHalf(ave >> 24, c2 >> 24); + const int r = AddSubtractComponentHalf((ave >> 16) & 0xff, (c2 >> 16) & 0xff); + const int g = AddSubtractComponentHalf((ave >> 8) & 0xff, (c2 >> 8) & 0xff); + const int b = AddSubtractComponentHalf((ave >> 0) & 0xff, (c2 >> 0) & 0xff); + return ((uint32_t)a << 24) | (r << 16) | (g << 8) | b; +} + +// gcc-4.9 on ARM generates incorrect code in Select() when Sub3() is inlined. +#if defined(__arm__) && \ + (LOCAL_GCC_VERSION == 0x409 || LOCAL_GCC_VERSION == 0x408) +# define LOCAL_INLINE __attribute__ ((noinline)) +#else +# define LOCAL_INLINE WEBP_INLINE +#endif + +static LOCAL_INLINE int Sub3(int a, int b, int c) { + const int pb = b - c; + const int pa = a - c; + return abs(pb) - abs(pa); +} + +#undef LOCAL_INLINE + +static WEBP_INLINE uint32_t Select(uint32_t a, uint32_t b, uint32_t c) { + const int pa_minus_pb = + Sub3((a >> 24) , (b >> 24) , (c >> 24) ) + + Sub3((a >> 16) & 0xff, (b >> 16) & 0xff, (c >> 16) & 0xff) + + Sub3((a >> 8) & 0xff, (b >> 8) & 0xff, (c >> 8) & 0xff) + + Sub3((a ) & 0xff, (b ) & 0xff, (c ) & 0xff); + return (pa_minus_pb <= 0) ? a : b; +} + +//------------------------------------------------------------------------------ +// Predictors + +static uint32_t Predictor2(uint32_t left, const uint32_t* const top) { + (void)left; + return top[0]; +} +static uint32_t Predictor3(uint32_t left, const uint32_t* const top) { + (void)left; + return top[1]; +} +static uint32_t Predictor4(uint32_t left, const uint32_t* const top) { + (void)left; + return top[-1]; +} +static uint32_t Predictor5(uint32_t left, const uint32_t* const top) { + const uint32_t pred = Average3(left, top[0], top[1]); + return pred; +} +static uint32_t Predictor6(uint32_t left, const uint32_t* const top) { + const uint32_t pred = Average2(left, top[-1]); + return pred; +} +static uint32_t Predictor7(uint32_t left, const uint32_t* const top) { + const uint32_t pred = Average2(left, top[0]); + return pred; +} +static uint32_t Predictor8(uint32_t left, const uint32_t* const top) { + const uint32_t pred = Average2(top[-1], top[0]); + (void)left; + return pred; +} +static uint32_t Predictor9(uint32_t left, const uint32_t* const top) { + const uint32_t pred = Average2(top[0], top[1]); + (void)left; + return pred; +} +static uint32_t Predictor10(uint32_t left, const uint32_t* const top) { + const uint32_t pred = Average4(left, top[-1], top[0], top[1]); + return pred; +} +static uint32_t Predictor11(uint32_t left, const uint32_t* const top) { + const uint32_t pred = Select(top[0], left, top[-1]); + return pred; +} +static uint32_t Predictor12(uint32_t left, const uint32_t* const top) { + const uint32_t pred = ClampedAddSubtractFull(left, top[0], top[-1]); + return pred; +} +static uint32_t Predictor13(uint32_t left, const uint32_t* const top) { + const uint32_t pred = ClampedAddSubtractHalf(left, top[0], top[-1]); + return pred; +} + +//------------------------------------------------------------------------------ + +static void PredictorSub0_C(const uint32_t* in, const uint32_t* upper, + int num_pixels, uint32_t* out) { + int i; + for (i = 0; i < num_pixels; ++i) out[i] = VP8LSubPixels(in[i], ARGB_BLACK); + (void)upper; +} + +static void PredictorSub1_C(const uint32_t* in, const uint32_t* upper, + int num_pixels, uint32_t* out) { + int i; + for (i = 0; i < num_pixels; ++i) out[i] = VP8LSubPixels(in[i], in[i - 1]); + (void)upper; +} + +GENERATE_PREDICTOR_SUB(Predictor2, PredictorSub2_C) +GENERATE_PREDICTOR_SUB(Predictor3, PredictorSub3_C) +GENERATE_PREDICTOR_SUB(Predictor4, PredictorSub4_C) +GENERATE_PREDICTOR_SUB(Predictor5, PredictorSub5_C) +GENERATE_PREDICTOR_SUB(Predictor6, PredictorSub6_C) +GENERATE_PREDICTOR_SUB(Predictor7, PredictorSub7_C) +GENERATE_PREDICTOR_SUB(Predictor8, PredictorSub8_C) +GENERATE_PREDICTOR_SUB(Predictor9, PredictorSub9_C) +GENERATE_PREDICTOR_SUB(Predictor10, PredictorSub10_C) +GENERATE_PREDICTOR_SUB(Predictor11, PredictorSub11_C) +GENERATE_PREDICTOR_SUB(Predictor12, PredictorSub12_C) +GENERATE_PREDICTOR_SUB(Predictor13, PredictorSub13_C) + +//------------------------------------------------------------------------------ + +VP8LProcessEncBlueAndRedFunc VP8LSubtractGreenFromBlueAndRed; VP8LTransformColorFunc VP8LTransformColor; @@ -1365,17 +843,23 @@ VP8LCostFunc VP8LExtraCost; VP8LCostCombinedFunc VP8LExtraCostCombined; VP8LCombinedShannonEntropyFunc VP8LCombinedShannonEntropy; -GetEntropyUnrefinedHelperFunc VP8LGetEntropyUnrefinedHelper; +VP8LGetEntropyUnrefinedFunc VP8LGetEntropyUnrefined; +VP8LGetCombinedEntropyUnrefinedFunc VP8LGetCombinedEntropyUnrefined; VP8LHistogramAddFunc VP8LHistogramAdd; VP8LVectorMismatchFunc VP8LVectorMismatch; +VP8LBundleColorMapFunc VP8LBundleColorMap; + +VP8LPredictorAddSubFunc VP8LPredictorsSub[16]; +VP8LPredictorAddSubFunc VP8LPredictorsSub_C[16]; extern void VP8LEncDspInitSSE2(void); extern void VP8LEncDspInitSSE41(void); extern void VP8LEncDspInitNEON(void); extern void VP8LEncDspInitMIPS32(void); extern void VP8LEncDspInitMIPSdspR2(void); +extern void VP8LEncDspInitMSA(void); static volatile VP8CPUInfo lossless_enc_last_cpuinfo_used = (VP8CPUInfo)&lossless_enc_last_cpuinfo_used; @@ -1399,11 +883,47 @@ WEBP_TSAN_IGNORE_FUNCTION void VP8LEncDspInit(void) { VP8LExtraCostCombined = ExtraCostCombined; VP8LCombinedShannonEntropy = CombinedShannonEntropy; - VP8LGetEntropyUnrefinedHelper = GetEntropyUnrefinedHelper; + VP8LGetEntropyUnrefined = GetEntropyUnrefined; + VP8LGetCombinedEntropyUnrefined = GetCombinedEntropyUnrefined; VP8LHistogramAdd = HistogramAdd; VP8LVectorMismatch = VectorMismatch; + VP8LBundleColorMap = VP8LBundleColorMap_C; + + VP8LPredictorsSub[0] = PredictorSub0_C; + VP8LPredictorsSub[1] = PredictorSub1_C; + VP8LPredictorsSub[2] = PredictorSub2_C; + VP8LPredictorsSub[3] = PredictorSub3_C; + VP8LPredictorsSub[4] = PredictorSub4_C; + VP8LPredictorsSub[5] = PredictorSub5_C; + VP8LPredictorsSub[6] = PredictorSub6_C; + VP8LPredictorsSub[7] = PredictorSub7_C; + VP8LPredictorsSub[8] = PredictorSub8_C; + VP8LPredictorsSub[9] = PredictorSub9_C; + VP8LPredictorsSub[10] = PredictorSub10_C; + VP8LPredictorsSub[11] = PredictorSub11_C; + VP8LPredictorsSub[12] = PredictorSub12_C; + VP8LPredictorsSub[13] = PredictorSub13_C; + VP8LPredictorsSub[14] = PredictorSub0_C; // <- padding security sentinels + VP8LPredictorsSub[15] = PredictorSub0_C; + + VP8LPredictorsSub_C[0] = PredictorSub0_C; + VP8LPredictorsSub_C[1] = PredictorSub1_C; + VP8LPredictorsSub_C[2] = PredictorSub2_C; + VP8LPredictorsSub_C[3] = PredictorSub3_C; + VP8LPredictorsSub_C[4] = PredictorSub4_C; + VP8LPredictorsSub_C[5] = PredictorSub5_C; + VP8LPredictorsSub_C[6] = PredictorSub6_C; + VP8LPredictorsSub_C[7] = PredictorSub7_C; + VP8LPredictorsSub_C[8] = PredictorSub8_C; + VP8LPredictorsSub_C[9] = PredictorSub9_C; + VP8LPredictorsSub_C[10] = PredictorSub10_C; + VP8LPredictorsSub_C[11] = PredictorSub11_C; + VP8LPredictorsSub_C[12] = PredictorSub12_C; + VP8LPredictorsSub_C[13] = PredictorSub13_C; + VP8LPredictorsSub_C[14] = PredictorSub0_C; // <- padding security sentinels + VP8LPredictorsSub_C[15] = PredictorSub0_C; // If defined, use CPUInfo() to overwrite some pointers with faster versions. if (VP8GetCPUInfo != NULL) { @@ -1431,6 +951,11 @@ WEBP_TSAN_IGNORE_FUNCTION void VP8LEncDspInit(void) { if (VP8GetCPUInfo(kMIPSdspR2)) { VP8LEncDspInitMIPSdspR2(); } +#endif +#if defined(WEBP_USE_MSA) + if (VP8GetCPUInfo(kMSA)) { + VP8LEncDspInitMSA(); + } #endif } lossless_enc_last_cpuinfo_used = VP8GetCPUInfo; diff --git a/thirdparty/libwebp/dsp/lossless_enc_mips32.c b/thirdparty/libwebp/dsp/lossless_enc_mips32.c index 49c666d4fd..4186b9f50d 100644 --- a/thirdparty/libwebp/dsp/lossless_enc_mips32.c +++ b/thirdparty/libwebp/dsp/lossless_enc_mips32.c @@ -14,6 +14,7 @@ #include "./dsp.h" #include "./lossless.h" +#include "./lossless_common.h" #if defined(WEBP_USE_MIPS32) @@ -240,6 +241,49 @@ static WEBP_INLINE void GetEntropyUnrefinedHelper( *i_prev = i; } +static void GetEntropyUnrefined(const uint32_t X[], int length, + VP8LBitEntropy* const bit_entropy, + VP8LStreaks* const stats) { + int i; + int i_prev = 0; + uint32_t x_prev = X[0]; + + memset(stats, 0, sizeof(*stats)); + VP8LBitEntropyInit(bit_entropy); + + for (i = 1; i < length; ++i) { + const uint32_t x = X[i]; + if (x != x_prev) { + GetEntropyUnrefinedHelper(x, i, &x_prev, &i_prev, bit_entropy, stats); + } + } + GetEntropyUnrefinedHelper(0, i, &x_prev, &i_prev, bit_entropy, stats); + + bit_entropy->entropy += VP8LFastSLog2(bit_entropy->sum); +} + +static void GetCombinedEntropyUnrefined(const uint32_t X[], const uint32_t Y[], + int length, + VP8LBitEntropy* const bit_entropy, + VP8LStreaks* const stats) { + int i = 1; + int i_prev = 0; + uint32_t xy_prev = X[0] + Y[0]; + + memset(stats, 0, sizeof(*stats)); + VP8LBitEntropyInit(bit_entropy); + + for (i = 1; i < length; ++i) { + const uint32_t xy = X[i] + Y[i]; + if (xy != xy_prev) { + GetEntropyUnrefinedHelper(xy, i, &xy_prev, &i_prev, bit_entropy, stats); + } + } + GetEntropyUnrefinedHelper(0, i, &xy_prev, &i_prev, bit_entropy, stats); + + bit_entropy->entropy += VP8LFastSLog2(bit_entropy->sum); +} + #define ASM_START \ __asm__ volatile( \ ".set push \n\t" \ @@ -375,7 +419,8 @@ WEBP_TSAN_IGNORE_FUNCTION void VP8LEncDspInitMIPS32(void) { VP8LFastLog2Slow = FastLog2Slow; VP8LExtraCost = ExtraCost; VP8LExtraCostCombined = ExtraCostCombined; - VP8LGetEntropyUnrefinedHelper = GetEntropyUnrefinedHelper; + VP8LGetEntropyUnrefined = GetEntropyUnrefined; + VP8LGetCombinedEntropyUnrefined = GetCombinedEntropyUnrefined; VP8LHistogramAdd = HistogramAdd; } diff --git a/thirdparty/libwebp/dsp/lossless_enc_msa.c b/thirdparty/libwebp/dsp/lossless_enc_msa.c new file mode 100644 index 0000000000..2f69ba3bca --- /dev/null +++ b/thirdparty/libwebp/dsp/lossless_enc_msa.c @@ -0,0 +1,147 @@ +// Copyright 2016 Google Inc. All Rights Reserved. +// +// Use of this source code is governed by a BSD-style license +// that can be found in the COPYING file in the root of the source +// tree. An additional intellectual property rights grant can be found +// in the file PATENTS. All contributing project authors may +// be found in the AUTHORS file in the root of the source tree. +// ----------------------------------------------------------------------------- +// +// MSA variant of Image transform methods for lossless encoder. +// +// Authors: Prashant Patil (Prashant.Patil@imgtec.com) + +#include "./dsp.h" + +#if defined(WEBP_USE_MSA) + +#include "./lossless.h" +#include "./msa_macro.h" + +#define TRANSFORM_COLOR_8(src0, src1, dst0, dst1, c0, c1, mask0, mask1) do { \ + v8i16 g0, g1, t0, t1, t2, t3; \ + v4i32 t4, t5; \ + VSHF_B2_SH(src0, src0, src1, src1, mask0, mask0, g0, g1); \ + DOTP_SB2_SH(g0, g1, c0, c0, t0, t1); \ + SRAI_H2_SH(t0, t1, 5); \ + t0 = __msa_subv_h((v8i16)src0, t0); \ + t1 = __msa_subv_h((v8i16)src1, t1); \ + t4 = __msa_srli_w((v4i32)src0, 16); \ + t5 = __msa_srli_w((v4i32)src1, 16); \ + DOTP_SB2_SH(t4, t5, c1, c1, t2, t3); \ + SRAI_H2_SH(t2, t3, 5); \ + SUB2(t0, t2, t1, t3, t0, t1); \ + VSHF_B2_UB(src0, t0, src1, t1, mask1, mask1, dst0, dst1); \ +} while (0) + +#define TRANSFORM_COLOR_4(src, dst, c0, c1, mask0, mask1) do { \ + const v16i8 g0 = VSHF_SB(src, src, mask0); \ + v8i16 t0 = __msa_dotp_s_h(c0, g0); \ + v8i16 t1; \ + v4i32 t2; \ + t0 = SRAI_H(t0, 5); \ + t0 = __msa_subv_h((v8i16)src, t0); \ + t2 = __msa_srli_w((v4i32)src, 16); \ + t1 = __msa_dotp_s_h(c1, (v16i8)t2); \ + t1 = SRAI_H(t1, 5); \ + t0 = t0 - t1; \ + dst = VSHF_UB(src, t0, mask1); \ +} while (0) + +static void TransformColor(const VP8LMultipliers* const m, uint32_t* data, + int num_pixels) { + v16u8 src0, dst0; + const v16i8 g2br = (v16i8)__msa_fill_w(m->green_to_blue_ | + (m->green_to_red_ << 16)); + const v16i8 r2b = (v16i8)__msa_fill_w(m->red_to_blue_); + const v16u8 mask0 = { 1, 255, 1, 255, 5, 255, 5, 255, 9, 255, 9, 255, + 13, 255, 13, 255 }; + const v16u8 mask1 = { 16, 1, 18, 3, 20, 5, 22, 7, 24, 9, 26, 11, + 28, 13, 30, 15 }; + + while (num_pixels >= 8) { + v16u8 src1, dst1; + LD_UB2(data, 4, src0, src1); + TRANSFORM_COLOR_8(src0, src1, dst0, dst1, g2br, r2b, mask0, mask1); + ST_UB2(dst0, dst1, data, 4); + data += 8; + num_pixels -= 8; + } + if (num_pixels > 0) { + if (num_pixels >= 4) { + src0 = LD_UB(data); + TRANSFORM_COLOR_4(src0, dst0, g2br, r2b, mask0, mask1); + ST_UB(dst0, data); + data += 4; + num_pixels -= 4; + } + if (num_pixels > 0) { + src0 = LD_UB(data); + TRANSFORM_COLOR_4(src0, dst0, g2br, r2b, mask0, mask1); + if (num_pixels == 3) { + const uint64_t pix_d = __msa_copy_s_d((v2i64)dst0, 0); + const uint32_t pix_w = __msa_copy_s_w((v4i32)dst0, 2); + SD(pix_d, data + 0); + SW(pix_w, data + 2); + } else if (num_pixels == 2) { + const uint64_t pix_d = __msa_copy_s_d((v2i64)dst0, 0); + SD(pix_d, data); + } else { + const uint32_t pix_w = __msa_copy_s_w((v4i32)dst0, 0); + SW(pix_w, data); + } + } + } +} + +static void SubtractGreenFromBlueAndRed(uint32_t* argb_data, int num_pixels) { + int i; + uint8_t* ptemp_data = (uint8_t*)argb_data; + v16u8 src0, dst0, tmp0; + const v16u8 mask = { 1, 255, 1, 255, 5, 255, 5, 255, 9, 255, 9, 255, + 13, 255, 13, 255 }; + + while (num_pixels >= 8) { + v16u8 src1, dst1, tmp1; + LD_UB2(ptemp_data, 16, src0, src1); + VSHF_B2_UB(src0, src1, src1, src0, mask, mask, tmp0, tmp1); + SUB2(src0, tmp0, src1, tmp1, dst0, dst1); + ST_UB2(dst0, dst1, ptemp_data, 16); + ptemp_data += 8 * 4; + num_pixels -= 8; + } + if (num_pixels > 0) { + if (num_pixels >= 4) { + src0 = LD_UB(ptemp_data); + tmp0 = VSHF_UB(src0, src0, mask); + dst0 = src0 - tmp0; + ST_UB(dst0, ptemp_data); + ptemp_data += 4 * 4; + num_pixels -= 4; + } + for (i = 0; i < num_pixels; i++) { + const uint8_t b = ptemp_data[0]; + const uint8_t g = ptemp_data[1]; + const uint8_t r = ptemp_data[2]; + ptemp_data[0] = (b - g) & 0xff; + ptemp_data[2] = (r - g) & 0xff; + ptemp_data += 4; + } + } +} + +//------------------------------------------------------------------------------ +// Entry point + +extern void VP8LEncDspInitMSA(void); + +WEBP_TSAN_IGNORE_FUNCTION void VP8LEncDspInitMSA(void) { + VP8LSubtractGreenFromBlueAndRed = SubtractGreenFromBlueAndRed; + VP8LTransformColor = TransformColor; +} + +#else // !WEBP_USE_MSA + +WEBP_DSP_INIT_STUB(VP8LEncDspInitMSA) + +#endif // WEBP_USE_MSA diff --git a/thirdparty/libwebp/dsp/lossless_enc_sse2.c b/thirdparty/libwebp/dsp/lossless_enc_sse2.c index 7c894e7ca4..8ad85d94d7 100644 --- a/thirdparty/libwebp/dsp/lossless_enc_sse2.c +++ b/thirdparty/libwebp/dsp/lossless_enc_sse2.c @@ -17,6 +17,8 @@ #include #include #include "./lossless.h" +#include "./common_sse2.h" +#include "./lossless_common.h" // For sign-extended multiplying constants, pre-shifted by 5: #define CST_5b(X) (((int16_t)((uint16_t)X << 8)) >> 5) @@ -35,7 +37,9 @@ static void SubtractGreenFromBlueAndRed(uint32_t* argb_data, int num_pixels) { _mm_storeu_si128((__m128i*)&argb_data[i], out); } // fallthrough and finish off with plain-C - VP8LSubtractGreenFromBlueAndRed_C(argb_data + i, num_pixels - i); + if (i != num_pixels) { + VP8LSubtractGreenFromBlueAndRed_C(argb_data + i, num_pixels - i); + } } //------------------------------------------------------------------------------ @@ -69,7 +73,9 @@ static void TransformColor(const VP8LMultipliers* const m, _mm_storeu_si128((__m128i*)&argb_data[i], out); } // fallthrough and finish off with plain-C - VP8LTransformColor_C(m, argb_data + i, num_pixels - i); + if (i != num_pixels) { + VP8LTransformColor_C(m, argb_data + i, num_pixels - i); + } } //------------------------------------------------------------------------------ @@ -364,8 +370,9 @@ static int VectorMismatch(const uint32_t* const array1, if (length >= 8 && _mm_movemask_epi8(_mm_cmpeq_epi32( _mm_loadu_si128((const __m128i*)&array1[4]), - _mm_loadu_si128((const __m128i*)&array2[4]))) == 0xffff) + _mm_loadu_si128((const __m128i*)&array2[4]))) == 0xffff) { match_len = 8; + } } } @@ -375,6 +382,295 @@ static int VectorMismatch(const uint32_t* const array1, return match_len; } +// Bundles multiple (1, 2, 4 or 8) pixels into a single pixel. +static void BundleColorMap_SSE2(const uint8_t* const row, int width, int xbits, + uint32_t* dst) { + int x; + assert(xbits >= 0); + assert(xbits <= 3); + switch (xbits) { + case 0: { + const __m128i ff = _mm_set1_epi16(0xff00); + const __m128i zero = _mm_setzero_si128(); + // Store 0xff000000 | (row[x] << 8). + for (x = 0; x + 16 <= width; x += 16, dst += 16) { + const __m128i in = _mm_loadu_si128((const __m128i*)&row[x]); + const __m128i in_lo = _mm_unpacklo_epi8(zero, in); + const __m128i dst0 = _mm_unpacklo_epi16(in_lo, ff); + const __m128i dst1 = _mm_unpackhi_epi16(in_lo, ff); + const __m128i in_hi = _mm_unpackhi_epi8(zero, in); + const __m128i dst2 = _mm_unpacklo_epi16(in_hi, ff); + const __m128i dst3 = _mm_unpackhi_epi16(in_hi, ff); + _mm_storeu_si128((__m128i*)&dst[0], dst0); + _mm_storeu_si128((__m128i*)&dst[4], dst1); + _mm_storeu_si128((__m128i*)&dst[8], dst2); + _mm_storeu_si128((__m128i*)&dst[12], dst3); + } + break; + } + case 1: { + const __m128i ff = _mm_set1_epi16(0xff00); + const __m128i mul = _mm_set1_epi16(0x110); + for (x = 0; x + 16 <= width; x += 16, dst += 8) { + // 0a0b | (where a/b are 4 bits). + const __m128i in = _mm_loadu_si128((const __m128i*)&row[x]); + const __m128i tmp = _mm_mullo_epi16(in, mul); // aba0 + const __m128i pack = _mm_and_si128(tmp, ff); // ab00 + const __m128i dst0 = _mm_unpacklo_epi16(pack, ff); + const __m128i dst1 = _mm_unpackhi_epi16(pack, ff); + _mm_storeu_si128((__m128i*)&dst[0], dst0); + _mm_storeu_si128((__m128i*)&dst[4], dst1); + } + break; + } + case 2: { + const __m128i mask_or = _mm_set1_epi32(0xff000000); + const __m128i mul_cst = _mm_set1_epi16(0x0104); + const __m128i mask_mul = _mm_set1_epi16(0x0f00); + for (x = 0; x + 16 <= width; x += 16, dst += 4) { + // 000a000b000c000d | (where a/b/c/d are 2 bits). + const __m128i in = _mm_loadu_si128((const __m128i*)&row[x]); + const __m128i mul = _mm_mullo_epi16(in, mul_cst); // 00ab00b000cd00d0 + const __m128i tmp = _mm_and_si128(mul, mask_mul); // 00ab000000cd0000 + const __m128i shift = _mm_srli_epi32(tmp, 12); // 00000000ab000000 + const __m128i pack = _mm_or_si128(shift, tmp); // 00000000abcd0000 + // Convert to 0xff00**00. + const __m128i res = _mm_or_si128(pack, mask_or); + _mm_storeu_si128((__m128i*)dst, res); + } + break; + } + default: { + assert(xbits == 3); + for (x = 0; x + 16 <= width; x += 16, dst += 2) { + // 0000000a00000000b... | (where a/b are 1 bit). + const __m128i in = _mm_loadu_si128((const __m128i*)&row[x]); + const __m128i shift = _mm_slli_epi64(in, 7); + const uint32_t move = _mm_movemask_epi8(shift); + dst[0] = 0xff000000 | ((move & 0xff) << 8); + dst[1] = 0xff000000 | (move & 0xff00); + } + break; + } + } + if (x != width) { + VP8LBundleColorMap_C(row + x, width - x, xbits, dst); + } +} + +//------------------------------------------------------------------------------ +// Batch version of Predictor Transform subtraction + +static WEBP_INLINE void Average2_m128i(const __m128i* const a0, + const __m128i* const a1, + __m128i* const avg) { + // (a + b) >> 1 = ((a + b + 1) >> 1) - ((a ^ b) & 1) + const __m128i ones = _mm_set1_epi8(1); + const __m128i avg1 = _mm_avg_epu8(*a0, *a1); + const __m128i one = _mm_and_si128(_mm_xor_si128(*a0, *a1), ones); + *avg = _mm_sub_epi8(avg1, one); +} + +// Predictor0: ARGB_BLACK. +static void PredictorSub0_SSE2(const uint32_t* in, const uint32_t* upper, + int num_pixels, uint32_t* out) { + int i; + const __m128i black = _mm_set1_epi32(ARGB_BLACK); + for (i = 0; i + 4 <= num_pixels; i += 4) { + const __m128i src = _mm_loadu_si128((const __m128i*)&in[i]); + const __m128i res = _mm_sub_epi8(src, black); + _mm_storeu_si128((__m128i*)&out[i], res); + } + if (i != num_pixels) { + VP8LPredictorsSub_C[0](in + i, upper + i, num_pixels - i, out + i); + } +} + +#define GENERATE_PREDICTOR_1(X, IN) \ +static void PredictorSub##X##_SSE2(const uint32_t* in, const uint32_t* upper, \ + int num_pixels, uint32_t* out) { \ + int i; \ + for (i = 0; i + 4 <= num_pixels; i += 4) { \ + const __m128i src = _mm_loadu_si128((const __m128i*)&in[i]); \ + const __m128i pred = _mm_loadu_si128((const __m128i*)&(IN)); \ + const __m128i res = _mm_sub_epi8(src, pred); \ + _mm_storeu_si128((__m128i*)&out[i], res); \ + } \ + if (i != num_pixels) { \ + VP8LPredictorsSub_C[(X)](in + i, upper + i, num_pixels - i, out + i); \ + } \ +} + +GENERATE_PREDICTOR_1(1, in[i - 1]) // Predictor1: L +GENERATE_PREDICTOR_1(2, upper[i]) // Predictor2: T +GENERATE_PREDICTOR_1(3, upper[i + 1]) // Predictor3: TR +GENERATE_PREDICTOR_1(4, upper[i - 1]) // Predictor4: TL +#undef GENERATE_PREDICTOR_1 + +// Predictor5: avg2(avg2(L, TR), T) +static void PredictorSub5_SSE2(const uint32_t* in, const uint32_t* upper, + int num_pixels, uint32_t* out) { + int i; + for (i = 0; i + 4 <= num_pixels; i += 4) { + const __m128i L = _mm_loadu_si128((const __m128i*)&in[i - 1]); + const __m128i T = _mm_loadu_si128((const __m128i*)&upper[i]); + const __m128i TR = _mm_loadu_si128((const __m128i*)&upper[i + 1]); + const __m128i src = _mm_loadu_si128((const __m128i*)&in[i]); + __m128i avg, pred, res; + Average2_m128i(&L, &TR, &avg); + Average2_m128i(&avg, &T, &pred); + res = _mm_sub_epi8(src, pred); + _mm_storeu_si128((__m128i*)&out[i], res); + } + if (i != num_pixels) { + VP8LPredictorsSub_C[5](in + i, upper + i, num_pixels - i, out + i); + } +} + +#define GENERATE_PREDICTOR_2(X, A, B) \ +static void PredictorSub##X##_SSE2(const uint32_t* in, const uint32_t* upper, \ + int num_pixels, uint32_t* out) { \ + int i; \ + for (i = 0; i + 4 <= num_pixels; i += 4) { \ + const __m128i tA = _mm_loadu_si128((const __m128i*)&(A)); \ + const __m128i tB = _mm_loadu_si128((const __m128i*)&(B)); \ + const __m128i src = _mm_loadu_si128((const __m128i*)&in[i]); \ + __m128i pred, res; \ + Average2_m128i(&tA, &tB, &pred); \ + res = _mm_sub_epi8(src, pred); \ + _mm_storeu_si128((__m128i*)&out[i], res); \ + } \ + if (i != num_pixels) { \ + VP8LPredictorsSub_C[(X)](in + i, upper + i, num_pixels - i, out + i); \ + } \ +} + +GENERATE_PREDICTOR_2(6, in[i - 1], upper[i - 1]) // Predictor6: avg(L, TL) +GENERATE_PREDICTOR_2(7, in[i - 1], upper[i]) // Predictor7: avg(L, T) +GENERATE_PREDICTOR_2(8, upper[i - 1], upper[i]) // Predictor8: avg(TL, T) +GENERATE_PREDICTOR_2(9, upper[i], upper[i + 1]) // Predictor9: average(T, TR) +#undef GENERATE_PREDICTOR_2 + +// Predictor10: avg(avg(L,TL), avg(T, TR)). +static void PredictorSub10_SSE2(const uint32_t* in, const uint32_t* upper, + int num_pixels, uint32_t* out) { + int i; + for (i = 0; i + 4 <= num_pixels; i += 4) { + const __m128i L = _mm_loadu_si128((const __m128i*)&in[i - 1]); + const __m128i src = _mm_loadu_si128((const __m128i*)&in[i]); + const __m128i TL = _mm_loadu_si128((const __m128i*)&upper[i - 1]); + const __m128i T = _mm_loadu_si128((const __m128i*)&upper[i]); + const __m128i TR = _mm_loadu_si128((const __m128i*)&upper[i + 1]); + __m128i avgTTR, avgLTL, avg, res; + Average2_m128i(&T, &TR, &avgTTR); + Average2_m128i(&L, &TL, &avgLTL); + Average2_m128i(&avgTTR, &avgLTL, &avg); + res = _mm_sub_epi8(src, avg); + _mm_storeu_si128((__m128i*)&out[i], res); + } + if (i != num_pixels) { + VP8LPredictorsSub_C[10](in + i, upper + i, num_pixels - i, out + i); + } +} + +// Predictor11: select. +static void GetSumAbsDiff32(const __m128i* const A, const __m128i* const B, + __m128i* const out) { + // We can unpack with any value on the upper 32 bits, provided it's the same + // on both operands (to that their sum of abs diff is zero). Here we use *A. + const __m128i A_lo = _mm_unpacklo_epi32(*A, *A); + const __m128i B_lo = _mm_unpacklo_epi32(*B, *A); + const __m128i A_hi = _mm_unpackhi_epi32(*A, *A); + const __m128i B_hi = _mm_unpackhi_epi32(*B, *A); + const __m128i s_lo = _mm_sad_epu8(A_lo, B_lo); + const __m128i s_hi = _mm_sad_epu8(A_hi, B_hi); + *out = _mm_packs_epi32(s_lo, s_hi); +} + +static void PredictorSub11_SSE2(const uint32_t* in, const uint32_t* upper, + int num_pixels, uint32_t* out) { + int i; + for (i = 0; i + 4 <= num_pixels; i += 4) { + const __m128i L = _mm_loadu_si128((const __m128i*)&in[i - 1]); + const __m128i T = _mm_loadu_si128((const __m128i*)&upper[i]); + const __m128i TL = _mm_loadu_si128((const __m128i*)&upper[i - 1]); + const __m128i src = _mm_loadu_si128((const __m128i*)&in[i]); + __m128i pa, pb; + GetSumAbsDiff32(&T, &TL, &pa); // pa = sum |T-TL| + GetSumAbsDiff32(&L, &TL, &pb); // pb = sum |L-TL| + { + const __m128i mask = _mm_cmpgt_epi32(pb, pa); + const __m128i A = _mm_and_si128(mask, L); + const __m128i B = _mm_andnot_si128(mask, T); + const __m128i pred = _mm_or_si128(A, B); // pred = (L > T)? L : T + const __m128i res = _mm_sub_epi8(src, pred); + _mm_storeu_si128((__m128i*)&out[i], res); + } + } + if (i != num_pixels) { + VP8LPredictorsSub_C[11](in + i, upper + i, num_pixels - i, out + i); + } +} + +// Predictor12: ClampedSubSubtractFull. +static void PredictorSub12_SSE2(const uint32_t* in, const uint32_t* upper, + int num_pixels, uint32_t* out) { + int i; + const __m128i zero = _mm_setzero_si128(); + for (i = 0; i + 4 <= num_pixels; i += 4) { + const __m128i src = _mm_loadu_si128((const __m128i*)&in[i]); + const __m128i L = _mm_loadu_si128((const __m128i*)&in[i - 1]); + const __m128i L_lo = _mm_unpacklo_epi8(L, zero); + const __m128i L_hi = _mm_unpackhi_epi8(L, zero); + const __m128i T = _mm_loadu_si128((const __m128i*)&upper[i]); + const __m128i T_lo = _mm_unpacklo_epi8(T, zero); + const __m128i T_hi = _mm_unpackhi_epi8(T, zero); + const __m128i TL = _mm_loadu_si128((const __m128i*)&upper[i - 1]); + const __m128i TL_lo = _mm_unpacklo_epi8(TL, zero); + const __m128i TL_hi = _mm_unpackhi_epi8(TL, zero); + const __m128i diff_lo = _mm_sub_epi16(T_lo, TL_lo); + const __m128i diff_hi = _mm_sub_epi16(T_hi, TL_hi); + const __m128i pred_lo = _mm_add_epi16(L_lo, diff_lo); + const __m128i pred_hi = _mm_add_epi16(L_hi, diff_hi); + const __m128i pred = _mm_packus_epi16(pred_lo, pred_hi); + const __m128i res = _mm_sub_epi8(src, pred); + _mm_storeu_si128((__m128i*)&out[i], res); + } + if (i != num_pixels) { + VP8LPredictorsSub_C[12](in + i, upper + i, num_pixels - i, out + i); + } +} + +// Predictors13: ClampedAddSubtractHalf +static void PredictorSub13_SSE2(const uint32_t* in, const uint32_t* upper, + int num_pixels, uint32_t* out) { + int i; + const __m128i zero = _mm_setzero_si128(); + for (i = 0; i + 2 <= num_pixels; i += 2) { + // we can only process two pixels at a time + const __m128i L = _mm_loadl_epi64((const __m128i*)&in[i - 1]); + const __m128i src = _mm_loadl_epi64((const __m128i*)&in[i]); + const __m128i T = _mm_loadl_epi64((const __m128i*)&upper[i]); + const __m128i TL = _mm_loadl_epi64((const __m128i*)&upper[i - 1]); + const __m128i L_lo = _mm_unpacklo_epi8(L, zero); + const __m128i T_lo = _mm_unpacklo_epi8(T, zero); + const __m128i TL_lo = _mm_unpacklo_epi8(TL, zero); + const __m128i sum = _mm_add_epi16(T_lo, L_lo); + const __m128i avg = _mm_srli_epi16(sum, 1); + const __m128i A1 = _mm_sub_epi16(avg, TL_lo); + const __m128i bit_fix = _mm_cmpgt_epi16(TL_lo, avg); + const __m128i A2 = _mm_sub_epi16(A1, bit_fix); + const __m128i A3 = _mm_srai_epi16(A2, 1); + const __m128i A4 = _mm_add_epi16(avg, A3); + const __m128i pred = _mm_packus_epi16(A4, A4); + const __m128i res = _mm_sub_epi8(src, pred); + _mm_storel_epi64((__m128i*)&out[i], res); + } + if (i != num_pixels) { + VP8LPredictorsSub_C[13](in + i, upper + i, num_pixels - i, out + i); + } +} + //------------------------------------------------------------------------------ // Entry point @@ -388,6 +684,24 @@ WEBP_TSAN_IGNORE_FUNCTION void VP8LEncDspInitSSE2(void) { VP8LHistogramAdd = HistogramAdd; VP8LCombinedShannonEntropy = CombinedShannonEntropy; VP8LVectorMismatch = VectorMismatch; + VP8LBundleColorMap = BundleColorMap_SSE2; + + VP8LPredictorsSub[0] = PredictorSub0_SSE2; + VP8LPredictorsSub[1] = PredictorSub1_SSE2; + VP8LPredictorsSub[2] = PredictorSub2_SSE2; + VP8LPredictorsSub[3] = PredictorSub3_SSE2; + VP8LPredictorsSub[4] = PredictorSub4_SSE2; + VP8LPredictorsSub[5] = PredictorSub5_SSE2; + VP8LPredictorsSub[6] = PredictorSub6_SSE2; + VP8LPredictorsSub[7] = PredictorSub7_SSE2; + VP8LPredictorsSub[8] = PredictorSub8_SSE2; + VP8LPredictorsSub[9] = PredictorSub9_SSE2; + VP8LPredictorsSub[10] = PredictorSub10_SSE2; + VP8LPredictorsSub[11] = PredictorSub11_SSE2; + VP8LPredictorsSub[12] = PredictorSub12_SSE2; + VP8LPredictorsSub[13] = PredictorSub13_SSE2; + VP8LPredictorsSub[14] = PredictorSub0_SSE2; // <- padding security sentinels + VP8LPredictorsSub[15] = PredictorSub0_SSE2; } #else // !WEBP_USE_SSE2 diff --git a/thirdparty/libwebp/dsp/lossless_enc_sse41.c b/thirdparty/libwebp/dsp/lossless_enc_sse41.c index 3e493198db..821057ccd4 100644 --- a/thirdparty/libwebp/dsp/lossless_enc_sse41.c +++ b/thirdparty/libwebp/dsp/lossless_enc_sse41.c @@ -32,7 +32,9 @@ static void SubtractGreenFromBlueAndRed(uint32_t* argb_data, int num_pixels) { _mm_storeu_si128((__m128i*)&argb_data[i], out); } // fallthrough and finish off with plain-C - VP8LSubtractGreenFromBlueAndRed_C(argb_data + i, num_pixels - i); + if (i != num_pixels) { + VP8LSubtractGreenFromBlueAndRed_C(argb_data + i, num_pixels - i); + } } //------------------------------------------------------------------------------ diff --git a/thirdparty/libwebp/dsp/lossless_mips_dsp_r2.c b/thirdparty/libwebp/dsp/lossless_mips_dsp_r2.c index 90aed7f151..2984ce8df7 100644 --- a/thirdparty/libwebp/dsp/lossless_mips_dsp_r2.c +++ b/thirdparty/libwebp/dsp/lossless_mips_dsp_r2.c @@ -17,6 +17,7 @@ #if defined(WEBP_USE_MIPS_DSP_R2) #include "./lossless.h" +#include "./lossless_common.h" #define MAP_COLOR_FUNCS(FUNC_NAME, TYPE, GET_INDEX, GET_VALUE) \ static void FUNC_NAME(const TYPE* src, \ @@ -227,25 +228,27 @@ static uint32_t Predictor13(uint32_t left, const uint32_t* const top) { // Add green to blue and red channels (i.e. perform the inverse transform of // 'subtract green'). -static void AddGreenToBlueAndRed(uint32_t* data, int num_pixels) { +static void AddGreenToBlueAndRed(const uint32_t* src, int num_pixels, + uint32_t* dst) { uint32_t temp0, temp1, temp2, temp3, temp4, temp5, temp6, temp7; - uint32_t* const p_loop1_end = data + (num_pixels & ~3); - uint32_t* const p_loop2_end = data + num_pixels; + const uint32_t* const p_loop1_end = src + (num_pixels & ~3); + const uint32_t* const p_loop2_end = src + num_pixels; __asm__ volatile ( ".set push \n\t" ".set noreorder \n\t" - "beq %[data], %[p_loop1_end], 3f \n\t" + "beq %[src], %[p_loop1_end], 3f \n\t" " nop \n\t" "0: \n\t" - "lw %[temp0], 0(%[data]) \n\t" - "lw %[temp1], 4(%[data]) \n\t" - "lw %[temp2], 8(%[data]) \n\t" - "lw %[temp3], 12(%[data]) \n\t" + "lw %[temp0], 0(%[src]) \n\t" + "lw %[temp1], 4(%[src]) \n\t" + "lw %[temp2], 8(%[src]) \n\t" + "lw %[temp3], 12(%[src]) \n\t" "ext %[temp4], %[temp0], 8, 8 \n\t" "ext %[temp5], %[temp1], 8, 8 \n\t" "ext %[temp6], %[temp2], 8, 8 \n\t" "ext %[temp7], %[temp3], 8, 8 \n\t" - "addiu %[data], %[data], 16 \n\t" + "addiu %[src], %[src], 16 \n\t" + "addiu %[dst], %[dst], 16 \n\t" "replv.ph %[temp4], %[temp4] \n\t" "replv.ph %[temp5], %[temp5] \n\t" "replv.ph %[temp6], %[temp6] \n\t" @@ -254,44 +257,47 @@ static void AddGreenToBlueAndRed(uint32_t* data, int num_pixels) { "addu.qb %[temp1], %[temp1], %[temp5] \n\t" "addu.qb %[temp2], %[temp2], %[temp6] \n\t" "addu.qb %[temp3], %[temp3], %[temp7] \n\t" - "sw %[temp0], -16(%[data]) \n\t" - "sw %[temp1], -12(%[data]) \n\t" - "sw %[temp2], -8(%[data]) \n\t" - "bne %[data], %[p_loop1_end], 0b \n\t" - " sw %[temp3], -4(%[data]) \n\t" + "sw %[temp0], -16(%[dst]) \n\t" + "sw %[temp1], -12(%[dst]) \n\t" + "sw %[temp2], -8(%[dst]) \n\t" + "bne %[src], %[p_loop1_end], 0b \n\t" + " sw %[temp3], -4(%[dst]) \n\t" "3: \n\t" - "beq %[data], %[p_loop2_end], 2f \n\t" + "beq %[src], %[p_loop2_end], 2f \n\t" " nop \n\t" "1: \n\t" - "lw %[temp0], 0(%[data]) \n\t" - "addiu %[data], %[data], 4 \n\t" + "lw %[temp0], 0(%[src]) \n\t" + "addiu %[src], %[src], 4 \n\t" + "addiu %[dst], %[dst], 4 \n\t" "ext %[temp4], %[temp0], 8, 8 \n\t" "replv.ph %[temp4], %[temp4] \n\t" "addu.qb %[temp0], %[temp0], %[temp4] \n\t" - "bne %[data], %[p_loop2_end], 1b \n\t" - " sw %[temp0], -4(%[data]) \n\t" + "bne %[src], %[p_loop2_end], 1b \n\t" + " sw %[temp0], -4(%[dst]) \n\t" "2: \n\t" ".set pop \n\t" - : [data]"+&r"(data), [temp0]"=&r"(temp0), [temp1]"=&r"(temp1), - [temp2]"=&r"(temp2), [temp3]"=&r"(temp3), [temp4]"=&r"(temp4), - [temp5]"=&r"(temp5), [temp6]"=&r"(temp6), [temp7]"=&r"(temp7) + : [dst]"+&r"(dst), [src]"+&r"(src), [temp0]"=&r"(temp0), + [temp1]"=&r"(temp1), [temp2]"=&r"(temp2), [temp3]"=&r"(temp3), + [temp4]"=&r"(temp4), [temp5]"=&r"(temp5), [temp6]"=&r"(temp6), + [temp7]"=&r"(temp7) : [p_loop1_end]"r"(p_loop1_end), [p_loop2_end]"r"(p_loop2_end) : "memory" ); } static void TransformColorInverse(const VP8LMultipliers* const m, - uint32_t* data, int num_pixels) { + const uint32_t* src, int num_pixels, + uint32_t* dst) { int temp0, temp1, temp2, temp3, temp4, temp5; uint32_t argb, argb1, new_red; const uint32_t G_to_R = m->green_to_red_; const uint32_t G_to_B = m->green_to_blue_; const uint32_t R_to_B = m->red_to_blue_; - uint32_t* const p_loop_end = data + (num_pixels & ~1); + const uint32_t* const p_loop_end = src + (num_pixels & ~1); __asm__ volatile ( ".set push \n\t" ".set noreorder \n\t" - "beq %[data], %[p_loop_end], 1f \n\t" + "beq %[src], %[p_loop_end], 1f \n\t" " nop \n\t" "replv.ph %[temp0], %[G_to_R] \n\t" "replv.ph %[temp1], %[G_to_B] \n\t" @@ -303,9 +309,12 @@ static void TransformColorInverse(const VP8LMultipliers* const m, "shra.ph %[temp1], %[temp1], 8 \n\t" "shra.ph %[temp2], %[temp2], 8 \n\t" "0: \n\t" - "lw %[argb], 0(%[data]) \n\t" - "lw %[argb1], 4(%[data]) \n\t" - "addiu %[data], %[data], 8 \n\t" + "lw %[argb], 0(%[src]) \n\t" + "lw %[argb1], 4(%[src]) \n\t" + "sw %[argb], 0(%[dst]) \n\t" + "sw %[argb1], 4(%[dst]) \n\t" + "addiu %[src], %[src], 8 \n\t" + "addiu %[dst], %[dst], 8 \n\t" "precrq.qb.ph %[temp3], %[argb], %[argb1] \n\t" "preceu.ph.qbra %[temp3], %[temp3] \n\t" "shll.ph %[temp3], %[temp3], 8 \n\t" @@ -322,29 +331,29 @@ static void TransformColorInverse(const VP8LMultipliers* const m, "shll.ph %[temp4], %[temp5], 8 \n\t" "shra.ph %[temp4], %[temp4], 8 \n\t" "mul.ph %[temp4], %[temp4], %[temp2] \n\t" - "sb %[temp5], -2(%[data]) \n\t" + "sb %[temp5], -2(%[dst]) \n\t" "sra %[temp5], %[temp5], 16 \n\t" "shra.ph %[temp4], %[temp4], 5 \n\t" "addu.ph %[argb1], %[argb1], %[temp4] \n\t" "preceu.ph.qbra %[temp3], %[argb1] \n\t" - "sb %[temp5], -6(%[data]) \n\t" - "sb %[temp3], -4(%[data]) \n\t" + "sb %[temp5], -6(%[dst]) \n\t" + "sb %[temp3], -4(%[dst]) \n\t" "sra %[temp3], %[temp3], 16 \n\t" - "bne %[data], %[p_loop_end], 0b \n\t" - " sb %[temp3], -8(%[data]) \n\t" + "bne %[src], %[p_loop_end], 0b \n\t" + " sb %[temp3], -8(%[dst]) \n\t" "1: \n\t" ".set pop \n\t" : [temp0]"=&r"(temp0), [temp1]"=&r"(temp1), [temp2]"=&r"(temp2), [temp3]"=&r"(temp3), [temp4]"=&r"(temp4), [temp5]"=&r"(temp5), [new_red]"=&r"(new_red), [argb]"=&r"(argb), - [argb1]"=&r"(argb1), [data]"+&r"(data) + [argb1]"=&r"(argb1), [dst]"+&r"(dst), [src]"+&r"(src) : [G_to_R]"r"(G_to_R), [R_to_B]"r"(R_to_B), [G_to_B]"r"(G_to_B), [p_loop_end]"r"(p_loop_end) : "memory", "hi", "lo" ); // Fall-back to C-version for left-overs. - if (num_pixels & 1) VP8LTransformColorInverse_C(m, data, 1); + if (num_pixels & 1) VP8LTransformColorInverse_C(m, src, 1, dst); } static void ConvertBGRAToRGB(const uint32_t* src, diff --git a/thirdparty/libwebp/dsp/lossless_msa.c b/thirdparty/libwebp/dsp/lossless_msa.c new file mode 100644 index 0000000000..f6dd5649ac --- /dev/null +++ b/thirdparty/libwebp/dsp/lossless_msa.c @@ -0,0 +1,355 @@ +// Copyright 2016 Google Inc. All Rights Reserved. +// +// Use of this source code is governed by a BSD-style license +// that can be found in the COPYING file in the root of the source +// tree. An additional intellectual property rights grant can be found +// in the file PATENTS. All contributing project authors may +// be found in the AUTHORS file in the root of the source tree. +// ----------------------------------------------------------------------------- +// +// MSA variant of methods for lossless decoder +// +// Author: Prashant Patil (prashant.patil@imgtec.com) + +#include "./dsp.h" + +#if defined(WEBP_USE_MSA) + +#include "./lossless.h" +#include "./msa_macro.h" + +//------------------------------------------------------------------------------ +// Colorspace conversion functions + +#define CONVERT16_BGRA_XXX(psrc, pdst, m0, m1, m2) do { \ + v16u8 src0, src1, src2, src3, dst0, dst1, dst2; \ + LD_UB4(psrc, 16, src0, src1, src2, src3); \ + VSHF_B2_UB(src0, src1, src1, src2, m0, m1, dst0, dst1); \ + dst2 = VSHF_UB(src2, src3, m2); \ + ST_UB2(dst0, dst1, pdst, 16); \ + ST_UB(dst2, pdst + 32); \ +} while (0) + +#define CONVERT12_BGRA_XXX(psrc, pdst, m0, m1, m2) do { \ + uint32_t pix_w; \ + v16u8 src0, src1, src2, dst0, dst1, dst2; \ + LD_UB3(psrc, 16, src0, src1, src2); \ + VSHF_B2_UB(src0, src1, src1, src2, m0, m1, dst0, dst1); \ + dst2 = VSHF_UB(src2, src2, m2); \ + ST_UB2(dst0, dst1, pdst, 16); \ + pix_w = __msa_copy_s_w((v4i32)dst2, 0); \ + SW(pix_w, pdst + 32); \ +} while (0) + +#define CONVERT8_BGRA_XXX(psrc, pdst, m0, m1) do { \ + uint64_t pix_d; \ + v16u8 src0, src1, src2, dst0, dst1; \ + LD_UB2(psrc, 16, src0, src1); \ + VSHF_B2_UB(src0, src1, src1, src2, m0, m1, dst0, dst1); \ + ST_UB(dst0, pdst); \ + pix_d = __msa_copy_s_d((v2i64)dst1, 0); \ + SD(pix_d, pdst + 16); \ +} while (0) + +#define CONVERT4_BGRA_XXX(psrc, pdst, m) do { \ + const v16u8 src0 = LD_UB(psrc); \ + const v16u8 dst0 = VSHF_UB(src0, src0, m); \ + uint64_t pix_d = __msa_copy_s_d((v2i64)dst0, 0); \ + uint32_t pix_w = __msa_copy_s_w((v4i32)dst0, 2); \ + SD(pix_d, pdst + 0); \ + SW(pix_w, pdst + 8); \ +} while (0) + +#define CONVERT1_BGRA_BGR(psrc, pdst) do { \ + const int32_t b = (psrc)[0]; \ + const int32_t g = (psrc)[1]; \ + const int32_t r = (psrc)[2]; \ + (pdst)[0] = b; \ + (pdst)[1] = g; \ + (pdst)[2] = r; \ +} while (0) + +#define CONVERT1_BGRA_RGB(psrc, pdst) do { \ + const int32_t b = (psrc)[0]; \ + const int32_t g = (psrc)[1]; \ + const int32_t r = (psrc)[2]; \ + (pdst)[0] = r; \ + (pdst)[1] = g; \ + (pdst)[2] = b; \ +} while (0) + +#define TRANSFORM_COLOR_INVERSE_8(src0, src1, dst0, dst1, \ + c0, c1, mask0, mask1) do { \ + v8i16 g0, g1, t0, t1, t2, t3; \ + v4i32 t4, t5; \ + VSHF_B2_SH(src0, src0, src1, src1, mask0, mask0, g0, g1); \ + DOTP_SB2_SH(g0, g1, c0, c0, t0, t1); \ + SRAI_H2_SH(t0, t1, 5); \ + t0 = __msa_addv_h(t0, (v8i16)src0); \ + t1 = __msa_addv_h(t1, (v8i16)src1); \ + t4 = __msa_srli_w((v4i32)t0, 16); \ + t5 = __msa_srli_w((v4i32)t1, 16); \ + DOTP_SB2_SH(t4, t5, c1, c1, t2, t3); \ + SRAI_H2_SH(t2, t3, 5); \ + ADD2(t0, t2, t1, t3, t0, t1); \ + VSHF_B2_UB(src0, t0, src1, t1, mask1, mask1, dst0, dst1); \ +} while (0) + +#define TRANSFORM_COLOR_INVERSE_4(src, dst, c0, c1, mask0, mask1) do { \ + const v16i8 g0 = VSHF_SB(src, src, mask0); \ + v8i16 t0 = __msa_dotp_s_h(c0, g0); \ + v8i16 t1; \ + v4i32 t2; \ + t0 = SRAI_H(t0, 5); \ + t0 = __msa_addv_h(t0, (v8i16)src); \ + t2 = __msa_srli_w((v4i32)t0, 16); \ + t1 = __msa_dotp_s_h(c1, (v16i8)t2); \ + t1 = SRAI_H(t1, 5); \ + t0 = t0 + t1; \ + dst = VSHF_UB(src, t0, mask1); \ +} while (0) + +static void ConvertBGRAToRGBA(const uint32_t* src, + int num_pixels, uint8_t* dst) { + int i; + const uint8_t* ptemp_src = (const uint8_t*)src; + uint8_t* ptemp_dst = (uint8_t*)dst; + v16u8 src0, dst0; + const v16u8 mask = { 2, 1, 0, 3, 6, 5, 4, 7, 10, 9, 8, 11, 14, 13, 12, 15 }; + + while (num_pixels >= 8) { + v16u8 src1, dst1; + LD_UB2(ptemp_src, 16, src0, src1); + VSHF_B2_UB(src0, src0, src1, src1, mask, mask, dst0, dst1); + ST_UB2(dst0, dst1, ptemp_dst, 16); + ptemp_src += 32; + ptemp_dst += 32; + num_pixels -= 8; + } + if (num_pixels > 0) { + if (num_pixels >= 4) { + src0 = LD_UB(ptemp_src); + dst0 = VSHF_UB(src0, src0, mask); + ST_UB(dst0, ptemp_dst); + ptemp_src += 16; + ptemp_dst += 16; + num_pixels -= 4; + } + for (i = 0; i < num_pixels; i++) { + const uint8_t b = ptemp_src[2]; + const uint8_t g = ptemp_src[1]; + const uint8_t r = ptemp_src[0]; + const uint8_t a = ptemp_src[3]; + ptemp_dst[0] = b; + ptemp_dst[1] = g; + ptemp_dst[2] = r; + ptemp_dst[3] = a; + ptemp_src += 4; + ptemp_dst += 4; + } + } +} + +static void ConvertBGRAToBGR(const uint32_t* src, + int num_pixels, uint8_t* dst) { + const uint8_t* ptemp_src = (const uint8_t*)src; + uint8_t* ptemp_dst = (uint8_t*)dst; + const v16u8 mask0 = { 0, 1, 2, 4, 5, 6, 8, 9, 10, 12, 13, 14, + 16, 17, 18, 20 }; + const v16u8 mask1 = { 5, 6, 8, 9, 10, 12, 13, 14, 16, 17, 18, 20, + 21, 22, 24, 25 }; + const v16u8 mask2 = { 10, 12, 13, 14, 16, 17, 18, 20, 21, 22, 24, 25, + 26, 28, 29, 30 }; + + while (num_pixels >= 16) { + CONVERT16_BGRA_XXX(ptemp_src, ptemp_dst, mask0, mask1, mask2); + ptemp_src += 64; + ptemp_dst += 48; + num_pixels -= 16; + } + if (num_pixels > 0) { + if (num_pixels >= 12) { + CONVERT12_BGRA_XXX(ptemp_src, ptemp_dst, mask0, mask1, mask2); + ptemp_src += 48; + ptemp_dst += 36; + num_pixels -= 12; + } else if (num_pixels >= 8) { + CONVERT8_BGRA_XXX(ptemp_src, ptemp_dst, mask0, mask1); + ptemp_src += 32; + ptemp_dst += 24; + num_pixels -= 8; + } else if (num_pixels >= 4) { + CONVERT4_BGRA_XXX(ptemp_src, ptemp_dst, mask0); + ptemp_src += 16; + ptemp_dst += 12; + num_pixels -= 4; + } + if (num_pixels == 3) { + CONVERT1_BGRA_BGR(ptemp_src + 0, ptemp_dst + 0); + CONVERT1_BGRA_BGR(ptemp_src + 4, ptemp_dst + 3); + CONVERT1_BGRA_BGR(ptemp_src + 8, ptemp_dst + 6); + } else if (num_pixels == 2) { + CONVERT1_BGRA_BGR(ptemp_src + 0, ptemp_dst + 0); + CONVERT1_BGRA_BGR(ptemp_src + 4, ptemp_dst + 3); + } else if (num_pixels == 1) { + CONVERT1_BGRA_BGR(ptemp_src, ptemp_dst); + } + } +} + +static void ConvertBGRAToRGB(const uint32_t* src, + int num_pixels, uint8_t* dst) { + const uint8_t* ptemp_src = (const uint8_t*)src; + uint8_t* ptemp_dst = (uint8_t*)dst; + const v16u8 mask0 = { 2, 1, 0, 6, 5, 4, 10, 9, 8, 14, 13, 12, + 18, 17, 16, 22 }; + const v16u8 mask1 = { 5, 4, 10, 9, 8, 14, 13, 12, 18, 17, 16, 22, + 21, 20, 26, 25 }; + const v16u8 mask2 = { 8, 14, 13, 12, 18, 17, 16, 22, 21, 20, 26, 25, + 24, 30, 29, 28 }; + + while (num_pixels >= 16) { + CONVERT16_BGRA_XXX(ptemp_src, ptemp_dst, mask0, mask1, mask2); + ptemp_src += 64; + ptemp_dst += 48; + num_pixels -= 16; + } + if (num_pixels) { + if (num_pixels >= 12) { + CONVERT12_BGRA_XXX(ptemp_src, ptemp_dst, mask0, mask1, mask2); + ptemp_src += 48; + ptemp_dst += 36; + num_pixels -= 12; + } else if (num_pixels >= 8) { + CONVERT8_BGRA_XXX(ptemp_src, ptemp_dst, mask0, mask1); + ptemp_src += 32; + ptemp_dst += 24; + num_pixels -= 8; + } else if (num_pixels >= 4) { + CONVERT4_BGRA_XXX(ptemp_src, ptemp_dst, mask0); + ptemp_src += 16; + ptemp_dst += 12; + num_pixels -= 4; + } + if (num_pixels == 3) { + CONVERT1_BGRA_RGB(ptemp_src + 0, ptemp_dst + 0); + CONVERT1_BGRA_RGB(ptemp_src + 4, ptemp_dst + 3); + CONVERT1_BGRA_RGB(ptemp_src + 8, ptemp_dst + 6); + } else if (num_pixels == 2) { + CONVERT1_BGRA_RGB(ptemp_src + 0, ptemp_dst + 0); + CONVERT1_BGRA_RGB(ptemp_src + 4, ptemp_dst + 3); + } else if (num_pixels == 1) { + CONVERT1_BGRA_RGB(ptemp_src, ptemp_dst); + } + } +} + +static void AddGreenToBlueAndRed(const uint32_t* const src, int num_pixels, + uint32_t* dst) { + int i; + const uint8_t* in = (const uint8_t*)src; + uint8_t* out = (uint8_t*)dst; + v16u8 src0, dst0, tmp0; + const v16u8 mask = { 1, 255, 1, 255, 5, 255, 5, 255, 9, 255, 9, 255, + 13, 255, 13, 255 }; + + while (num_pixels >= 8) { + v16u8 src1, dst1, tmp1; + LD_UB2(in, 16, src0, src1); + VSHF_B2_UB(src0, src1, src1, src0, mask, mask, tmp0, tmp1); + ADD2(src0, tmp0, src1, tmp1, dst0, dst1); + ST_UB2(dst0, dst1, out, 16); + in += 32; + out += 32; + num_pixels -= 8; + } + if (num_pixels > 0) { + if (num_pixels >= 4) { + src0 = LD_UB(in); + tmp0 = VSHF_UB(src0, src0, mask); + dst0 = src0 + tmp0; + ST_UB(dst0, out); + in += 16; + out += 16; + num_pixels -= 4; + } + for (i = 0; i < num_pixels; i++) { + const uint8_t b = in[0]; + const uint8_t g = in[1]; + const uint8_t r = in[2]; + out[0] = (b + g) & 0xff; + out[1] = g; + out[2] = (r + g) & 0xff; + out[4] = in[4]; + out += 4; + } + } +} + +static void TransformColorInverse(const VP8LMultipliers* const m, + const uint32_t* src, int num_pixels, + uint32_t* dst) { + v16u8 src0, dst0; + const v16i8 g2br = (v16i8)__msa_fill_w(m->green_to_blue_ | + (m->green_to_red_ << 16)); + const v16i8 r2b = (v16i8)__msa_fill_w(m->red_to_blue_); + const v16u8 mask0 = { 1, 255, 1, 255, 5, 255, 5, 255, 9, 255, 9, 255, + 13, 255, 13, 255 }; + const v16u8 mask1 = { 16, 1, 18, 3, 20, 5, 22, 7, 24, 9, 26, 11, + 28, 13, 30, 15 }; + + while (num_pixels >= 8) { + v16u8 src1, dst1; + LD_UB2(src, 4, src0, src1); + TRANSFORM_COLOR_INVERSE_8(src0, src1, dst0, dst1, g2br, r2b, mask0, mask1); + ST_UB2(dst0, dst1, dst, 4); + src += 8; + dst += 8; + num_pixels -= 8; + } + if (num_pixels > 0) { + if (num_pixels >= 4) { + src0 = LD_UB(src); + TRANSFORM_COLOR_INVERSE_4(src0, dst0, g2br, r2b, mask0, mask1); + ST_UB(dst0, dst); + src += 4; + dst += 4; + num_pixels -= 4; + } + if (num_pixels > 0) { + src0 = LD_UB(src); + TRANSFORM_COLOR_INVERSE_4(src0, dst0, g2br, r2b, mask0, mask1); + if (num_pixels == 3) { + const uint64_t pix_d = __msa_copy_s_d((v2i64)dst0, 0); + const uint32_t pix_w = __msa_copy_s_w((v4i32)dst0, 2); + SD(pix_d, dst + 0); + SW(pix_w, dst + 2); + } else if (num_pixels == 2) { + const uint64_t pix_d = __msa_copy_s_d((v2i64)dst0, 0); + SD(pix_d, dst); + } else { + const uint32_t pix_w = __msa_copy_s_w((v4i32)dst0, 0); + SW(pix_w, dst); + } + } + } +} + +//------------------------------------------------------------------------------ +// Entry point + +extern void VP8LDspInitMSA(void); + +WEBP_TSAN_IGNORE_FUNCTION void VP8LDspInitMSA(void) { + VP8LConvertBGRAToRGBA = ConvertBGRAToRGBA; + VP8LConvertBGRAToBGR = ConvertBGRAToBGR; + VP8LConvertBGRAToRGB = ConvertBGRAToRGB; + VP8LAddGreenToBlueAndRed = AddGreenToBlueAndRed; + VP8LTransformColorInverse = TransformColorInverse; +} + +#else // !WEBP_USE_MSA + +WEBP_DSP_INIT_STUB(VP8LDspInitMSA) + +#endif // WEBP_USE_MSA diff --git a/thirdparty/libwebp/dsp/lossless_neon.c b/thirdparty/libwebp/dsp/lossless_neon.c index 6faccb8f97..1145d5fad0 100644 --- a/thirdparty/libwebp/dsp/lossless_neon.c +++ b/thirdparty/libwebp/dsp/lossless_neon.c @@ -139,6 +139,357 @@ static void ConvertBGRAToRGB(const uint32_t* src, #endif // !WORK_AROUND_GCC + +//------------------------------------------------------------------------------ +// Predictor Transform + +#define LOAD_U32_AS_U8(IN) vreinterpret_u8_u32(vdup_n_u32((IN))) +#define LOAD_U32P_AS_U8(IN) vreinterpret_u8_u32(vld1_u32((IN))) +#define LOADQ_U32_AS_U8(IN) vreinterpretq_u8_u32(vdupq_n_u32((IN))) +#define LOADQ_U32P_AS_U8(IN) vreinterpretq_u8_u32(vld1q_u32((IN))) +#define GET_U8_AS_U32(IN) vget_lane_u32(vreinterpret_u32_u8((IN)), 0); +#define GETQ_U8_AS_U32(IN) vgetq_lane_u32(vreinterpretq_u32_u8((IN)), 0); +#define STOREQ_U8_AS_U32P(OUT, IN) vst1q_u32((OUT), vreinterpretq_u32_u8((IN))); +#define ROTATE32_LEFT(L) vextq_u8((L), (L), 12) // D|C|B|A -> C|B|A|D + +static WEBP_INLINE uint8x8_t Average2_u8_NEON(uint32_t a0, uint32_t a1) { + const uint8x8_t A0 = LOAD_U32_AS_U8(a0); + const uint8x8_t A1 = LOAD_U32_AS_U8(a1); + return vhadd_u8(A0, A1); +} + +static WEBP_INLINE uint32_t ClampedAddSubtractHalf_NEON(uint32_t c0, + uint32_t c1, + uint32_t c2) { + const uint8x8_t avg = Average2_u8_NEON(c0, c1); + // Remove one to c2 when bigger than avg. + const uint8x8_t C2 = LOAD_U32_AS_U8(c2); + const uint8x8_t cmp = vcgt_u8(C2, avg); + const uint8x8_t C2_1 = vadd_u8(C2, cmp); + // Compute half of the difference between avg and c2. + const int8x8_t diff_avg = vreinterpret_s8_u8(vhsub_u8(avg, C2_1)); + // Compute the sum with avg and saturate. + const int16x8_t avg_16 = vreinterpretq_s16_u16(vmovl_u8(avg)); + const uint8x8_t res = vqmovun_s16(vaddw_s8(avg_16, diff_avg)); + const uint32_t output = GET_U8_AS_U32(res); + return output; +} + +static WEBP_INLINE uint32_t Average2_NEON(uint32_t a0, uint32_t a1) { + const uint8x8_t avg_u8x8 = Average2_u8_NEON(a0, a1); + const uint32_t avg = GET_U8_AS_U32(avg_u8x8); + return avg; +} + +static WEBP_INLINE uint32_t Average3_NEON(uint32_t a0, uint32_t a1, + uint32_t a2) { + const uint8x8_t avg0 = Average2_u8_NEON(a0, a2); + const uint8x8_t A1 = LOAD_U32_AS_U8(a1); + const uint32_t avg = GET_U8_AS_U32(vhadd_u8(avg0, A1)); + return avg; +} + +static uint32_t Predictor5_NEON(uint32_t left, const uint32_t* const top) { + return Average3_NEON(left, top[0], top[1]); +} +static uint32_t Predictor6_NEON(uint32_t left, const uint32_t* const top) { + return Average2_NEON(left, top[-1]); +} +static uint32_t Predictor7_NEON(uint32_t left, const uint32_t* const top) { + return Average2_NEON(left, top[0]); +} +static uint32_t Predictor13_NEON(uint32_t left, const uint32_t* const top) { + return ClampedAddSubtractHalf_NEON(left, top[0], top[-1]); +} + +// Batch versions of those functions. + +// Predictor0: ARGB_BLACK. +static void PredictorAdd0_NEON(const uint32_t* in, const uint32_t* upper, + int num_pixels, uint32_t* out) { + int i; + const uint8x16_t black = vreinterpretq_u8_u32(vdupq_n_u32(ARGB_BLACK)); + for (i = 0; i + 4 <= num_pixels; i += 4) { + const uint8x16_t src = LOADQ_U32P_AS_U8(&in[i]); + const uint8x16_t res = vaddq_u8(src, black); + STOREQ_U8_AS_U32P(&out[i], res); + } + VP8LPredictorsAdd_C[0](in + i, upper + i, num_pixels - i, out + i); +} + +// Predictor1: left. +static void PredictorAdd1_NEON(const uint32_t* in, const uint32_t* upper, + int num_pixels, uint32_t* out) { + int i; + const uint8x16_t zero = LOADQ_U32_AS_U8(0); + for (i = 0; i + 4 <= num_pixels; i += 4) { + // a | b | c | d + const uint8x16_t src = LOADQ_U32P_AS_U8(&in[i]); + // 0 | a | b | c + const uint8x16_t shift0 = vextq_u8(zero, src, 12); + // a | a + b | b + c | c + d + const uint8x16_t sum0 = vaddq_u8(src, shift0); + // 0 | 0 | a | a + b + const uint8x16_t shift1 = vextq_u8(zero, sum0, 8); + // a | a + b | a + b + c | a + b + c + d + const uint8x16_t sum1 = vaddq_u8(sum0, shift1); + const uint8x16_t prev = LOADQ_U32_AS_U8(out[i - 1]); + const uint8x16_t res = vaddq_u8(sum1, prev); + STOREQ_U8_AS_U32P(&out[i], res); + } + VP8LPredictorsAdd_C[1](in + i, upper + i, num_pixels - i, out + i); +} + +// Macro that adds 32-bit integers from IN using mod 256 arithmetic +// per 8 bit channel. +#define GENERATE_PREDICTOR_1(X, IN) \ +static void PredictorAdd##X##_NEON(const uint32_t* in, \ + const uint32_t* upper, int num_pixels, \ + uint32_t* out) { \ + int i; \ + for (i = 0; i + 4 <= num_pixels; i += 4) { \ + const uint8x16_t src = LOADQ_U32P_AS_U8(&in[i]); \ + const uint8x16_t other = LOADQ_U32P_AS_U8(&(IN)); \ + const uint8x16_t res = vaddq_u8(src, other); \ + STOREQ_U8_AS_U32P(&out[i], res); \ + } \ + VP8LPredictorsAdd_C[(X)](in + i, upper + i, num_pixels - i, out + i); \ +} +// Predictor2: Top. +GENERATE_PREDICTOR_1(2, upper[i]) +// Predictor3: Top-right. +GENERATE_PREDICTOR_1(3, upper[i + 1]) +// Predictor4: Top-left. +GENERATE_PREDICTOR_1(4, upper[i - 1]) +#undef GENERATE_PREDICTOR_1 + +// Predictor5: average(average(left, TR), T) +#define DO_PRED5(LANE) do { \ + const uint8x16_t avgLTR = vhaddq_u8(L, TR); \ + const uint8x16_t avg = vhaddq_u8(avgLTR, T); \ + const uint8x16_t res = vaddq_u8(avg, src); \ + vst1q_lane_u32(&out[i + (LANE)], vreinterpretq_u32_u8(res), (LANE)); \ + L = ROTATE32_LEFT(res); \ +} while (0) + +static void PredictorAdd5_NEON(const uint32_t* in, const uint32_t* upper, + int num_pixels, uint32_t* out) { + int i; + uint8x16_t L = LOADQ_U32_AS_U8(out[-1]); + for (i = 0; i + 4 <= num_pixels; i += 4) { + const uint8x16_t src = LOADQ_U32P_AS_U8(&in[i]); + const uint8x16_t T = LOADQ_U32P_AS_U8(&upper[i + 0]); + const uint8x16_t TR = LOADQ_U32P_AS_U8(&upper[i + 1]); + DO_PRED5(0); + DO_PRED5(1); + DO_PRED5(2); + DO_PRED5(3); + } + VP8LPredictorsAdd_C[5](in + i, upper + i, num_pixels - i, out + i); +} +#undef DO_PRED5 + +#define DO_PRED67(LANE) do { \ + const uint8x16_t avg = vhaddq_u8(L, top); \ + const uint8x16_t res = vaddq_u8(avg, src); \ + vst1q_lane_u32(&out[i + (LANE)], vreinterpretq_u32_u8(res), (LANE)); \ + L = ROTATE32_LEFT(res); \ +} while (0) + +// Predictor6: average(left, TL) +static void PredictorAdd6_NEON(const uint32_t* in, const uint32_t* upper, + int num_pixels, uint32_t* out) { + int i; + uint8x16_t L = LOADQ_U32_AS_U8(out[-1]); + for (i = 0; i + 4 <= num_pixels; i += 4) { + const uint8x16_t src = LOADQ_U32P_AS_U8(&in[i]); + const uint8x16_t top = LOADQ_U32P_AS_U8(&upper[i - 1]); + DO_PRED67(0); + DO_PRED67(1); + DO_PRED67(2); + DO_PRED67(3); + } + VP8LPredictorsAdd_C[6](in + i, upper + i, num_pixels - i, out + i); +} + +// Predictor7: average(left, T) +static void PredictorAdd7_NEON(const uint32_t* in, const uint32_t* upper, + int num_pixels, uint32_t* out) { + int i; + uint8x16_t L = LOADQ_U32_AS_U8(out[-1]); + for (i = 0; i + 4 <= num_pixels; i += 4) { + const uint8x16_t src = LOADQ_U32P_AS_U8(&in[i]); + const uint8x16_t top = LOADQ_U32P_AS_U8(&upper[i]); + DO_PRED67(0); + DO_PRED67(1); + DO_PRED67(2); + DO_PRED67(3); + } + VP8LPredictorsAdd_C[7](in + i, upper + i, num_pixels - i, out + i); +} +#undef DO_PRED67 + +#define GENERATE_PREDICTOR_2(X, IN) \ +static void PredictorAdd##X##_NEON(const uint32_t* in, \ + const uint32_t* upper, int num_pixels, \ + uint32_t* out) { \ + int i; \ + for (i = 0; i + 4 <= num_pixels; i += 4) { \ + const uint8x16_t src = LOADQ_U32P_AS_U8(&in[i]); \ + const uint8x16_t Tother = LOADQ_U32P_AS_U8(&(IN)); \ + const uint8x16_t T = LOADQ_U32P_AS_U8(&upper[i]); \ + const uint8x16_t avg = vhaddq_u8(T, Tother); \ + const uint8x16_t res = vaddq_u8(avg, src); \ + STOREQ_U8_AS_U32P(&out[i], res); \ + } \ + VP8LPredictorsAdd_C[(X)](in + i, upper + i, num_pixels - i, out + i); \ +} +// Predictor8: average TL T. +GENERATE_PREDICTOR_2(8, upper[i - 1]) +// Predictor9: average T TR. +GENERATE_PREDICTOR_2(9, upper[i + 1]) +#undef GENERATE_PREDICTOR_2 + +// Predictor10: average of (average of (L,TL), average of (T, TR)). +#define DO_PRED10(LANE) do { \ + const uint8x16_t avgLTL = vhaddq_u8(L, TL); \ + const uint8x16_t avg = vhaddq_u8(avgTTR, avgLTL); \ + const uint8x16_t res = vaddq_u8(avg, src); \ + vst1q_lane_u32(&out[i + (LANE)], vreinterpretq_u32_u8(res), (LANE)); \ + L = ROTATE32_LEFT(res); \ +} while (0) + +static void PredictorAdd10_NEON(const uint32_t* in, const uint32_t* upper, + int num_pixels, uint32_t* out) { + int i; + uint8x16_t L = LOADQ_U32_AS_U8(out[-1]); + for (i = 0; i + 4 <= num_pixels; i += 4) { + const uint8x16_t src = LOADQ_U32P_AS_U8(&in[i]); + const uint8x16_t TL = LOADQ_U32P_AS_U8(&upper[i - 1]); + const uint8x16_t T = LOADQ_U32P_AS_U8(&upper[i]); + const uint8x16_t TR = LOADQ_U32P_AS_U8(&upper[i + 1]); + const uint8x16_t avgTTR = vhaddq_u8(T, TR); + DO_PRED10(0); + DO_PRED10(1); + DO_PRED10(2); + DO_PRED10(3); + } + VP8LPredictorsAdd_C[10](in + i, upper + i, num_pixels - i, out + i); +} +#undef DO_PRED10 + +// Predictor11: select. +#define DO_PRED11(LANE) do { \ + const uint8x16_t sumLin = vaddq_u8(L, src); /* in + L */ \ + const uint8x16_t pLTL = vabdq_u8(L, TL); /* |L - TL| */ \ + const uint16x8_t sum_LTL = vpaddlq_u8(pLTL); \ + const uint32x4_t pa = vpaddlq_u16(sum_LTL); \ + const uint32x4_t mask = vcleq_u32(pa, pb); \ + const uint8x16_t res = vbslq_u8(vreinterpretq_u8_u32(mask), sumTin, sumLin); \ + vst1q_lane_u32(&out[i + (LANE)], vreinterpretq_u32_u8(res), (LANE)); \ + L = ROTATE32_LEFT(res); \ +} while (0) + +static void PredictorAdd11_NEON(const uint32_t* in, const uint32_t* upper, + int num_pixels, uint32_t* out) { + int i; + uint8x16_t L = LOADQ_U32_AS_U8(out[-1]); + for (i = 0; i + 4 <= num_pixels; i += 4) { + const uint8x16_t T = LOADQ_U32P_AS_U8(&upper[i]); + const uint8x16_t TL = LOADQ_U32P_AS_U8(&upper[i - 1]); + const uint8x16_t pTTL = vabdq_u8(T, TL); // |T - TL| + const uint16x8_t sum_TTL = vpaddlq_u8(pTTL); + const uint32x4_t pb = vpaddlq_u16(sum_TTL); + const uint8x16_t src = LOADQ_U32P_AS_U8(&in[i]); + const uint8x16_t sumTin = vaddq_u8(T, src); // in + T + DO_PRED11(0); + DO_PRED11(1); + DO_PRED11(2); + DO_PRED11(3); + } + VP8LPredictorsAdd_C[11](in + i, upper + i, num_pixels - i, out + i); +} +#undef DO_PRED11 + +// Predictor12: ClampedAddSubtractFull. +#define DO_PRED12(DIFF, LANE) do { \ + const uint8x8_t pred = \ + vqmovun_s16(vaddq_s16(vreinterpretq_s16_u16(L), (DIFF))); \ + const uint8x8_t res = \ + vadd_u8(pred, (LANE <= 1) ? vget_low_u8(src) : vget_high_u8(src)); \ + const uint16x8_t res16 = vmovl_u8(res); \ + vst1_lane_u32(&out[i + (LANE)], vreinterpret_u32_u8(res), (LANE) & 1); \ + /* rotate in the left predictor for next iteration */ \ + L = vextq_u16(res16, res16, 4); \ +} while (0) + +static void PredictorAdd12_NEON(const uint32_t* in, const uint32_t* upper, + int num_pixels, uint32_t* out) { + int i; + uint16x8_t L = vmovl_u8(LOAD_U32_AS_U8(out[-1])); + for (i = 0; i + 4 <= num_pixels; i += 4) { + // load four pixels of source + const uint8x16_t src = LOADQ_U32P_AS_U8(&in[i]); + // precompute the difference T - TL once for all, stored as s16 + const uint8x16_t TL = LOADQ_U32P_AS_U8(&upper[i - 1]); + const uint8x16_t T = LOADQ_U32P_AS_U8(&upper[i]); + const int16x8_t diff_lo = + vreinterpretq_s16_u16(vsubl_u8(vget_low_u8(T), vget_low_u8(TL))); + const int16x8_t diff_hi = + vreinterpretq_s16_u16(vsubl_u8(vget_high_u8(T), vget_high_u8(TL))); + // loop over the four reconstructed pixels + DO_PRED12(diff_lo, 0); + DO_PRED12(diff_lo, 1); + DO_PRED12(diff_hi, 2); + DO_PRED12(diff_hi, 3); + } + VP8LPredictorsAdd_C[12](in + i, upper + i, num_pixels - i, out + i); +} +#undef DO_PRED12 + +// Predictor13: ClampedAddSubtractHalf +#define DO_PRED13(LANE, LOW_OR_HI) do { \ + const uint8x16_t avg = vhaddq_u8(L, T); \ + const uint8x16_t cmp = vcgtq_u8(TL, avg); \ + const uint8x16_t TL_1 = vaddq_u8(TL, cmp); \ + /* Compute half of the difference between avg and TL'. */ \ + const int8x8_t diff_avg = \ + vreinterpret_s8_u8(LOW_OR_HI(vhsubq_u8(avg, TL_1))); \ + /* Compute the sum with avg and saturate. */ \ + const int16x8_t avg_16 = vreinterpretq_s16_u16(vmovl_u8(LOW_OR_HI(avg))); \ + const uint8x8_t delta = vqmovun_s16(vaddw_s8(avg_16, diff_avg)); \ + const uint8x8_t res = vadd_u8(LOW_OR_HI(src), delta); \ + const uint8x16_t res2 = vcombine_u8(res, res); \ + vst1_lane_u32(&out[i + (LANE)], vreinterpret_u32_u8(res), (LANE) & 1); \ + L = ROTATE32_LEFT(res2); \ +} while (0) + +static void PredictorAdd13_NEON(const uint32_t* in, const uint32_t* upper, + int num_pixels, uint32_t* out) { + int i; + uint8x16_t L = LOADQ_U32_AS_U8(out[-1]); + for (i = 0; i + 4 <= num_pixels; i += 4) { + const uint8x16_t src = LOADQ_U32P_AS_U8(&in[i]); + const uint8x16_t T = LOADQ_U32P_AS_U8(&upper[i]); + const uint8x16_t TL = LOADQ_U32P_AS_U8(&upper[i - 1]); + DO_PRED13(0, vget_low_u8); + DO_PRED13(1, vget_low_u8); + DO_PRED13(2, vget_high_u8); + DO_PRED13(3, vget_high_u8); + } + VP8LPredictorsAdd_C[13](in + i, upper + i, num_pixels - i, out + i); +} +#undef DO_PRED13 + +#undef LOAD_U32_AS_U8 +#undef LOAD_U32P_AS_U8 +#undef LOADQ_U32_AS_U8 +#undef LOADQ_U32P_AS_U8 +#undef GET_U8_AS_U32 +#undef GETQ_U8_AS_U32 +#undef STOREQ_U8_AS_U32P +#undef ROTATE32_LEFT + //------------------------------------------------------------------------------ // Subtract-Green Transform @@ -171,28 +522,30 @@ static WEBP_INLINE uint8x16_t DoGreenShuffle(const uint8x16_t argb, } #endif // USE_VTBLQ -static void AddGreenToBlueAndRed(uint32_t* argb_data, int num_pixels) { - const uint32_t* const end = argb_data + (num_pixels & ~3); +static void AddGreenToBlueAndRed(const uint32_t* src, int num_pixels, + uint32_t* dst) { + const uint32_t* const end = src + (num_pixels & ~3); #ifdef USE_VTBLQ const uint8x16_t shuffle = vld1q_u8(kGreenShuffle); #else const uint8x8_t shuffle = vld1_u8(kGreenShuffle); #endif - for (; argb_data < end; argb_data += 4) { - const uint8x16_t argb = vld1q_u8((uint8_t*)argb_data); + for (; src < end; src += 4, dst += 4) { + const uint8x16_t argb = vld1q_u8((const uint8_t*)src); const uint8x16_t greens = DoGreenShuffle(argb, shuffle); - vst1q_u8((uint8_t*)argb_data, vaddq_u8(argb, greens)); + vst1q_u8((uint8_t*)dst, vaddq_u8(argb, greens)); } // fallthrough and finish off with plain-C - VP8LAddGreenToBlueAndRed_C(argb_data, num_pixels & 3); + VP8LAddGreenToBlueAndRed_C(src, num_pixels & 3, dst); } //------------------------------------------------------------------------------ // Color Transform static void TransformColorInverse(const VP8LMultipliers* const m, - uint32_t* argb_data, int num_pixels) { - // sign-extended multiplying constants, pre-shifted by 6. + const uint32_t* const src, int num_pixels, + uint32_t* dst) { +// sign-extended multiplying constants, pre-shifted by 6. #define CST(X) (((int16_t)(m->X << 8)) >> 6) const int16_t rb[8] = { CST(green_to_blue_), CST(green_to_red_), @@ -219,7 +572,7 @@ static void TransformColorInverse(const VP8LMultipliers* const m, const uint32x4_t mask_ag = vdupq_n_u32(0xff00ff00u); int i; for (i = 0; i + 4 <= num_pixels; i += 4) { - const uint8x16_t in = vld1q_u8((uint8_t*)(argb_data + i)); + const uint8x16_t in = vld1q_u8((const uint8_t*)(src + i)); const uint32x4_t a0g0 = vandq_u32(vreinterpretq_u32_u8(in), mask_ag); // 0 g 0 g const uint8x16_t greens = DoGreenShuffle(in, shuffle); @@ -240,10 +593,10 @@ static void TransformColorInverse(const VP8LMultipliers* const m, // 0 r' 0 b'' const uint16x8_t G = vshrq_n_u16(vreinterpretq_u16_s8(F), 8); const uint32x4_t out = vorrq_u32(vreinterpretq_u32_u16(G), a0g0); - vst1q_u32(argb_data + i, out); + vst1q_u32(dst + i, out); } // Fall-back to C-version for left-overs. - VP8LTransformColorInverse_C(m, argb_data + i, num_pixels - i); + VP8LTransformColorInverse_C(m, src + i, num_pixels - i, dst + i); } #undef USE_VTBLQ @@ -254,6 +607,26 @@ static void TransformColorInverse(const VP8LMultipliers* const m, extern void VP8LDspInitNEON(void); WEBP_TSAN_IGNORE_FUNCTION void VP8LDspInitNEON(void) { + VP8LPredictors[5] = Predictor5_NEON; + VP8LPredictors[6] = Predictor6_NEON; + VP8LPredictors[7] = Predictor7_NEON; + VP8LPredictors[13] = Predictor13_NEON; + + VP8LPredictorsAdd[0] = PredictorAdd0_NEON; + VP8LPredictorsAdd[1] = PredictorAdd1_NEON; + VP8LPredictorsAdd[2] = PredictorAdd2_NEON; + VP8LPredictorsAdd[3] = PredictorAdd3_NEON; + VP8LPredictorsAdd[4] = PredictorAdd4_NEON; + VP8LPredictorsAdd[5] = PredictorAdd5_NEON; + VP8LPredictorsAdd[6] = PredictorAdd6_NEON; + VP8LPredictorsAdd[7] = PredictorAdd7_NEON; + VP8LPredictorsAdd[8] = PredictorAdd8_NEON; + VP8LPredictorsAdd[9] = PredictorAdd9_NEON; + VP8LPredictorsAdd[10] = PredictorAdd10_NEON; + VP8LPredictorsAdd[11] = PredictorAdd11_NEON; + VP8LPredictorsAdd[12] = PredictorAdd12_NEON; + VP8LPredictorsAdd[13] = PredictorAdd13_NEON; + VP8LConvertBGRAToRGBA = ConvertBGRAToRGBA; VP8LConvertBGRAToBGR = ConvertBGRAToBGR; VP8LConvertBGRAToRGB = ConvertBGRAToRGB; diff --git a/thirdparty/libwebp/dsp/lossless_sse2.c b/thirdparty/libwebp/dsp/lossless_sse2.c index 2d016c2911..15aae93869 100644 --- a/thirdparty/libwebp/dsp/lossless_sse2.c +++ b/thirdparty/libwebp/dsp/lossless_sse2.c @@ -14,9 +14,12 @@ #include "./dsp.h" #if defined(WEBP_USE_SSE2) + +#include "./common_sse2.h" +#include "./lossless.h" +#include "./lossless_common.h" #include #include -#include "./lossless.h" //------------------------------------------------------------------------------ // Predictor Transform @@ -75,25 +78,44 @@ static WEBP_INLINE uint32_t Select(uint32_t a, uint32_t b, uint32_t c) { return (pa_minus_pb <= 0) ? a : b; } -static WEBP_INLINE __m128i Average2_128i(uint32_t a0, uint32_t a1) { +static WEBP_INLINE void Average2_m128i(const __m128i* const a0, + const __m128i* const a1, + __m128i* const avg) { + // (a + b) >> 1 = ((a + b + 1) >> 1) - ((a ^ b) & 1) + const __m128i ones = _mm_set1_epi8(1); + const __m128i avg1 = _mm_avg_epu8(*a0, *a1); + const __m128i one = _mm_and_si128(_mm_xor_si128(*a0, *a1), ones); + *avg = _mm_sub_epi8(avg1, one); +} + +static WEBP_INLINE void Average2_uint32(const uint32_t a0, const uint32_t a1, + __m128i* const avg) { + // (a + b) >> 1 = ((a + b + 1) >> 1) - ((a ^ b) & 1) + const __m128i ones = _mm_set1_epi8(1); + const __m128i A0 = _mm_cvtsi32_si128(a0); + const __m128i A1 = _mm_cvtsi32_si128(a1); + const __m128i avg1 = _mm_avg_epu8(A0, A1); + const __m128i one = _mm_and_si128(_mm_xor_si128(A0, A1), ones); + *avg = _mm_sub_epi8(avg1, one); +} + +static WEBP_INLINE __m128i Average2_uint32_16(uint32_t a0, uint32_t a1) { const __m128i zero = _mm_setzero_si128(); const __m128i A0 = _mm_unpacklo_epi8(_mm_cvtsi32_si128(a0), zero); const __m128i A1 = _mm_unpacklo_epi8(_mm_cvtsi32_si128(a1), zero); const __m128i sum = _mm_add_epi16(A1, A0); - const __m128i avg = _mm_srli_epi16(sum, 1); - return avg; + return _mm_srli_epi16(sum, 1); } static WEBP_INLINE uint32_t Average2(uint32_t a0, uint32_t a1) { - const __m128i avg = Average2_128i(a0, a1); - const __m128i A2 = _mm_packus_epi16(avg, avg); - const uint32_t output = _mm_cvtsi128_si32(A2); - return output; + __m128i output; + Average2_uint32(a0, a1, &output); + return _mm_cvtsi128_si32(output); } static WEBP_INLINE uint32_t Average3(uint32_t a0, uint32_t a1, uint32_t a2) { const __m128i zero = _mm_setzero_si128(); - const __m128i avg1 = Average2_128i(a0, a2); + const __m128i avg1 = Average2_uint32_16(a0, a2); const __m128i A1 = _mm_unpacklo_epi8(_mm_cvtsi32_si128(a1), zero); const __m128i sum = _mm_add_epi16(avg1, A1); const __m128i avg2 = _mm_srli_epi16(sum, 1); @@ -104,8 +126,8 @@ static WEBP_INLINE uint32_t Average3(uint32_t a0, uint32_t a1, uint32_t a2) { static WEBP_INLINE uint32_t Average4(uint32_t a0, uint32_t a1, uint32_t a2, uint32_t a3) { - const __m128i avg1 = Average2_128i(a0, a1); - const __m128i avg2 = Average2_128i(a2, a3); + const __m128i avg1 = Average2_uint32_16(a0, a1); + const __m128i avg2 = Average2_uint32_16(a2, a3); const __m128i sum = _mm_add_epi16(avg2, avg1); const __m128i avg3 = _mm_srli_epi16(sum, 1); const __m128i A0 = _mm_packus_epi16(avg3, avg3); @@ -113,68 +135,289 @@ static WEBP_INLINE uint32_t Average4(uint32_t a0, uint32_t a1, return output; } -static uint32_t Predictor5(uint32_t left, const uint32_t* const top) { +static uint32_t Predictor5_SSE2(uint32_t left, const uint32_t* const top) { const uint32_t pred = Average3(left, top[0], top[1]); return pred; } -static uint32_t Predictor6(uint32_t left, const uint32_t* const top) { +static uint32_t Predictor6_SSE2(uint32_t left, const uint32_t* const top) { const uint32_t pred = Average2(left, top[-1]); return pred; } -static uint32_t Predictor7(uint32_t left, const uint32_t* const top) { +static uint32_t Predictor7_SSE2(uint32_t left, const uint32_t* const top) { const uint32_t pred = Average2(left, top[0]); return pred; } -static uint32_t Predictor8(uint32_t left, const uint32_t* const top) { +static uint32_t Predictor8_SSE2(uint32_t left, const uint32_t* const top) { const uint32_t pred = Average2(top[-1], top[0]); (void)left; return pred; } -static uint32_t Predictor9(uint32_t left, const uint32_t* const top) { +static uint32_t Predictor9_SSE2(uint32_t left, const uint32_t* const top) { const uint32_t pred = Average2(top[0], top[1]); (void)left; return pred; } -static uint32_t Predictor10(uint32_t left, const uint32_t* const top) { +static uint32_t Predictor10_SSE2(uint32_t left, const uint32_t* const top) { const uint32_t pred = Average4(left, top[-1], top[0], top[1]); return pred; } -static uint32_t Predictor11(uint32_t left, const uint32_t* const top) { +static uint32_t Predictor11_SSE2(uint32_t left, const uint32_t* const top) { const uint32_t pred = Select(top[0], left, top[-1]); return pred; } -static uint32_t Predictor12(uint32_t left, const uint32_t* const top) { +static uint32_t Predictor12_SSE2(uint32_t left, const uint32_t* const top) { const uint32_t pred = ClampedAddSubtractFull(left, top[0], top[-1]); return pred; } -static uint32_t Predictor13(uint32_t left, const uint32_t* const top) { +static uint32_t Predictor13_SSE2(uint32_t left, const uint32_t* const top) { const uint32_t pred = ClampedAddSubtractHalf(left, top[0], top[-1]); return pred; } +// Batch versions of those functions. + +// Predictor0: ARGB_BLACK. +static void PredictorAdd0_SSE2(const uint32_t* in, const uint32_t* upper, + int num_pixels, uint32_t* out) { + int i; + const __m128i black = _mm_set1_epi32(ARGB_BLACK); + for (i = 0; i + 4 <= num_pixels; i += 4) { + const __m128i src = _mm_loadu_si128((const __m128i*)&in[i]); + const __m128i res = _mm_add_epi8(src, black); + _mm_storeu_si128((__m128i*)&out[i], res); + } + if (i != num_pixels) { + VP8LPredictorsAdd_C[0](in + i, upper + i, num_pixels - i, out + i); + } +} + +// Predictor1: left. +static void PredictorAdd1_SSE2(const uint32_t* in, const uint32_t* upper, + int num_pixels, uint32_t* out) { + int i; + __m128i prev = _mm_set1_epi32(out[-1]); + for (i = 0; i + 4 <= num_pixels; i += 4) { + // a | b | c | d + const __m128i src = _mm_loadu_si128((const __m128i*)&in[i]); + // 0 | a | b | c + const __m128i shift0 = _mm_slli_si128(src, 4); + // a | a + b | b + c | c + d + const __m128i sum0 = _mm_add_epi8(src, shift0); + // 0 | 0 | a | a + b + const __m128i shift1 = _mm_slli_si128(sum0, 8); + // a | a + b | a + b + c | a + b + c + d + const __m128i sum1 = _mm_add_epi8(sum0, shift1); + const __m128i res = _mm_add_epi8(sum1, prev); + _mm_storeu_si128((__m128i*)&out[i], res); + // replicate prev output on the four lanes + prev = _mm_shuffle_epi32(res, (3 << 0) | (3 << 2) | (3 << 4) | (3 << 6)); + } + if (i != num_pixels) { + VP8LPredictorsAdd_C[1](in + i, upper + i, num_pixels - i, out + i); + } +} + +// Macro that adds 32-bit integers from IN using mod 256 arithmetic +// per 8 bit channel. +#define GENERATE_PREDICTOR_1(X, IN) \ +static void PredictorAdd##X##_SSE2(const uint32_t* in, const uint32_t* upper, \ + int num_pixels, uint32_t* out) { \ + int i; \ + for (i = 0; i + 4 <= num_pixels; i += 4) { \ + const __m128i src = _mm_loadu_si128((const __m128i*)&in[i]); \ + const __m128i other = _mm_loadu_si128((const __m128i*)&(IN)); \ + const __m128i res = _mm_add_epi8(src, other); \ + _mm_storeu_si128((__m128i*)&out[i], res); \ + } \ + if (i != num_pixels) { \ + VP8LPredictorsAdd_C[(X)](in + i, upper + i, num_pixels - i, out + i); \ + } \ +} + +// Predictor2: Top. +GENERATE_PREDICTOR_1(2, upper[i]) +// Predictor3: Top-right. +GENERATE_PREDICTOR_1(3, upper[i + 1]) +// Predictor4: Top-left. +GENERATE_PREDICTOR_1(4, upper[i - 1]) +#undef GENERATE_PREDICTOR_1 + +// Due to averages with integers, values cannot be accumulated in parallel for +// predictors 5 to 7. +GENERATE_PREDICTOR_ADD(Predictor5_SSE2, PredictorAdd5_SSE2) +GENERATE_PREDICTOR_ADD(Predictor6_SSE2, PredictorAdd6_SSE2) +GENERATE_PREDICTOR_ADD(Predictor7_SSE2, PredictorAdd7_SSE2) + +#define GENERATE_PREDICTOR_2(X, IN) \ +static void PredictorAdd##X##_SSE2(const uint32_t* in, const uint32_t* upper, \ + int num_pixels, uint32_t* out) { \ + int i; \ + for (i = 0; i + 4 <= num_pixels; i += 4) { \ + const __m128i Tother = _mm_loadu_si128((const __m128i*)&(IN)); \ + const __m128i T = _mm_loadu_si128((const __m128i*)&upper[i]); \ + const __m128i src = _mm_loadu_si128((const __m128i*)&in[i]); \ + __m128i avg, res; \ + Average2_m128i(&T, &Tother, &avg); \ + res = _mm_add_epi8(avg, src); \ + _mm_storeu_si128((__m128i*)&out[i], res); \ + } \ + if (i != num_pixels) { \ + VP8LPredictorsAdd_C[(X)](in + i, upper + i, num_pixels - i, out + i); \ + } \ +} +// Predictor8: average TL T. +GENERATE_PREDICTOR_2(8, upper[i - 1]) +// Predictor9: average T TR. +GENERATE_PREDICTOR_2(9, upper[i + 1]) +#undef GENERATE_PREDICTOR_2 + +// Predictor10: average of (average of (L,TL), average of (T, TR)). +static void PredictorAdd10_SSE2(const uint32_t* in, const uint32_t* upper, + int num_pixels, uint32_t* out) { + int i, j; + __m128i L = _mm_cvtsi32_si128(out[-1]); + for (i = 0; i + 4 <= num_pixels; i += 4) { + __m128i src = _mm_loadu_si128((const __m128i*)&in[i]); + __m128i TL = _mm_loadu_si128((const __m128i*)&upper[i - 1]); + const __m128i T = _mm_loadu_si128((const __m128i*)&upper[i]); + const __m128i TR = _mm_loadu_si128((const __m128i*)&upper[i + 1]); + __m128i avgTTR; + Average2_m128i(&T, &TR, &avgTTR); + for (j = 0; j < 4; ++j) { + __m128i avgLTL, avg; + Average2_m128i(&L, &TL, &avgLTL); + Average2_m128i(&avgTTR, &avgLTL, &avg); + L = _mm_add_epi8(avg, src); + out[i + j] = _mm_cvtsi128_si32(L); + // Rotate the pre-computed values for the next iteration. + avgTTR = _mm_srli_si128(avgTTR, 4); + TL = _mm_srli_si128(TL, 4); + src = _mm_srli_si128(src, 4); + } + } + if (i != num_pixels) { + VP8LPredictorsAdd_C[10](in + i, upper + i, num_pixels - i, out + i); + } +} + +// Predictor11: select. +static void GetSumAbsDiff32(const __m128i* const A, const __m128i* const B, + __m128i* const out) { + // We can unpack with any value on the upper 32 bits, provided it's the same + // on both operands (to that their sum of abs diff is zero). Here we use *A. + const __m128i A_lo = _mm_unpacklo_epi32(*A, *A); + const __m128i B_lo = _mm_unpacklo_epi32(*B, *A); + const __m128i A_hi = _mm_unpackhi_epi32(*A, *A); + const __m128i B_hi = _mm_unpackhi_epi32(*B, *A); + const __m128i s_lo = _mm_sad_epu8(A_lo, B_lo); + const __m128i s_hi = _mm_sad_epu8(A_hi, B_hi); + *out = _mm_packs_epi32(s_lo, s_hi); +} + +static void PredictorAdd11_SSE2(const uint32_t* in, const uint32_t* upper, + int num_pixels, uint32_t* out) { + int i, j; + __m128i L = _mm_cvtsi32_si128(out[-1]); + for (i = 0; i + 4 <= num_pixels; i += 4) { + __m128i T = _mm_loadu_si128((const __m128i*)&upper[i]); + __m128i TL = _mm_loadu_si128((const __m128i*)&upper[i - 1]); + __m128i src = _mm_loadu_si128((const __m128i*)&in[i]); + __m128i pa; + GetSumAbsDiff32(&T, &TL, &pa); // pa = sum |T-TL| + for (j = 0; j < 4; ++j) { + const __m128i L_lo = _mm_unpacklo_epi32(L, L); + const __m128i TL_lo = _mm_unpacklo_epi32(TL, L); + const __m128i pb = _mm_sad_epu8(L_lo, TL_lo); // pb = sum |L-TL| + const __m128i mask = _mm_cmpgt_epi32(pb, pa); + const __m128i A = _mm_and_si128(mask, L); + const __m128i B = _mm_andnot_si128(mask, T); + const __m128i pred = _mm_or_si128(A, B); // pred = (L > T)? L : T + L = _mm_add_epi8(src, pred); + out[i + j] = _mm_cvtsi128_si32(L); + // Shift the pre-computed value for the next iteration. + T = _mm_srli_si128(T, 4); + TL = _mm_srli_si128(TL, 4); + src = _mm_srli_si128(src, 4); + pa = _mm_srli_si128(pa, 4); + } + } + if (i != num_pixels) { + VP8LPredictorsAdd_C[11](in + i, upper + i, num_pixels - i, out + i); + } +} + +// Predictor12: ClampedAddSubtractFull. +#define DO_PRED12(DIFF, LANE, OUT) \ +do { \ + const __m128i all = _mm_add_epi16(L, (DIFF)); \ + const __m128i alls = _mm_packus_epi16(all, all); \ + const __m128i res = _mm_add_epi8(src, alls); \ + out[i + (OUT)] = _mm_cvtsi128_si32(res); \ + L = _mm_unpacklo_epi8(res, zero); \ + /* Shift the pre-computed value for the next iteration.*/ \ + if (LANE == 0) (DIFF) = _mm_srli_si128((DIFF), 8); \ + src = _mm_srli_si128(src, 4); \ +} while (0) + +static void PredictorAdd12_SSE2(const uint32_t* in, const uint32_t* upper, + int num_pixels, uint32_t* out) { + int i; + const __m128i zero = _mm_setzero_si128(); + const __m128i L8 = _mm_cvtsi32_si128(out[-1]); + __m128i L = _mm_unpacklo_epi8(L8, zero); + for (i = 0; i + 4 <= num_pixels; i += 4) { + // Load 4 pixels at a time. + __m128i src = _mm_loadu_si128((const __m128i*)&in[i]); + const __m128i T = _mm_loadu_si128((const __m128i*)&upper[i]); + const __m128i T_lo = _mm_unpacklo_epi8(T, zero); + const __m128i T_hi = _mm_unpackhi_epi8(T, zero); + const __m128i TL = _mm_loadu_si128((const __m128i*)&upper[i - 1]); + const __m128i TL_lo = _mm_unpacklo_epi8(TL, zero); + const __m128i TL_hi = _mm_unpackhi_epi8(TL, zero); + __m128i diff_lo = _mm_sub_epi16(T_lo, TL_lo); + __m128i diff_hi = _mm_sub_epi16(T_hi, TL_hi); + DO_PRED12(diff_lo, 0, 0); + DO_PRED12(diff_lo, 1, 1); + DO_PRED12(diff_hi, 0, 2); + DO_PRED12(diff_hi, 1, 3); + } + if (i != num_pixels) { + VP8LPredictorsAdd_C[12](in + i, upper + i, num_pixels - i, out + i); + } +} +#undef DO_PRED12 + +// Due to averages with integers, values cannot be accumulated in parallel for +// predictors 13. +GENERATE_PREDICTOR_ADD(Predictor13_SSE2, PredictorAdd13_SSE2) + //------------------------------------------------------------------------------ // Subtract-Green Transform -static void AddGreenToBlueAndRed(uint32_t* argb_data, int num_pixels) { +static void AddGreenToBlueAndRed(const uint32_t* const src, int num_pixels, + uint32_t* dst) { int i; for (i = 0; i + 4 <= num_pixels; i += 4) { - const __m128i in = _mm_loadu_si128((__m128i*)&argb_data[i]); // argb + const __m128i in = _mm_loadu_si128((const __m128i*)&src[i]); // argb const __m128i A = _mm_srli_epi16(in, 8); // 0 a 0 g const __m128i B = _mm_shufflelo_epi16(A, _MM_SHUFFLE(2, 2, 0, 0)); const __m128i C = _mm_shufflehi_epi16(B, _MM_SHUFFLE(2, 2, 0, 0)); // 0g0g const __m128i out = _mm_add_epi8(in, C); - _mm_storeu_si128((__m128i*)&argb_data[i], out); + _mm_storeu_si128((__m128i*)&dst[i], out); } // fallthrough and finish off with plain-C - VP8LAddGreenToBlueAndRed_C(argb_data + i, num_pixels - i); + if (i != num_pixels) { + VP8LAddGreenToBlueAndRed_C(src + i, num_pixels - i, dst + i); + } } //------------------------------------------------------------------------------ // Color Transform static void TransformColorInverse(const VP8LMultipliers* const m, - uint32_t* argb_data, int num_pixels) { - // sign-extended multiplying constants, pre-shifted by 5. + const uint32_t* const src, int num_pixels, + uint32_t* dst) { +// sign-extended multiplying constants, pre-shifted by 5. #define CST(X) (((int16_t)(m->X << 8)) >> 5) // sign-extend const __m128i mults_rb = _mm_set_epi16( CST(green_to_red_), CST(green_to_blue_), @@ -188,7 +431,7 @@ static void TransformColorInverse(const VP8LMultipliers* const m, const __m128i mask_ag = _mm_set1_epi32(0xff00ff00); // alpha-green masks int i; for (i = 0; i + 4 <= num_pixels; i += 4) { - const __m128i in = _mm_loadu_si128((__m128i*)&argb_data[i]); // argb + const __m128i in = _mm_loadu_si128((const __m128i*)&src[i]); // argb const __m128i A = _mm_and_si128(in, mask_ag); // a 0 g 0 const __m128i B = _mm_shufflelo_epi16(A, _MM_SHUFFLE(2, 2, 0, 0)); const __m128i C = _mm_shufflehi_epi16(B, _MM_SHUFFLE(2, 2, 0, 0)); // g0g0 @@ -200,15 +443,53 @@ static void TransformColorInverse(const VP8LMultipliers* const m, const __m128i I = _mm_add_epi8(H, F); // r' x b'' 0 const __m128i J = _mm_srli_epi16(I, 8); // 0 r' 0 b'' const __m128i out = _mm_or_si128(J, A); - _mm_storeu_si128((__m128i*)&argb_data[i], out); + _mm_storeu_si128((__m128i*)&dst[i], out); } // Fall-back to C-version for left-overs. - VP8LTransformColorInverse_C(m, argb_data + i, num_pixels - i); + if (i != num_pixels) { + VP8LTransformColorInverse_C(m, src + i, num_pixels - i, dst + i); + } } //------------------------------------------------------------------------------ // Color-space conversion functions +static void ConvertBGRAToRGB(const uint32_t* src, int num_pixels, + uint8_t* dst) { + const __m128i* in = (const __m128i*)src; + __m128i* out = (__m128i*)dst; + + while (num_pixels >= 32) { + // Load the BGRA buffers. + __m128i in0 = _mm_loadu_si128(in + 0); + __m128i in1 = _mm_loadu_si128(in + 1); + __m128i in2 = _mm_loadu_si128(in + 2); + __m128i in3 = _mm_loadu_si128(in + 3); + __m128i in4 = _mm_loadu_si128(in + 4); + __m128i in5 = _mm_loadu_si128(in + 5); + __m128i in6 = _mm_loadu_si128(in + 6); + __m128i in7 = _mm_loadu_si128(in + 7); + VP8L32bToPlanar(&in0, &in1, &in2, &in3); + VP8L32bToPlanar(&in4, &in5, &in6, &in7); + // At this points, in1/in5 contains red only, in2/in6 green only ... + // Pack the colors in 24b RGB. + VP8PlanarTo24b(&in1, &in5, &in2, &in6, &in3, &in7); + _mm_storeu_si128(out + 0, in1); + _mm_storeu_si128(out + 1, in5); + _mm_storeu_si128(out + 2, in2); + _mm_storeu_si128(out + 3, in6); + _mm_storeu_si128(out + 4, in3); + _mm_storeu_si128(out + 5, in7); + in += 8; + out += 6; + num_pixels -= 32; + } + // left-overs + if (num_pixels > 0) { + VP8LConvertBGRAToRGB_C((const uint32_t*)in, num_pixels, (uint8_t*)out); + } +} + static void ConvertBGRAToRGBA(const uint32_t* src, int num_pixels, uint8_t* dst) { const __m128i* in = (const __m128i*)src; @@ -233,7 +514,9 @@ static void ConvertBGRAToRGBA(const uint32_t* src, num_pixels -= 8; } // left-overs - VP8LConvertBGRAToRGBA_C((const uint32_t*)in, num_pixels, (uint8_t*)out); + if (num_pixels > 0) { + VP8LConvertBGRAToRGBA_C((const uint32_t*)in, num_pixels, (uint8_t*)out); + } } static void ConvertBGRAToRGBA4444(const uint32_t* src, @@ -267,7 +550,9 @@ static void ConvertBGRAToRGBA4444(const uint32_t* src, num_pixels -= 8; } // left-overs - VP8LConvertBGRAToRGBA4444_C((const uint32_t*)in, num_pixels, (uint8_t*)out); + if (num_pixels > 0) { + VP8LConvertBGRAToRGBA4444_C((const uint32_t*)in, num_pixels, (uint8_t*)out); + } } static void ConvertBGRAToRGB565(const uint32_t* src, @@ -306,7 +591,9 @@ static void ConvertBGRAToRGB565(const uint32_t* src, num_pixels -= 8; } // left-overs - VP8LConvertBGRAToRGB565_C((const uint32_t*)in, num_pixels, (uint8_t*)out); + if (num_pixels > 0) { + VP8LConvertBGRAToRGB565_C((const uint32_t*)in, num_pixels, (uint8_t*)out); + } } static void ConvertBGRAToBGR(const uint32_t* src, @@ -337,7 +624,9 @@ static void ConvertBGRAToBGR(const uint32_t* src, num_pixels -= 8; } // left-overs - VP8LConvertBGRAToBGR_C((const uint32_t*)in, num_pixels, dst); + if (num_pixels > 0) { + VP8LConvertBGRAToBGR_C((const uint32_t*)in, num_pixels, dst); + } } //------------------------------------------------------------------------------ @@ -346,19 +635,35 @@ static void ConvertBGRAToBGR(const uint32_t* src, extern void VP8LDspInitSSE2(void); WEBP_TSAN_IGNORE_FUNCTION void VP8LDspInitSSE2(void) { - VP8LPredictors[5] = Predictor5; - VP8LPredictors[6] = Predictor6; - VP8LPredictors[7] = Predictor7; - VP8LPredictors[8] = Predictor8; - VP8LPredictors[9] = Predictor9; - VP8LPredictors[10] = Predictor10; - VP8LPredictors[11] = Predictor11; - VP8LPredictors[12] = Predictor12; - VP8LPredictors[13] = Predictor13; + VP8LPredictors[5] = Predictor5_SSE2; + VP8LPredictors[6] = Predictor6_SSE2; + VP8LPredictors[7] = Predictor7_SSE2; + VP8LPredictors[8] = Predictor8_SSE2; + VP8LPredictors[9] = Predictor9_SSE2; + VP8LPredictors[10] = Predictor10_SSE2; + VP8LPredictors[11] = Predictor11_SSE2; + VP8LPredictors[12] = Predictor12_SSE2; + VP8LPredictors[13] = Predictor13_SSE2; + + VP8LPredictorsAdd[0] = PredictorAdd0_SSE2; + VP8LPredictorsAdd[1] = PredictorAdd1_SSE2; + VP8LPredictorsAdd[2] = PredictorAdd2_SSE2; + VP8LPredictorsAdd[3] = PredictorAdd3_SSE2; + VP8LPredictorsAdd[4] = PredictorAdd4_SSE2; + VP8LPredictorsAdd[5] = PredictorAdd5_SSE2; + VP8LPredictorsAdd[6] = PredictorAdd6_SSE2; + VP8LPredictorsAdd[7] = PredictorAdd7_SSE2; + VP8LPredictorsAdd[8] = PredictorAdd8_SSE2; + VP8LPredictorsAdd[9] = PredictorAdd9_SSE2; + VP8LPredictorsAdd[10] = PredictorAdd10_SSE2; + VP8LPredictorsAdd[11] = PredictorAdd11_SSE2; + VP8LPredictorsAdd[12] = PredictorAdd12_SSE2; + VP8LPredictorsAdd[13] = PredictorAdd13_SSE2; VP8LAddGreenToBlueAndRed = AddGreenToBlueAndRed; VP8LTransformColorInverse = TransformColorInverse; + VP8LConvertBGRAToRGB = ConvertBGRAToRGB; VP8LConvertBGRAToRGBA = ConvertBGRAToRGBA; VP8LConvertBGRAToRGBA4444 = ConvertBGRAToRGBA4444; VP8LConvertBGRAToRGB565 = ConvertBGRAToRGB565; diff --git a/thirdparty/libwebp/dsp/msa_macro.h b/thirdparty/libwebp/dsp/msa_macro.h index 5c707f476a..d0e5f45e01 100644 --- a/thirdparty/libwebp/dsp/msa_macro.h +++ b/thirdparty/libwebp/dsp/msa_macro.h @@ -23,12 +23,24 @@ #ifdef CLANG_BUILD #define ADDVI_H(a, b) __msa_addvi_h((v8i16)a, b) + #define ADDVI_W(a, b) __msa_addvi_w((v4i32)a, b) + #define SRAI_B(a, b) __msa_srai_b((v16i8)a, b) #define SRAI_H(a, b) __msa_srai_h((v8i16)a, b) #define SRAI_W(a, b) __msa_srai_w((v4i32)a, b) + #define SRLI_H(a, b) __msa_srli_h((v8i16)a, b) + #define SLLI_B(a, b) __msa_slli_b((v4i32)a, b) + #define ANDI_B(a, b) __msa_andi_b((v16u8)a, b) + #define ORI_B(a, b) __msa_ori_b((v16u8)a, b) #else #define ADDVI_H(a, b) (a + b) + #define ADDVI_W(a, b) (a + b) + #define SRAI_B(a, b) (a >> b) #define SRAI_H(a, b) (a >> b) #define SRAI_W(a, b) (a >> b) + #define SRLI_H(a, b) (a << b) + #define SLLI_B(a, b) (a << b) + #define ANDI_B(a, b) (a & b) + #define ORI_B(a, b) (a | b) #endif #define LD_B(RTYPE, psrc) *((RTYPE*)(psrc)) @@ -116,13 +128,13 @@ #define SH(val, pdst) MSA_STORE(val, pdst, msa_ush) MSA_STORE_FUNC(uint32_t, usw, msa_usw); #define SW(val, pdst) MSA_STORE(val, pdst, msa_usw) - #define SD(val, pdst) { \ + #define SD(val, pdst) do { \ uint8_t* const pdst_sd_m = (uint8_t*)(pdst); \ const uint32_t val0_m = (uint32_t)(val & 0x00000000FFFFFFFF); \ const uint32_t val1_m = (uint32_t)((val >> 32) & 0x00000000FFFFFFFF); \ SW(val0_m, pdst_sd_m); \ SW(val1_m, pdst_sd_m + 4); \ - } + } while (0) #endif // (__mips_isa_rev >= 6) /* Description : Load 4 words with stride @@ -133,34 +145,68 @@ * Load word in 'out2' from (psrc + 2 * stride) * Load word in 'out3' from (psrc + 3 * stride) */ -#define LW4(psrc, stride, out0, out1, out2, out3) { \ - const uint8_t* ptmp = (const uint8_t*)psrc; \ - out0 = LW(ptmp); \ - ptmp += stride; \ - out1 = LW(ptmp); \ - ptmp += stride; \ - out2 = LW(ptmp); \ - ptmp += stride; \ - out3 = LW(ptmp); \ -} +#define LW4(psrc, stride, out0, out1, out2, out3) do { \ + const uint8_t* ptmp = (const uint8_t*)psrc; \ + out0 = LW(ptmp); \ + ptmp += stride; \ + out1 = LW(ptmp); \ + ptmp += stride; \ + out2 = LW(ptmp); \ + ptmp += stride; \ + out3 = LW(ptmp); \ +} while (0) -/* Description : Store 4 words with stride +/* Description : Store words with stride * Arguments : Inputs - in0, in1, in2, in3, pdst, stride * Details : Store word from 'in0' to (pdst) * Store word from 'in1' to (pdst + stride) * Store word from 'in2' to (pdst + 2 * stride) * Store word from 'in3' to (pdst + 3 * stride) */ -#define SW4(in0, in1, in2, in3, pdst, stride) { \ - uint8_t* ptmp = (uint8_t*)pdst; \ - SW(in0, ptmp); \ - ptmp += stride; \ - SW(in1, ptmp); \ - ptmp += stride; \ - SW(in2, ptmp); \ - ptmp += stride; \ - SW(in3, ptmp); \ -} +#define SW4(in0, in1, in2, in3, pdst, stride) do { \ + uint8_t* ptmp = (uint8_t*)pdst; \ + SW(in0, ptmp); \ + ptmp += stride; \ + SW(in1, ptmp); \ + ptmp += stride; \ + SW(in2, ptmp); \ + ptmp += stride; \ + SW(in3, ptmp); \ +} while (0) + +#define SW3(in0, in1, in2, pdst, stride) do { \ + uint8_t* ptmp = (uint8_t*)pdst; \ + SW(in0, ptmp); \ + ptmp += stride; \ + SW(in1, ptmp); \ + ptmp += stride; \ + SW(in2, ptmp); \ +} while (0) + +#define SW2(in0, in1, pdst, stride) do { \ + uint8_t* ptmp = (uint8_t*)pdst; \ + SW(in0, ptmp); \ + ptmp += stride; \ + SW(in1, ptmp); \ +} while (0) + +/* Description : Store 4 double words with stride + * Arguments : Inputs - in0, in1, in2, in3, pdst, stride + * Details : Store double word from 'in0' to (pdst) + * Store double word from 'in1' to (pdst + stride) + * Store double word from 'in2' to (pdst + 2 * stride) + * Store double word from 'in3' to (pdst + 3 * stride) + */ +#define SD4(in0, in1, in2, in3, pdst, stride) do { \ + uint8_t* ptmp = (uint8_t*)pdst; \ + SD(in0, ptmp); \ + ptmp += stride; \ + SD(in1, ptmp); \ + ptmp += stride; \ + SD(in2, ptmp); \ + ptmp += stride; \ + SD(in3, ptmp); \ +} while (0) /* Description : Load vectors with 16 byte elements with stride * Arguments : Inputs - psrc, stride @@ -169,33 +215,169 @@ * Details : Load 16 byte elements in 'out0' from (psrc) * Load 16 byte elements in 'out1' from (psrc + stride) */ -#define LD_B2(RTYPE, psrc, stride, out0, out1) { \ - out0 = LD_B(RTYPE, psrc); \ - out1 = LD_B(RTYPE, psrc + stride); \ -} +#define LD_B2(RTYPE, psrc, stride, out0, out1) do { \ + out0 = LD_B(RTYPE, psrc); \ + out1 = LD_B(RTYPE, psrc + stride); \ +} while (0) #define LD_UB2(...) LD_B2(v16u8, __VA_ARGS__) #define LD_SB2(...) LD_B2(v16i8, __VA_ARGS__) -#define LD_B4(RTYPE, psrc, stride, out0, out1, out2, out3) { \ - LD_B2(RTYPE, psrc, stride, out0, out1); \ - LD_B2(RTYPE, psrc + 2 * stride , stride, out2, out3); \ -} +#define LD_B3(RTYPE, psrc, stride, out0, out1, out2) do { \ + LD_B2(RTYPE, psrc, stride, out0, out1); \ + out2 = LD_B(RTYPE, psrc + 2 * stride); \ +} while (0) +#define LD_UB3(...) LD_B3(v16u8, __VA_ARGS__) +#define LD_SB3(...) LD_B3(v16i8, __VA_ARGS__) + +#define LD_B4(RTYPE, psrc, stride, out0, out1, out2, out3) do { \ + LD_B2(RTYPE, psrc, stride, out0, out1); \ + LD_B2(RTYPE, psrc + 2 * stride , stride, out2, out3); \ +} while (0) #define LD_UB4(...) LD_B4(v16u8, __VA_ARGS__) #define LD_SB4(...) LD_B4(v16i8, __VA_ARGS__) +#define LD_B8(RTYPE, psrc, stride, \ + out0, out1, out2, out3, out4, out5, out6, out7) do { \ + LD_B4(RTYPE, psrc, stride, out0, out1, out2, out3); \ + LD_B4(RTYPE, psrc + 4 * stride, stride, out4, out5, out6, out7); \ +} while (0) +#define LD_UB8(...) LD_B8(v16u8, __VA_ARGS__) +#define LD_SB8(...) LD_B8(v16i8, __VA_ARGS__) + /* Description : Load vectors with 8 halfword elements with stride * Arguments : Inputs - psrc, stride * Outputs - out0, out1 * Details : Load 8 halfword elements in 'out0' from (psrc) * Load 8 halfword elements in 'out1' from (psrc + stride) */ -#define LD_H2(RTYPE, psrc, stride, out0, out1) { \ - out0 = LD_H(RTYPE, psrc); \ - out1 = LD_H(RTYPE, psrc + stride); \ -} +#define LD_H2(RTYPE, psrc, stride, out0, out1) do { \ + out0 = LD_H(RTYPE, psrc); \ + out1 = LD_H(RTYPE, psrc + stride); \ +} while (0) #define LD_UH2(...) LD_H2(v8u16, __VA_ARGS__) #define LD_SH2(...) LD_H2(v8i16, __VA_ARGS__) +/* Description : Load vectors with 4 word elements with stride + * Arguments : Inputs - psrc, stride + * Outputs - out0, out1, out2, out3 + * Details : Load 4 word elements in 'out0' from (psrc + 0 * stride) + * Load 4 word elements in 'out1' from (psrc + 1 * stride) + * Load 4 word elements in 'out2' from (psrc + 2 * stride) + * Load 4 word elements in 'out3' from (psrc + 3 * stride) + */ +#define LD_W2(RTYPE, psrc, stride, out0, out1) do { \ + out0 = LD_W(RTYPE, psrc); \ + out1 = LD_W(RTYPE, psrc + stride); \ +} while (0) +#define LD_UW2(...) LD_W2(v4u32, __VA_ARGS__) +#define LD_SW2(...) LD_W2(v4i32, __VA_ARGS__) + +#define LD_W3(RTYPE, psrc, stride, out0, out1, out2) do { \ + LD_W2(RTYPE, psrc, stride, out0, out1); \ + out2 = LD_W(RTYPE, psrc + 2 * stride); \ +} while (0) +#define LD_UW3(...) LD_W3(v4u32, __VA_ARGS__) +#define LD_SW3(...) LD_W3(v4i32, __VA_ARGS__) + +#define LD_W4(RTYPE, psrc, stride, out0, out1, out2, out3) do { \ + LD_W2(RTYPE, psrc, stride, out0, out1); \ + LD_W2(RTYPE, psrc + 2 * stride, stride, out2, out3); \ +} while (0) +#define LD_UW4(...) LD_W4(v4u32, __VA_ARGS__) +#define LD_SW4(...) LD_W4(v4i32, __VA_ARGS__) + +/* Description : Store vectors of 16 byte elements with stride + * Arguments : Inputs - in0, in1, pdst, stride + * Details : Store 16 byte elements from 'in0' to (pdst) + * Store 16 byte elements from 'in1' to (pdst + stride) + */ +#define ST_B2(RTYPE, in0, in1, pdst, stride) do { \ + ST_B(RTYPE, in0, pdst); \ + ST_B(RTYPE, in1, pdst + stride); \ +} while (0) +#define ST_UB2(...) ST_B2(v16u8, __VA_ARGS__) +#define ST_SB2(...) ST_B2(v16i8, __VA_ARGS__) + +#define ST_B4(RTYPE, in0, in1, in2, in3, pdst, stride) do { \ + ST_B2(RTYPE, in0, in1, pdst, stride); \ + ST_B2(RTYPE, in2, in3, pdst + 2 * stride, stride); \ +} while (0) +#define ST_UB4(...) ST_B4(v16u8, __VA_ARGS__) +#define ST_SB4(...) ST_B4(v16i8, __VA_ARGS__) + +#define ST_B8(RTYPE, in0, in1, in2, in3, in4, in5, in6, in7, \ + pdst, stride) do { \ + ST_B4(RTYPE, in0, in1, in2, in3, pdst, stride); \ + ST_B4(RTYPE, in4, in5, in6, in7, pdst + 4 * stride, stride); \ +} while (0) +#define ST_UB8(...) ST_B8(v16u8, __VA_ARGS__) + +/* Description : Store vectors of 4 word elements with stride + * Arguments : Inputs - in0, in1, in2, in3, pdst, stride + * Details : Store 4 word elements from 'in0' to (pdst + 0 * stride) + * Store 4 word elements from 'in1' to (pdst + 1 * stride) + * Store 4 word elements from 'in2' to (pdst + 2 * stride) + * Store 4 word elements from 'in3' to (pdst + 3 * stride) + */ +#define ST_W2(RTYPE, in0, in1, pdst, stride) do { \ + ST_W(RTYPE, in0, pdst); \ + ST_W(RTYPE, in1, pdst + stride); \ +} while (0) +#define ST_UW2(...) ST_W2(v4u32, __VA_ARGS__) +#define ST_SW2(...) ST_W2(v4i32, __VA_ARGS__) + +#define ST_W3(RTYPE, in0, in1, in2, pdst, stride) do { \ + ST_W2(RTYPE, in0, in1, pdst, stride); \ + ST_W(RTYPE, in2, pdst + 2 * stride); \ +} while (0) +#define ST_UW3(...) ST_W3(v4u32, __VA_ARGS__) +#define ST_SW3(...) ST_W3(v4i32, __VA_ARGS__) + +#define ST_W4(RTYPE, in0, in1, in2, in3, pdst, stride) do { \ + ST_W2(RTYPE, in0, in1, pdst, stride); \ + ST_W2(RTYPE, in2, in3, pdst + 2 * stride, stride); \ +} while (0) +#define ST_UW4(...) ST_W4(v4u32, __VA_ARGS__) +#define ST_SW4(...) ST_W4(v4i32, __VA_ARGS__) + +/* Description : Store vectors of 8 halfword elements with stride + * Arguments : Inputs - in0, in1, pdst, stride + * Details : Store 8 halfword elements from 'in0' to (pdst) + * Store 8 halfword elements from 'in1' to (pdst + stride) + */ +#define ST_H2(RTYPE, in0, in1, pdst, stride) do { \ + ST_H(RTYPE, in0, pdst); \ + ST_H(RTYPE, in1, pdst + stride); \ +} while (0) +#define ST_UH2(...) ST_H2(v8u16, __VA_ARGS__) +#define ST_SH2(...) ST_H2(v8i16, __VA_ARGS__) + +/* Description : Store 2x4 byte block to destination memory from input vector + * Arguments : Inputs - in, stidx, pdst, stride + * Details : Index 'stidx' halfword element from 'in' vector is copied to + * the GP register and stored to (pdst) + * Index 'stidx+1' halfword element from 'in' vector is copied to + * the GP register and stored to (pdst + stride) + * Index 'stidx+2' halfword element from 'in' vector is copied to + * the GP register and stored to (pdst + 2 * stride) + * Index 'stidx+3' halfword element from 'in' vector is copied to + * the GP register and stored to (pdst + 3 * stride) + */ +#define ST2x4_UB(in, stidx, pdst, stride) do { \ + uint8_t* pblk_2x4_m = (uint8_t*)pdst; \ + const uint16_t out0_m = __msa_copy_s_h((v8i16)in, stidx); \ + const uint16_t out1_m = __msa_copy_s_h((v8i16)in, stidx + 1); \ + const uint16_t out2_m = __msa_copy_s_h((v8i16)in, stidx + 2); \ + const uint16_t out3_m = __msa_copy_s_h((v8i16)in, stidx + 3); \ + SH(out0_m, pblk_2x4_m); \ + pblk_2x4_m += stride; \ + SH(out1_m, pblk_2x4_m); \ + pblk_2x4_m += stride; \ + SH(out2_m, pblk_2x4_m); \ + pblk_2x4_m += stride; \ + SH(out3_m, pblk_2x4_m); \ +} while (0) + /* Description : Store 4x4 byte block to destination memory from input vector * Arguments : Inputs - in0, in1, pdst, stride * Details : 'Idx0' word element from input vector 'in0' is copied to the @@ -207,14 +389,20 @@ * 'Idx3' word element from input vector 'in0' is copied to the * GP register and stored to (pdst + 3 * stride) */ -#define ST4x4_UB(in0, in1, idx0, idx1, idx2, idx3, pdst, stride) { \ - uint8_t* const pblk_4x4_m = (uint8_t*)pdst; \ - const uint32_t out0_m = __msa_copy_s_w((v4i32)in0, idx0); \ - const uint32_t out1_m = __msa_copy_s_w((v4i32)in0, idx1); \ - const uint32_t out2_m = __msa_copy_s_w((v4i32)in1, idx2); \ - const uint32_t out3_m = __msa_copy_s_w((v4i32)in1, idx3); \ - SW4(out0_m, out1_m, out2_m, out3_m, pblk_4x4_m, stride); \ -} +#define ST4x4_UB(in0, in1, idx0, idx1, idx2, idx3, pdst, stride) do { \ + uint8_t* const pblk_4x4_m = (uint8_t*)pdst; \ + const uint32_t out0_m = __msa_copy_s_w((v4i32)in0, idx0); \ + const uint32_t out1_m = __msa_copy_s_w((v4i32)in0, idx1); \ + const uint32_t out2_m = __msa_copy_s_w((v4i32)in1, idx2); \ + const uint32_t out3_m = __msa_copy_s_w((v4i32)in1, idx3); \ + SW4(out0_m, out1_m, out2_m, out3_m, pblk_4x4_m, stride); \ +} while (0) + +#define ST4x8_UB(in0, in1, pdst, stride) do { \ + uint8_t* const pblk_4x8 = (uint8_t*)pdst; \ + ST4x4_UB(in0, in0, 0, 1, 2, 3, pblk_4x8, stride); \ + ST4x4_UB(in1, in1, 0, 1, 2, 3, pblk_4x8 + 4 * stride, stride); \ +} while (0) /* Description : Immediate number of elements to slide * Arguments : Inputs - in0, in1, slide_val @@ -230,6 +418,30 @@ #define SLDI_SB(...) SLDI_B(v16i8, __VA_ARGS__) #define SLDI_SH(...) SLDI_B(v8i16, __VA_ARGS__) +/* Description : Shuffle byte vector elements as per mask vector + * Arguments : Inputs - in0, in1, in2, in3, mask0, mask1 + * Outputs - out0, out1 + * Return Type - as per RTYPE + * Details : Byte elements from 'in0' & 'in1' are copied selectively to + * 'out0' as per control vector 'mask0' + */ +#define VSHF_B(RTYPE, in0, in1, mask) \ + (RTYPE)__msa_vshf_b((v16i8)mask, (v16i8)in1, (v16i8)in0) + +#define VSHF_UB(...) VSHF_B(v16u8, __VA_ARGS__) +#define VSHF_SB(...) VSHF_B(v16i8, __VA_ARGS__) +#define VSHF_UH(...) VSHF_B(v8u16, __VA_ARGS__) +#define VSHF_SH(...) VSHF_B(v8i16, __VA_ARGS__) + +#define VSHF_B2(RTYPE, in0, in1, in2, in3, mask0, mask1, out0, out1) do { \ + out0 = VSHF_B(RTYPE, in0, in1, mask0); \ + out1 = VSHF_B(RTYPE, in2, in3, mask1); \ +} while (0) +#define VSHF_B2_UB(...) VSHF_B2(v16u8, __VA_ARGS__) +#define VSHF_B2_SB(...) VSHF_B2(v16i8, __VA_ARGS__) +#define VSHF_B2_UH(...) VSHF_B2(v8u16, __VA_ARGS__) +#define VSHF_B2_SH(...) VSHF_B2(v8i16, __VA_ARGS__) + /* Description : Shuffle halfword vector elements as per mask vector * Arguments : Inputs - in0, in1, in2, in3, mask0, mask1 * Outputs - out0, out1 @@ -237,44 +449,219 @@ * Details : halfword elements from 'in0' & 'in1' are copied selectively to * 'out0' as per control vector 'mask0' */ -#define VSHF_H2(RTYPE, in0, in1, in2, in3, mask0, mask1, out0, out1) { \ - out0 = (RTYPE)__msa_vshf_h((v8i16)mask0, (v8i16)in1, (v8i16)in0); \ - out1 = (RTYPE)__msa_vshf_h((v8i16)mask1, (v8i16)in3, (v8i16)in2); \ -} +#define VSHF_H2(RTYPE, in0, in1, in2, in3, mask0, mask1, out0, out1) do { \ + out0 = (RTYPE)__msa_vshf_h((v8i16)mask0, (v8i16)in1, (v8i16)in0); \ + out1 = (RTYPE)__msa_vshf_h((v8i16)mask1, (v8i16)in3, (v8i16)in2); \ +} while (0) #define VSHF_H2_UH(...) VSHF_H2(v8u16, __VA_ARGS__) #define VSHF_H2_SH(...) VSHF_H2(v8i16, __VA_ARGS__) +/* Description : Dot product of byte vector elements + * Arguments : Inputs - mult0, mult1, cnst0, cnst1 + * Outputs - out0, out1 + * Return Type - as per RTYPE + * Details : Signed byte elements from 'mult0' are multiplied with + * signed byte elements from 'cnst0' producing a result + * twice the size of input i.e. signed halfword. + * The multiplication result of adjacent odd-even elements + * are added together and written to the 'out0' vector +*/ +#define DOTP_SB2(RTYPE, mult0, mult1, cnst0, cnst1, out0, out1) do { \ + out0 = (RTYPE)__msa_dotp_s_h((v16i8)mult0, (v16i8)cnst0); \ + out1 = (RTYPE)__msa_dotp_s_h((v16i8)mult1, (v16i8)cnst1); \ +} while (0) +#define DOTP_SB2_SH(...) DOTP_SB2(v8i16, __VA_ARGS__) + +/* Description : Dot product of halfword vector elements + * Arguments : Inputs - mult0, mult1, cnst0, cnst1 + * Outputs - out0, out1 + * Return Type - as per RTYPE + * Details : Signed halfword elements from 'mult0' are multiplied with + * signed halfword elements from 'cnst0' producing a result + * twice the size of input i.e. signed word. + * The multiplication result of adjacent odd-even elements + * are added together and written to the 'out0' vector + */ +#define DOTP_SH2(RTYPE, mult0, mult1, cnst0, cnst1, out0, out1) do { \ + out0 = (RTYPE)__msa_dotp_s_w((v8i16)mult0, (v8i16)cnst0); \ + out1 = (RTYPE)__msa_dotp_s_w((v8i16)mult1, (v8i16)cnst1); \ +} while (0) +#define DOTP_SH2_SW(...) DOTP_SH2(v4i32, __VA_ARGS__) + +/* Description : Dot product of unsigned word vector elements + * Arguments : Inputs - mult0, mult1, cnst0, cnst1 + * Outputs - out0, out1 + * Return Type - as per RTYPE + * Details : Unsigned word elements from 'mult0' are multiplied with + * unsigned word elements from 'cnst0' producing a result + * twice the size of input i.e. unsigned double word. + * The multiplication result of adjacent odd-even elements + * are added together and written to the 'out0' vector + */ +#define DOTP_UW2(RTYPE, mult0, mult1, cnst0, cnst1, out0, out1) do { \ + out0 = (RTYPE)__msa_dotp_u_d((v4u32)mult0, (v4u32)cnst0); \ + out1 = (RTYPE)__msa_dotp_u_d((v4u32)mult1, (v4u32)cnst1); \ +} while (0) +#define DOTP_UW2_UD(...) DOTP_UW2(v2u64, __VA_ARGS__) + +/* Description : Dot product & addition of halfword vector elements + * Arguments : Inputs - mult0, mult1, cnst0, cnst1 + * Outputs - out0, out1 + * Return Type - as per RTYPE + * Details : Signed halfword elements from 'mult0' are multiplied with + * signed halfword elements from 'cnst0' producing a result + * twice the size of input i.e. signed word. + * The multiplication result of adjacent odd-even elements + * are added to the 'out0' vector + */ +#define DPADD_SH2(RTYPE, mult0, mult1, cnst0, cnst1, out0, out1) do { \ + out0 = (RTYPE)__msa_dpadd_s_w((v4i32)out0, (v8i16)mult0, (v8i16)cnst0); \ + out1 = (RTYPE)__msa_dpadd_s_w((v4i32)out1, (v8i16)mult1, (v8i16)cnst1); \ +} while (0) +#define DPADD_SH2_SW(...) DPADD_SH2(v4i32, __VA_ARGS__) + /* Description : Clips all signed halfword elements of input vector * between 0 & 255 * Arguments : Input/output - val * Return Type - signed halfword */ -#define CLIP_SH_0_255(val) { \ +#define CLIP_SH_0_255(val) do { \ const v8i16 max_m = __msa_ldi_h(255); \ val = __msa_maxi_s_h((v8i16)val, 0); \ val = __msa_min_s_h(max_m, (v8i16)val); \ -} -#define CLIP_SH2_0_255(in0, in1) { \ - CLIP_SH_0_255(in0); \ - CLIP_SH_0_255(in1); \ -} +} while (0) + +#define CLIP_SH2_0_255(in0, in1) do { \ + CLIP_SH_0_255(in0); \ + CLIP_SH_0_255(in1); \ +} while (0) + +#define CLIP_SH4_0_255(in0, in1, in2, in3) do { \ + CLIP_SH2_0_255(in0, in1); \ + CLIP_SH2_0_255(in2, in3); \ +} while (0) + +/* Description : Clips all unsigned halfword elements of input vector + * between 0 & 255 + * Arguments : Input - in + * Output - out_m + * Return Type - unsigned halfword + */ +#define CLIP_UH_0_255(in) do { \ + const v8u16 max_m = (v8u16)__msa_ldi_h(255); \ + in = __msa_maxi_u_h((v8u16) in, 0); \ + in = __msa_min_u_h((v8u16) max_m, (v8u16) in); \ +} while (0) + +#define CLIP_UH2_0_255(in0, in1) do { \ + CLIP_UH_0_255(in0); \ + CLIP_UH_0_255(in1); \ +} while (0) /* Description : Clips all signed word elements of input vector * between 0 & 255 * Arguments : Input/output - val * Return Type - signed word */ -#define CLIP_SW_0_255(val) { \ +#define CLIP_SW_0_255(val) do { \ const v4i32 max_m = __msa_ldi_w(255); \ val = __msa_maxi_s_w((v4i32)val, 0); \ val = __msa_min_s_w(max_m, (v4i32)val); \ +} while (0) + +#define CLIP_SW4_0_255(in0, in1, in2, in3) do { \ + CLIP_SW_0_255(in0); \ + CLIP_SW_0_255(in1); \ + CLIP_SW_0_255(in2); \ + CLIP_SW_0_255(in3); \ +} while (0) + +/* Description : Horizontal addition of 4 signed word elements of input vector + * Arguments : Input - in (signed word vector) + * Output - sum_m (i32 sum) + * Return Type - signed word (GP) + * Details : 4 signed word elements of 'in' vector are added together and + * the resulting integer sum is returned + */ +static WEBP_INLINE int32_t func_hadd_sw_s32(v4i32 in) { + const v2i64 res0_m = __msa_hadd_s_d((v4i32)in, (v4i32)in); + const v2i64 res1_m = __msa_splati_d(res0_m, 1); + const v2i64 out = res0_m + res1_m; + int32_t sum_m = __msa_copy_s_w((v4i32)out, 0); + return sum_m; } -#define CLIP_SW4_0_255(in0, in1, in2, in3) { \ - CLIP_SW_0_255(in0); \ - CLIP_SW_0_255(in1); \ - CLIP_SW_0_255(in2); \ - CLIP_SW_0_255(in3); \ +#define HADD_SW_S32(in) func_hadd_sw_s32(in) + +/* Description : Horizontal addition of 8 signed halfword elements + * Arguments : Input - in (signed halfword vector) + * Output - sum_m (s32 sum) + * Return Type - signed word + * Details : 8 signed halfword elements of input vector are added + * together and the resulting integer sum is returned + */ +static WEBP_INLINE int32_t func_hadd_sh_s32(v8i16 in) { + const v4i32 res = __msa_hadd_s_w(in, in); + const v2i64 res0 = __msa_hadd_s_d(res, res); + const v2i64 res1 = __msa_splati_d(res0, 1); + const v2i64 res2 = res0 + res1; + const int32_t sum_m = __msa_copy_s_w((v4i32)res2, 0); + return sum_m; +} +#define HADD_SH_S32(in) func_hadd_sh_s32(in) + +/* Description : Horizontal addition of 8 unsigned halfword elements + * Arguments : Input - in (unsigned halfword vector) + * Output - sum_m (u32 sum) + * Return Type - unsigned word + * Details : 8 unsigned halfword elements of input vector are added + * together and the resulting integer sum is returned + */ +static WEBP_INLINE uint32_t func_hadd_uh_u32(v8u16 in) { + uint32_t sum_m; + const v4u32 res_m = __msa_hadd_u_w(in, in); + v2u64 res0_m = __msa_hadd_u_d(res_m, res_m); + v2u64 res1_m = (v2u64)__msa_splati_d((v2i64)res0_m, 1); + res0_m = res0_m + res1_m; + sum_m = __msa_copy_s_w((v4i32)res0_m, 0); + return sum_m; } +#define HADD_UH_U32(in) func_hadd_uh_u32(in) + +/* Description : Horizontal addition of signed half word vector elements + Arguments : Inputs - in0, in1 + Outputs - out0, out1 + Return Type - as per RTYPE + Details : Each signed odd half word element from 'in0' is added to + even signed half word element from 'in0' (pairwise) and the + halfword result is written in 'out0' +*/ +#define HADD_SH2(RTYPE, in0, in1, out0, out1) do { \ + out0 = (RTYPE)__msa_hadd_s_w((v8i16)in0, (v8i16)in0); \ + out1 = (RTYPE)__msa_hadd_s_w((v8i16)in1, (v8i16)in1); \ +} while (0) +#define HADD_SH2_SW(...) HADD_SH2(v4i32, __VA_ARGS__) + +#define HADD_SH4(RTYPE, in0, in1, in2, in3, out0, out1, out2, out3) do { \ + HADD_SH2(RTYPE, in0, in1, out0, out1); \ + HADD_SH2(RTYPE, in2, in3, out2, out3); \ +} while (0) +#define HADD_SH4_SW(...) HADD_SH4(v4i32, __VA_ARGS__) + +/* Description : Horizontal subtraction of unsigned byte vector elements + * Arguments : Inputs - in0, in1 + * Outputs - out0, out1 + * Return Type - as per RTYPE + * Details : Each unsigned odd byte element from 'in0' is subtracted from + * even unsigned byte element from 'in0' (pairwise) and the + * halfword result is written to 'out0' + */ +#define HSUB_UB2(RTYPE, in0, in1, out0, out1) do { \ + out0 = (RTYPE)__msa_hsub_u_h((v16u8)in0, (v16u8)in0); \ + out1 = (RTYPE)__msa_hsub_u_h((v16u8)in1, (v16u8)in1); \ +} while (0) +#define HSUB_UB2_UH(...) HSUB_UB2(v8u16, __VA_ARGS__) +#define HSUB_UB2_SH(...) HSUB_UB2(v8i16, __VA_ARGS__) +#define HSUB_UB2_SW(...) HSUB_UB2(v4i32, __VA_ARGS__) /* Description : Set element n input vector to GPR value * Arguments : Inputs - in0, in1, in2, in3 @@ -282,23 +669,188 @@ * Return Type - as per RTYPE * Details : Set element 0 in vector 'out' to value specified in 'in0' */ -#define INSERT_W2(RTYPE, in0, in1, out) { \ +#define INSERT_W2(RTYPE, in0, in1, out) do { \ out = (RTYPE)__msa_insert_w((v4i32)out, 0, in0); \ out = (RTYPE)__msa_insert_w((v4i32)out, 1, in1); \ -} +} while (0) #define INSERT_W2_UB(...) INSERT_W2(v16u8, __VA_ARGS__) #define INSERT_W2_SB(...) INSERT_W2(v16i8, __VA_ARGS__) -#define INSERT_W4(RTYPE, in0, in1, in2, in3, out) { \ - out = (RTYPE)__msa_insert_w((v4i32)out, 0, in0); \ - out = (RTYPE)__msa_insert_w((v4i32)out, 1, in1); \ - out = (RTYPE)__msa_insert_w((v4i32)out, 2, in2); \ - out = (RTYPE)__msa_insert_w((v4i32)out, 3, in3); \ -} +#define INSERT_W4(RTYPE, in0, in1, in2, in3, out) do { \ + out = (RTYPE)__msa_insert_w((v4i32)out, 0, in0); \ + out = (RTYPE)__msa_insert_w((v4i32)out, 1, in1); \ + out = (RTYPE)__msa_insert_w((v4i32)out, 2, in2); \ + out = (RTYPE)__msa_insert_w((v4i32)out, 3, in3); \ +} while (0) #define INSERT_W4_UB(...) INSERT_W4(v16u8, __VA_ARGS__) #define INSERT_W4_SB(...) INSERT_W4(v16i8, __VA_ARGS__) #define INSERT_W4_SW(...) INSERT_W4(v4i32, __VA_ARGS__) +/* Description : Set element n of double word input vector to GPR value + * Arguments : Inputs - in0, in1 + * Output - out + * Return Type - as per RTYPE + * Details : Set element 0 in vector 'out' to GPR value specified in 'in0' + * Set element 1 in vector 'out' to GPR value specified in 'in1' + */ +#define INSERT_D2(RTYPE, in0, in1, out) do { \ + out = (RTYPE)__msa_insert_d((v2i64)out, 0, in0); \ + out = (RTYPE)__msa_insert_d((v2i64)out, 1, in1); \ +} while (0) +#define INSERT_D2_UB(...) INSERT_D2(v16u8, __VA_ARGS__) +#define INSERT_D2_SB(...) INSERT_D2(v16i8, __VA_ARGS__) + +/* Description : Interleave even byte elements from vectors + * Arguments : Inputs - in0, in1, in2, in3 + * Outputs - out0, out1 + * Return Type - as per RTYPE + * Details : Even byte elements of 'in0' and 'in1' are interleaved + * and written to 'out0' + */ +#define ILVEV_B2(RTYPE, in0, in1, in2, in3, out0, out1) do { \ + out0 = (RTYPE)__msa_ilvev_b((v16i8)in1, (v16i8)in0); \ + out1 = (RTYPE)__msa_ilvev_b((v16i8)in3, (v16i8)in2); \ +} while (0) +#define ILVEV_B2_UB(...) ILVEV_B2(v16u8, __VA_ARGS__) +#define ILVEV_B2_SB(...) ILVEV_B2(v16i8, __VA_ARGS__) +#define ILVEV_B2_UH(...) ILVEV_B2(v8u16, __VA_ARGS__) +#define ILVEV_B2_SH(...) ILVEV_B2(v8i16, __VA_ARGS__) +#define ILVEV_B2_SD(...) ILVEV_B2(v2i64, __VA_ARGS__) + +/* Description : Interleave odd byte elements from vectors + * Arguments : Inputs - in0, in1, in2, in3 + * Outputs - out0, out1 + * Return Type - as per RTYPE + * Details : Odd byte elements of 'in0' and 'in1' are interleaved + * and written to 'out0' + */ +#define ILVOD_B2(RTYPE, in0, in1, in2, in3, out0, out1) do { \ + out0 = (RTYPE)__msa_ilvod_b((v16i8)in1, (v16i8)in0); \ + out1 = (RTYPE)__msa_ilvod_b((v16i8)in3, (v16i8)in2); \ +} while (0) +#define ILVOD_B2_UB(...) ILVOD_B2(v16u8, __VA_ARGS__) +#define ILVOD_B2_SB(...) ILVOD_B2(v16i8, __VA_ARGS__) +#define ILVOD_B2_UH(...) ILVOD_B2(v8u16, __VA_ARGS__) +#define ILVOD_B2_SH(...) ILVOD_B2(v8i16, __VA_ARGS__) +#define ILVOD_B2_SD(...) ILVOD_B2(v2i64, __VA_ARGS__) + +/* Description : Interleave even halfword elements from vectors + * Arguments : Inputs - in0, in1, in2, in3 + * Outputs - out0, out1 + * Return Type - as per RTYPE + * Details : Even halfword elements of 'in0' and 'in1' are interleaved + * and written to 'out0' + */ +#define ILVEV_H2(RTYPE, in0, in1, in2, in3, out0, out1) do { \ + out0 = (RTYPE)__msa_ilvev_h((v8i16)in1, (v8i16)in0); \ + out1 = (RTYPE)__msa_ilvev_h((v8i16)in3, (v8i16)in2); \ +} while (0) +#define ILVEV_H2_UB(...) ILVEV_H2(v16u8, __VA_ARGS__) +#define ILVEV_H2_UH(...) ILVEV_H2(v8u16, __VA_ARGS__) +#define ILVEV_H2_SH(...) ILVEV_H2(v8i16, __VA_ARGS__) +#define ILVEV_H2_SW(...) ILVEV_H2(v4i32, __VA_ARGS__) + +/* Description : Interleave odd halfword elements from vectors + * Arguments : Inputs - in0, in1, in2, in3 + * Outputs - out0, out1 + * Return Type - as per RTYPE + * Details : Odd halfword elements of 'in0' and 'in1' are interleaved + * and written to 'out0' + */ +#define ILVOD_H2(RTYPE, in0, in1, in2, in3, out0, out1) do { \ + out0 = (RTYPE)__msa_ilvod_h((v8i16)in1, (v8i16)in0); \ + out1 = (RTYPE)__msa_ilvod_h((v8i16)in3, (v8i16)in2); \ +} while (0) +#define ILVOD_H2_UB(...) ILVOD_H2(v16u8, __VA_ARGS__) +#define ILVOD_H2_UH(...) ILVOD_H2(v8u16, __VA_ARGS__) +#define ILVOD_H2_SH(...) ILVOD_H2(v8i16, __VA_ARGS__) +#define ILVOD_H2_SW(...) ILVOD_H2(v4i32, __VA_ARGS__) + +/* Description : Interleave even word elements from vectors + * Arguments : Inputs - in0, in1, in2, in3 + * Outputs - out0, out1 + * Return Type - as per RTYPE + * Details : Even word elements of 'in0' and 'in1' are interleaved + * and written to 'out0' + */ +#define ILVEV_W2(RTYPE, in0, in1, in2, in3, out0, out1) do { \ + out0 = (RTYPE)__msa_ilvev_w((v4i32)in1, (v4i32)in0); \ + out1 = (RTYPE)__msa_ilvev_w((v4i32)in3, (v4i32)in2); \ +} while (0) +#define ILVEV_W2_UB(...) ILVEV_W2(v16u8, __VA_ARGS__) +#define ILVEV_W2_SB(...) ILVEV_W2(v16i8, __VA_ARGS__) +#define ILVEV_W2_UH(...) ILVEV_W2(v8u16, __VA_ARGS__) +#define ILVEV_W2_SD(...) ILVEV_W2(v2i64, __VA_ARGS__) + +/* Description : Interleave even-odd word elements from vectors + * Arguments : Inputs - in0, in1, in2, in3 + * Outputs - out0, out1 + * Return Type - as per RTYPE + * Details : Even word elements of 'in0' and 'in1' are interleaved + * and written to 'out0' + * Odd word elements of 'in2' and 'in3' are interleaved + * and written to 'out1' + */ +#define ILVEVOD_W2(RTYPE, in0, in1, in2, in3, out0, out1) do { \ + out0 = (RTYPE)__msa_ilvev_w((v4i32)in1, (v4i32)in0); \ + out1 = (RTYPE)__msa_ilvod_w((v4i32)in3, (v4i32)in2); \ +} while (0) +#define ILVEVOD_W2_UB(...) ILVEVOD_W2(v16u8, __VA_ARGS__) +#define ILVEVOD_W2_UH(...) ILVEVOD_W2(v8u16, __VA_ARGS__) +#define ILVEVOD_W2_SH(...) ILVEVOD_W2(v8i16, __VA_ARGS__) +#define ILVEVOD_W2_SW(...) ILVEVOD_W2(v4i32, __VA_ARGS__) + +/* Description : Interleave even-odd half-word elements from vectors + * Arguments : Inputs - in0, in1, in2, in3 + * Outputs - out0, out1 + * Return Type - as per RTYPE + * Details : Even half-word elements of 'in0' and 'in1' are interleaved + * and written to 'out0' + * Odd half-word elements of 'in2' and 'in3' are interleaved + * and written to 'out1' + */ +#define ILVEVOD_H2(RTYPE, in0, in1, in2, in3, out0, out1) do { \ + out0 = (RTYPE)__msa_ilvev_h((v8i16)in1, (v8i16)in0); \ + out1 = (RTYPE)__msa_ilvod_h((v8i16)in3, (v8i16)in2); \ +} while (0) +#define ILVEVOD_H2_UB(...) ILVEVOD_H2(v16u8, __VA_ARGS__) +#define ILVEVOD_H2_UH(...) ILVEVOD_H2(v8u16, __VA_ARGS__) +#define ILVEVOD_H2_SH(...) ILVEVOD_H2(v8i16, __VA_ARGS__) +#define ILVEVOD_H2_SW(...) ILVEVOD_H2(v4i32, __VA_ARGS__) + +/* Description : Interleave even double word elements from vectors + * Arguments : Inputs - in0, in1, in2, in3 + * Outputs - out0, out1 + * Return Type - as per RTYPE + * Details : Even double word elements of 'in0' and 'in1' are interleaved + * and written to 'out0' + */ +#define ILVEV_D2(RTYPE, in0, in1, in2, in3, out0, out1) do { \ + out0 = (RTYPE)__msa_ilvev_d((v2i64)in1, (v2i64)in0); \ + out1 = (RTYPE)__msa_ilvev_d((v2i64)in3, (v2i64)in2); \ +} while (0) +#define ILVEV_D2_UB(...) ILVEV_D2(v16u8, __VA_ARGS__) +#define ILVEV_D2_SB(...) ILVEV_D2(v16i8, __VA_ARGS__) +#define ILVEV_D2_SW(...) ILVEV_D2(v4i32, __VA_ARGS__) +#define ILVEV_D2_SD(...) ILVEV_D2(v2i64, __VA_ARGS__) + +/* Description : Interleave left half of byte elements from vectors + * Arguments : Inputs - in0, in1, in2, in3 + * Outputs - out0, out1 + * Return Type - as per RTYPE + * Details : Left half of byte elements of 'in0' and 'in1' are interleaved + * and written to 'out0'. + */ +#define ILVL_B2(RTYPE, in0, in1, in2, in3, out0, out1) do { \ + out0 = (RTYPE)__msa_ilvl_b((v16i8)in0, (v16i8)in1); \ + out1 = (RTYPE)__msa_ilvl_b((v16i8)in2, (v16i8)in3); \ +} while (0) +#define ILVL_B2_UB(...) ILVL_B2(v16u8, __VA_ARGS__) +#define ILVL_B2_SB(...) ILVL_B2(v16i8, __VA_ARGS__) +#define ILVL_B2_UH(...) ILVL_B2(v8u16, __VA_ARGS__) +#define ILVL_B2_SH(...) ILVL_B2(v8i16, __VA_ARGS__) +#define ILVL_B2_SW(...) ILVL_B2(v4i32, __VA_ARGS__) + /* Description : Interleave right half of byte elements from vectors * Arguments : Inputs - in0, in1, in2, in3 * Outputs - out0, out1 @@ -306,10 +858,10 @@ * Details : Right half of byte elements of 'in0' and 'in1' are interleaved * and written to out0. */ -#define ILVR_B2(RTYPE, in0, in1, in2, in3, out0, out1) { \ - out0 = (RTYPE)__msa_ilvr_b((v16i8)in0, (v16i8)in1); \ - out1 = (RTYPE)__msa_ilvr_b((v16i8)in2, (v16i8)in3); \ -} +#define ILVR_B2(RTYPE, in0, in1, in2, in3, out0, out1) do { \ + out0 = (RTYPE)__msa_ilvr_b((v16i8)in0, (v16i8)in1); \ + out1 = (RTYPE)__msa_ilvr_b((v16i8)in2, (v16i8)in3); \ +} while (0) #define ILVR_B2_UB(...) ILVR_B2(v16u8, __VA_ARGS__) #define ILVR_B2_SB(...) ILVR_B2(v16i8, __VA_ARGS__) #define ILVR_B2_UH(...) ILVR_B2(v8u16, __VA_ARGS__) @@ -317,10 +869,10 @@ #define ILVR_B2_SW(...) ILVR_B2(v4i32, __VA_ARGS__) #define ILVR_B4(RTYPE, in0, in1, in2, in3, in4, in5, in6, in7, \ - out0, out1, out2, out3) { \ + out0, out1, out2, out3) do { \ ILVR_B2(RTYPE, in0, in1, in2, in3, out0, out1); \ ILVR_B2(RTYPE, in4, in5, in6, in7, out2, out3); \ -} +} while (0) #define ILVR_B4_UB(...) ILVR_B4(v16u8, __VA_ARGS__) #define ILVR_B4_SB(...) ILVR_B4(v16i8, __VA_ARGS__) #define ILVR_B4_UH(...) ILVR_B4(v8u16, __VA_ARGS__) @@ -334,19 +886,19 @@ * Details : Right half of halfword elements of 'in0' and 'in1' are * interleaved and written to 'out0'. */ -#define ILVR_H2(RTYPE, in0, in1, in2, in3, out0, out1) { \ - out0 = (RTYPE)__msa_ilvr_h((v8i16)in0, (v8i16)in1); \ - out1 = (RTYPE)__msa_ilvr_h((v8i16)in2, (v8i16)in3); \ -} +#define ILVR_H2(RTYPE, in0, in1, in2, in3, out0, out1) do { \ + out0 = (RTYPE)__msa_ilvr_h((v8i16)in0, (v8i16)in1); \ + out1 = (RTYPE)__msa_ilvr_h((v8i16)in2, (v8i16)in3); \ +} while (0) #define ILVR_H2_UB(...) ILVR_H2(v16u8, __VA_ARGS__) #define ILVR_H2_SH(...) ILVR_H2(v8i16, __VA_ARGS__) #define ILVR_H2_SW(...) ILVR_H2(v4i32, __VA_ARGS__) #define ILVR_H4(RTYPE, in0, in1, in2, in3, in4, in5, in6, in7, \ - out0, out1, out2, out3) { \ + out0, out1, out2, out3) do { \ ILVR_H2(RTYPE, in0, in1, in2, in3, out0, out1); \ ILVR_H2(RTYPE, in4, in5, in6, in7, out2, out3); \ -} +} while (0) #define ILVR_H4_UB(...) ILVR_H4(v16u8, __VA_ARGS__) #define ILVR_H4_SH(...) ILVR_H4(v8i16, __VA_ARGS__) #define ILVR_H4_SW(...) ILVR_H4(v4i32, __VA_ARGS__) @@ -358,31 +910,57 @@ * Details : Right half of double word elements of 'in0' and 'in1' are * interleaved and written to 'out0'. */ -#define ILVR_D2(RTYPE, in0, in1, in2, in3, out0, out1) { \ - out0 = (RTYPE)__msa_ilvr_d((v2i64)in0, (v2i64)in1); \ - out1 = (RTYPE)__msa_ilvr_d((v2i64)in2, (v2i64)in3); \ -} +#define ILVR_D2(RTYPE, in0, in1, in2, in3, out0, out1) do { \ + out0 = (RTYPE)__msa_ilvr_d((v2i64)in0, (v2i64)in1); \ + out1 = (RTYPE)__msa_ilvr_d((v2i64)in2, (v2i64)in3); \ +} while (0) #define ILVR_D2_UB(...) ILVR_D2(v16u8, __VA_ARGS__) #define ILVR_D2_SB(...) ILVR_D2(v16i8, __VA_ARGS__) #define ILVR_D2_SH(...) ILVR_D2(v8i16, __VA_ARGS__) -#define ILVRL_H2(RTYPE, in0, in1, out0, out1) { \ +#define ILVR_D4(RTYPE, in0, in1, in2, in3, in4, in5, in6, in7, \ + out0, out1, out2, out3) do { \ + ILVR_D2(RTYPE, in0, in1, in2, in3, out0, out1); \ + ILVR_D2(RTYPE, in4, in5, in6, in7, out2, out3); \ +} while (0) +#define ILVR_D4_SB(...) ILVR_D4(v16i8, __VA_ARGS__) +#define ILVR_D4_UB(...) ILVR_D4(v16u8, __VA_ARGS__) + +/* Description : Interleave both left and right half of input vectors + * Arguments : Inputs - in0, in1 + * Outputs - out0, out1 + * Return Type - as per RTYPE + * Details : Right half of byte elements from 'in0' and 'in1' are + * interleaved and written to 'out0' + */ +#define ILVRL_B2(RTYPE, in0, in1, out0, out1) do { \ + out0 = (RTYPE)__msa_ilvr_b((v16i8)in0, (v16i8)in1); \ + out1 = (RTYPE)__msa_ilvl_b((v16i8)in0, (v16i8)in1); \ +} while (0) +#define ILVRL_B2_UB(...) ILVRL_B2(v16u8, __VA_ARGS__) +#define ILVRL_B2_SB(...) ILVRL_B2(v16i8, __VA_ARGS__) +#define ILVRL_B2_UH(...) ILVRL_B2(v8u16, __VA_ARGS__) +#define ILVRL_B2_SH(...) ILVRL_B2(v8i16, __VA_ARGS__) +#define ILVRL_B2_SW(...) ILVRL_B2(v4i32, __VA_ARGS__) + +#define ILVRL_H2(RTYPE, in0, in1, out0, out1) do { \ out0 = (RTYPE)__msa_ilvr_h((v8i16)in0, (v8i16)in1); \ out1 = (RTYPE)__msa_ilvl_h((v8i16)in0, (v8i16)in1); \ -} +} while (0) #define ILVRL_H2_UB(...) ILVRL_H2(v16u8, __VA_ARGS__) #define ILVRL_H2_SB(...) ILVRL_H2(v16i8, __VA_ARGS__) #define ILVRL_H2_SH(...) ILVRL_H2(v8i16, __VA_ARGS__) #define ILVRL_H2_SW(...) ILVRL_H2(v4i32, __VA_ARGS__) #define ILVRL_H2_UW(...) ILVRL_H2(v4u32, __VA_ARGS__) -#define ILVRL_W2(RTYPE, in0, in1, out0, out1) { \ +#define ILVRL_W2(RTYPE, in0, in1, out0, out1) do { \ out0 = (RTYPE)__msa_ilvr_w((v4i32)in0, (v4i32)in1); \ out1 = (RTYPE)__msa_ilvl_w((v4i32)in0, (v4i32)in1); \ -} +} while (0) #define ILVRL_W2_UB(...) ILVRL_W2(v16u8, __VA_ARGS__) #define ILVRL_W2_SH(...) ILVRL_W2(v8i16, __VA_ARGS__) #define ILVRL_W2_SW(...) ILVRL_W2(v4i32, __VA_ARGS__) +#define ILVRL_W2_UW(...) ILVRL_W2(v4u32, __VA_ARGS__) /* Description : Pack even byte elements of vector pairs * Arguments : Inputs - in0, in1, in2, in3 @@ -392,15 +970,76 @@ * 'out0' & even byte elements of 'in1' are copied to the right * half of 'out0'. */ -#define PCKEV_B2(RTYPE, in0, in1, in2, in3, out0, out1) { \ - out0 = (RTYPE)__msa_pckev_b((v16i8)in0, (v16i8)in1); \ - out1 = (RTYPE)__msa_pckev_b((v16i8)in2, (v16i8)in3); \ -} +#define PCKEV_B2(RTYPE, in0, in1, in2, in3, out0, out1) do { \ + out0 = (RTYPE)__msa_pckev_b((v16i8)in0, (v16i8)in1); \ + out1 = (RTYPE)__msa_pckev_b((v16i8)in2, (v16i8)in3); \ +} while (0) #define PCKEV_B2_SB(...) PCKEV_B2(v16i8, __VA_ARGS__) #define PCKEV_B2_UB(...) PCKEV_B2(v16u8, __VA_ARGS__) #define PCKEV_B2_SH(...) PCKEV_B2(v8i16, __VA_ARGS__) #define PCKEV_B2_SW(...) PCKEV_B2(v4i32, __VA_ARGS__) +#define PCKEV_B4(RTYPE, in0, in1, in2, in3, in4, in5, in6, in7, \ + out0, out1, out2, out3) do { \ + PCKEV_B2(RTYPE, in0, in1, in2, in3, out0, out1); \ + PCKEV_B2(RTYPE, in4, in5, in6, in7, out2, out3); \ +} while (0) +#define PCKEV_B4_SB(...) PCKEV_B4(v16i8, __VA_ARGS__) +#define PCKEV_B4_UB(...) PCKEV_B4(v16u8, __VA_ARGS__) +#define PCKEV_B4_SH(...) PCKEV_B4(v8i16, __VA_ARGS__) +#define PCKEV_B4_SW(...) PCKEV_B4(v4i32, __VA_ARGS__) + +/* Description : Pack even halfword elements of vector pairs + * Arguments : Inputs - in0, in1, in2, in3 + * Outputs - out0, out1 + * Return Type - as per RTYPE + * Details : Even halfword elements of 'in0' are copied to the left half of + * 'out0' & even halfword elements of 'in1' are copied to the + * right half of 'out0'. + */ +#define PCKEV_H2(RTYPE, in0, in1, in2, in3, out0, out1) do { \ + out0 = (RTYPE)__msa_pckev_h((v8i16)in0, (v8i16)in1); \ + out1 = (RTYPE)__msa_pckev_h((v8i16)in2, (v8i16)in3); \ +} while (0) +#define PCKEV_H2_UH(...) PCKEV_H2(v8u16, __VA_ARGS__) +#define PCKEV_H2_SH(...) PCKEV_H2(v8i16, __VA_ARGS__) +#define PCKEV_H2_SW(...) PCKEV_H2(v4i32, __VA_ARGS__) +#define PCKEV_H2_UW(...) PCKEV_H2(v4u32, __VA_ARGS__) + +/* Description : Pack even word elements of vector pairs + * Arguments : Inputs - in0, in1, in2, in3 + * Outputs - out0, out1 + * Return Type - as per RTYPE + * Details : Even word elements of 'in0' are copied to the left half of + * 'out0' & even word elements of 'in1' are copied to the + * right half of 'out0'. + */ +#define PCKEV_W2(RTYPE, in0, in1, in2, in3, out0, out1) do { \ + out0 = (RTYPE)__msa_pckev_w((v4i32)in0, (v4i32)in1); \ + out1 = (RTYPE)__msa_pckev_w((v4i32)in2, (v4i32)in3); \ +} while (0) +#define PCKEV_W2_UH(...) PCKEV_W2(v8u16, __VA_ARGS__) +#define PCKEV_W2_SH(...) PCKEV_W2(v8i16, __VA_ARGS__) +#define PCKEV_W2_SW(...) PCKEV_W2(v4i32, __VA_ARGS__) +#define PCKEV_W2_UW(...) PCKEV_W2(v4u32, __VA_ARGS__) + +/* Description : Pack odd halfword elements of vector pairs + * Arguments : Inputs - in0, in1, in2, in3 + * Outputs - out0, out1 + * Return Type - as per RTYPE + * Details : Odd halfword elements of 'in0' are copied to the left half of + * 'out0' & odd halfword elements of 'in1' are copied to the + * right half of 'out0'. + */ +#define PCKOD_H2(RTYPE, in0, in1, in2, in3, out0, out1) do { \ + out0 = (RTYPE)__msa_pckod_h((v8i16)in0, (v8i16)in1); \ + out1 = (RTYPE)__msa_pckod_h((v8i16)in2, (v8i16)in3); \ +} while (0) +#define PCKOD_H2_UH(...) PCKOD_H2(v8u16, __VA_ARGS__) +#define PCKOD_H2_SH(...) PCKOD_H2(v8i16, __VA_ARGS__) +#define PCKOD_H2_SW(...) PCKOD_H2(v4i32, __VA_ARGS__) +#define PCKOD_H2_UW(...) PCKOD_H2(v4u32, __VA_ARGS__) + /* Description : Arithmetic immediate shift right all elements of word vector * Arguments : Inputs - in0, in1, shift * Outputs - in place operation @@ -408,17 +1047,17 @@ * Details : Each element of vector 'in0' is right shifted by 'shift' and * the result is written in-place. 'shift' is a GP variable. */ -#define SRAI_W2(RTYPE, in0, in1, shift_val) { \ - in0 = (RTYPE)SRAI_W(in0, shift_val); \ - in1 = (RTYPE)SRAI_W(in1, shift_val); \ -} +#define SRAI_W2(RTYPE, in0, in1, shift_val) do { \ + in0 = (RTYPE)SRAI_W(in0, shift_val); \ + in1 = (RTYPE)SRAI_W(in1, shift_val); \ +} while (0) #define SRAI_W2_SW(...) SRAI_W2(v4i32, __VA_ARGS__) #define SRAI_W2_UW(...) SRAI_W2(v4u32, __VA_ARGS__) -#define SRAI_W4(RTYPE, in0, in1, in2, in3, shift_val) { \ - SRAI_W2(RTYPE, in0, in1, shift_val); \ - SRAI_W2(RTYPE, in2, in3, shift_val); \ -} +#define SRAI_W4(RTYPE, in0, in1, in2, in3, shift_val) do { \ + SRAI_W2(RTYPE, in0, in1, shift_val); \ + SRAI_W2(RTYPE, in2, in3, shift_val); \ +} while (0) #define SRAI_W4_SW(...) SRAI_W4(v4i32, __VA_ARGS__) #define SRAI_W4_UW(...) SRAI_W4(v4u32, __VA_ARGS__) @@ -429,10 +1068,10 @@ * Details : Each element of vector 'in0' is right shifted by 'shift' and * the result is written in-place. 'shift' is a GP variable. */ -#define SRAI_H2(RTYPE, in0, in1, shift_val) { \ - in0 = (RTYPE)SRAI_H(in0, shift_val); \ - in1 = (RTYPE)SRAI_H(in1, shift_val); \ -} +#define SRAI_H2(RTYPE, in0, in1, shift_val) do { \ + in0 = (RTYPE)SRAI_H(in0, shift_val); \ + in1 = (RTYPE)SRAI_H(in1, shift_val); \ +} while (0) #define SRAI_H2_SH(...) SRAI_H2(v8i16, __VA_ARGS__) #define SRAI_H2_UH(...) SRAI_H2(v8u16, __VA_ARGS__) @@ -443,48 +1082,166 @@ * Details : Each element of vector 'in0' is right shifted by 'shift' and * the result is written in-place. 'shift' is a GP variable. */ -#define SRARI_W2(RTYPE, in0, in1, shift) { \ +#define SRARI_W2(RTYPE, in0, in1, shift) do { \ in0 = (RTYPE)__msa_srari_w((v4i32)in0, shift); \ in1 = (RTYPE)__msa_srari_w((v4i32)in1, shift); \ -} +} while (0) #define SRARI_W2_SW(...) SRARI_W2(v4i32, __VA_ARGS__) -#define SRARI_W4(RTYPE, in0, in1, in2, in3, shift) { \ - SRARI_W2(RTYPE, in0, in1, shift); \ - SRARI_W2(RTYPE, in2, in3, shift); \ -} +#define SRARI_W4(RTYPE, in0, in1, in2, in3, shift) do { \ + SRARI_W2(RTYPE, in0, in1, shift); \ + SRARI_W2(RTYPE, in2, in3, shift); \ +} while (0) #define SRARI_W4_SH(...) SRARI_W4(v8i16, __VA_ARGS__) #define SRARI_W4_UW(...) SRARI_W4(v4u32, __VA_ARGS__) #define SRARI_W4_SW(...) SRARI_W4(v4i32, __VA_ARGS__) +/* Description : Shift right arithmetic rounded double words + * Arguments : Inputs - in0, in1, shift + * Outputs - in place operation + * Return Type - as per RTYPE + * Details : Each element of vector 'in0' is shifted right arithmetically by + * the number of bits in the corresponding element in the vector + * 'shift'. The last discarded bit is added to shifted value for + * rounding and the result is written in-place. + * 'shift' is a vector. + */ +#define SRAR_D2(RTYPE, in0, in1, shift) do { \ + in0 = (RTYPE)__msa_srar_d((v2i64)in0, (v2i64)shift); \ + in1 = (RTYPE)__msa_srar_d((v2i64)in1, (v2i64)shift); \ +} while (0) +#define SRAR_D2_SW(...) SRAR_D2(v4i32, __VA_ARGS__) +#define SRAR_D2_SD(...) SRAR_D2(v2i64, __VA_ARGS__) +#define SRAR_D2_UD(...) SRAR_D2(v2u64, __VA_ARGS__) + +#define SRAR_D4(RTYPE, in0, in1, in2, in3, shift) do { \ + SRAR_D2(RTYPE, in0, in1, shift); \ + SRAR_D2(RTYPE, in2, in3, shift); \ +} while (0) +#define SRAR_D4_SD(...) SRAR_D4(v2i64, __VA_ARGS__) +#define SRAR_D4_UD(...) SRAR_D4(v2u64, __VA_ARGS__) + /* Description : Addition of 2 pairs of half-word vectors * Arguments : Inputs - in0, in1, in2, in3 * Outputs - out0, out1 * Details : Each element in 'in0' is added to 'in1' and result is written * to 'out0'. */ -#define ADDVI_H2(RTYPE, in0, in1, in2, in3, out0, out1) { \ - out0 = (RTYPE)ADDVI_H(in0, in1); \ - out1 = (RTYPE)ADDVI_H(in2, in3); \ -} +#define ADDVI_H2(RTYPE, in0, in1, in2, in3, out0, out1) do { \ + out0 = (RTYPE)ADDVI_H(in0, in1); \ + out1 = (RTYPE)ADDVI_H(in2, in3); \ +} while (0) #define ADDVI_H2_SH(...) ADDVI_H2(v8i16, __VA_ARGS__) #define ADDVI_H2_UH(...) ADDVI_H2(v8u16, __VA_ARGS__) +/* Description : Addition of 2 pairs of word vectors + * Arguments : Inputs - in0, in1, in2, in3 + * Outputs - out0, out1 + * Details : Each element in 'in0' is added to 'in1' and result is written + * to 'out0'. + */ +#define ADDVI_W2(RTYPE, in0, in1, in2, in3, out0, out1) do { \ + out0 = (RTYPE)ADDVI_W(in0, in1); \ + out1 = (RTYPE)ADDVI_W(in2, in3); \ +} while (0) +#define ADDVI_W2_SW(...) ADDVI_W2(v4i32, __VA_ARGS__) + +/* Description : Fill 2 pairs of word vectors with GP registers + * Arguments : Inputs - in0, in1 + * Outputs - out0, out1 + * Details : GP register in0 is replicated in each word element of out0 + * GP register in1 is replicated in each word element of out1 + */ +#define FILL_W2(RTYPE, in0, in1, out0, out1) do { \ + out0 = (RTYPE)__msa_fill_w(in0); \ + out1 = (RTYPE)__msa_fill_w(in1); \ +} while (0) +#define FILL_W2_SW(...) FILL_W2(v4i32, __VA_ARGS__) + /* Description : Addition of 2 pairs of vectors * Arguments : Inputs - in0, in1, in2, in3 * Outputs - out0, out1 * Details : Each element in 'in0' is added to 'in1' and result is written * to 'out0'. */ -#define ADD2(in0, in1, in2, in3, out0, out1) { \ - out0 = in0 + in1; \ - out1 = in2 + in3; \ -} +#define ADD2(in0, in1, in2, in3, out0, out1) do { \ + out0 = in0 + in1; \ + out1 = in2 + in3; \ +} while (0) + #define ADD4(in0, in1, in2, in3, in4, in5, in6, in7, \ - out0, out1, out2, out3) { \ + out0, out1, out2, out3) do { \ ADD2(in0, in1, in2, in3, out0, out1); \ ADD2(in4, in5, in6, in7, out2, out3); \ -} +} while (0) + +/* Description : Subtraction of 2 pairs of vectors + * Arguments : Inputs - in0, in1, in2, in3 + * Outputs - out0, out1 + * Details : Each element in 'in1' is subtracted from 'in0' and result is + * written to 'out0'. + */ +#define SUB2(in0, in1, in2, in3, out0, out1) do { \ + out0 = in0 - in1; \ + out1 = in2 - in3; \ +} while (0) + +#define SUB3(in0, in1, in2, in3, in4, in5, out0, out1, out2) do { \ + out0 = in0 - in1; \ + out1 = in2 - in3; \ + out2 = in4 - in5; \ +} while (0) + +#define SUB4(in0, in1, in2, in3, in4, in5, in6, in7, \ + out0, out1, out2, out3) do { \ + out0 = in0 - in1; \ + out1 = in2 - in3; \ + out2 = in4 - in5; \ + out3 = in6 - in7; \ +} while (0) + +/* Description : Addition - Subtraction of input vectors + * Arguments : Inputs - in0, in1 + * Outputs - out0, out1 + * Details : Each element in 'in1' is added to 'in0' and result is + * written to 'out0'. + * Each element in 'in1' is subtracted from 'in0' and result is + * written to 'out1'. + */ +#define ADDSUB2(in0, in1, out0, out1) do { \ + out0 = in0 + in1; \ + out1 = in0 - in1; \ +} while (0) + +/* Description : Multiplication of pairs of vectors + * Arguments : Inputs - in0, in1, in2, in3 + * Outputs - out0, out1 + * Details : Each element from 'in0' is multiplied with elements from 'in1' + * and the result is written to 'out0' + */ +#define MUL2(in0, in1, in2, in3, out0, out1) do { \ + out0 = in0 * in1; \ + out1 = in2 * in3; \ +} while (0) + +#define MUL4(in0, in1, in2, in3, in4, in5, in6, in7, \ + out0, out1, out2, out3) do { \ + MUL2(in0, in1, in2, in3, out0, out1); \ + MUL2(in4, in5, in6, in7, out2, out3); \ +} while (0) + +/* Description : Sign extend halfword elements from right half of the vector + * Arguments : Input - in (halfword vector) + * Output - out (sign extended word vector) + * Return Type - signed word + * Details : Sign bit of halfword elements from input vector 'in' is + * extracted and interleaved with same vector 'in0' to generate + * 4 word elements keeping sign intact + */ +#define UNPCK_R_SH_SW(in, out) do { \ + const v8i16 sign_m = __msa_clti_s_h((v8i16)in, 0); \ + out = (v4i32)__msa_ilvr_h(sign_m, (v8i16)in); \ +} while (0) /* Description : Sign extend halfword elements from input vector and return * the result in pair of vectors @@ -497,29 +1254,82 @@ * Then interleaved left with same vector 'in0' to * generate 4 signed word elements in 'out1' */ -#define UNPCK_SH_SW(in, out0, out1) { \ +#define UNPCK_SH_SW(in, out0, out1) do { \ const v8i16 tmp_m = __msa_clti_s_h((v8i16)in, 0); \ ILVRL_H2_SW(tmp_m, in, out0, out1); \ -} +} while (0) /* Description : Butterfly of 4 input vectors * Arguments : Inputs - in0, in1, in2, in3 * Outputs - out0, out1, out2, out3 * Details : Butterfly operation */ -#define BUTTERFLY_4(in0, in1, in2, in3, out0, out1, out2, out3) { \ - out0 = in0 + in3; \ - out1 = in1 + in2; \ - out2 = in1 - in2; \ - out3 = in0 - in3; \ -} +#define BUTTERFLY_4(in0, in1, in2, in3, out0, out1, out2, out3) do { \ + out0 = in0 + in3; \ + out1 = in1 + in2; \ + out2 = in1 - in2; \ + out3 = in0 - in3; \ +} while (0) + +/* Description : Transpose 16x4 block into 4x16 with byte elements in vectors + * Arguments : Inputs - in0, in1, in2, in3, in4, in5, in6, in7, + * in8, in9, in10, in11, in12, in13, in14, in15 + * Outputs - out0, out1, out2, out3 + * Return Type - unsigned byte + */ +#define TRANSPOSE16x4_UB_UB(in0, in1, in2, in3, in4, in5, in6, in7, \ + in8, in9, in10, in11, in12, in13, in14, in15, \ + out0, out1, out2, out3) do { \ + v2i64 tmp0_m, tmp1_m, tmp2_m, tmp3_m, tmp4_m, tmp5_m; \ + ILVEV_W2_SD(in0, in4, in8, in12, tmp2_m, tmp3_m); \ + ILVEV_W2_SD(in1, in5, in9, in13, tmp0_m, tmp1_m); \ + ILVEV_D2_UB(tmp2_m, tmp3_m, tmp0_m, tmp1_m, out1, out3); \ + ILVEV_W2_SD(in2, in6, in10, in14, tmp4_m, tmp5_m); \ + ILVEV_W2_SD(in3, in7, in11, in15, tmp0_m, tmp1_m); \ + ILVEV_D2_SD(tmp4_m, tmp5_m, tmp0_m, tmp1_m, tmp2_m, tmp3_m); \ + ILVEV_B2_SD(out1, out3, tmp2_m, tmp3_m, tmp0_m, tmp1_m); \ + ILVEVOD_H2_UB(tmp0_m, tmp1_m, tmp0_m, tmp1_m, out0, out2); \ + ILVOD_B2_SD(out1, out3, tmp2_m, tmp3_m, tmp0_m, tmp1_m); \ + ILVEVOD_H2_UB(tmp0_m, tmp1_m, tmp0_m, tmp1_m, out1, out3); \ +} while (0) + +/* Description : Transpose 16x8 block into 8x16 with byte elements in vectors + * Arguments : Inputs - in0, in1, in2, in3, in4, in5, in6, in7, + * in8, in9, in10, in11, in12, in13, in14, in15 + * Outputs - out0, out1, out2, out3, out4, out5, out6, out7 + * Return Type - unsigned byte + */ +#define TRANSPOSE16x8_UB_UB(in0, in1, in2, in3, in4, in5, in6, in7, \ + in8, in9, in10, in11, in12, in13, in14, in15, \ + out0, out1, out2, out3, out4, out5, \ + out6, out7) do { \ + v8i16 tmp0_m, tmp1_m, tmp4_m, tmp5_m, tmp6_m, tmp7_m; \ + v4i32 tmp2_m, tmp3_m; \ + ILVEV_D2_UB(in0, in8, in1, in9, out7, out6); \ + ILVEV_D2_UB(in2, in10, in3, in11, out5, out4); \ + ILVEV_D2_UB(in4, in12, in5, in13, out3, out2); \ + ILVEV_D2_UB(in6, in14, in7, in15, out1, out0); \ + ILVEV_B2_SH(out7, out6, out5, out4, tmp0_m, tmp1_m); \ + ILVOD_B2_SH(out7, out6, out5, out4, tmp4_m, tmp5_m); \ + ILVEV_B2_UB(out3, out2, out1, out0, out5, out7); \ + ILVOD_B2_SH(out3, out2, out1, out0, tmp6_m, tmp7_m); \ + ILVEV_H2_SW(tmp0_m, tmp1_m, out5, out7, tmp2_m, tmp3_m); \ + ILVEVOD_W2_UB(tmp2_m, tmp3_m, tmp2_m, tmp3_m, out0, out4); \ + ILVOD_H2_SW(tmp0_m, tmp1_m, out5, out7, tmp2_m, tmp3_m); \ + ILVEVOD_W2_UB(tmp2_m, tmp3_m, tmp2_m, tmp3_m, out2, out6); \ + ILVEV_H2_SW(tmp4_m, tmp5_m, tmp6_m, tmp7_m, tmp2_m, tmp3_m); \ + ILVEVOD_W2_UB(tmp2_m, tmp3_m, tmp2_m, tmp3_m, out1, out5); \ + ILVOD_H2_SW(tmp4_m, tmp5_m, tmp6_m, tmp7_m, tmp2_m, tmp3_m); \ + ILVEVOD_W2_UB(tmp2_m, tmp3_m, tmp2_m, tmp3_m, out3, out7); \ +} while (0) /* Description : Transpose 4x4 block with word elements in vectors * Arguments : Inputs - in0, in1, in2, in3 * Outputs - out0, out1, out2, out3 * Return Type - as per RTYPE */ -#define TRANSPOSE4x4_W(RTYPE, in0, in1, in2, in3, out0, out1, out2, out3) { \ +#define TRANSPOSE4x4_W(RTYPE, in0, in1, in2, in3, \ + out0, out1, out2, out3) do { \ v4i32 s0_m, s1_m, s2_m, s3_m; \ ILVRL_W2_SW(in1, in0, s0_m, s1_m); \ ILVRL_W2_SW(in3, in2, s2_m, s3_m); \ @@ -527,7 +1337,7 @@ out1 = (RTYPE)__msa_ilvl_d((v2i64)s2_m, (v2i64)s0_m); \ out2 = (RTYPE)__msa_ilvr_d((v2i64)s3_m, (v2i64)s1_m); \ out3 = (RTYPE)__msa_ilvl_d((v2i64)s3_m, (v2i64)s1_m); \ -} +} while (0) #define TRANSPOSE4x4_SW_SW(...) TRANSPOSE4x4_W(v4i32, __VA_ARGS__) /* Description : Add block 4x4 @@ -535,7 +1345,7 @@ * Details : Least significant 4 bytes from each input vector are added to * the destination bytes, clipped between 0-255 and stored. */ -#define ADDBLK_ST4x4_UB(in0, in1, in2, in3, pdst, stride) { \ +#define ADDBLK_ST4x4_UB(in0, in1, in2, in3, pdst, stride) do { \ uint32_t src0_m, src1_m, src2_m, src3_m; \ v8i16 inp0_m, inp1_m, res0_m, res1_m; \ v16i8 dst0_m = { 0 }; \ @@ -550,6 +1360,31 @@ CLIP_SH2_0_255(res0_m, res1_m); \ PCKEV_B2_SB(res0_m, res0_m, res1_m, res1_m, dst0_m, dst1_m); \ ST4x4_UB(dst0_m, dst1_m, 0, 1, 0, 1, pdst, stride); \ -} +} while (0) + +/* Description : Pack even byte elements, extract 0 & 2 index words from pair + * of results and store 4 words in destination memory as per + * stride + * Arguments : Inputs - in0, in1, in2, in3, pdst, stride + */ +#define PCKEV_ST4x4_UB(in0, in1, in2, in3, pdst, stride) do { \ + v16i8 tmp0_m, tmp1_m; \ + PCKEV_B2_SB(in1, in0, in3, in2, tmp0_m, tmp1_m); \ + ST4x4_UB(tmp0_m, tmp1_m, 0, 2, 0, 2, pdst, stride); \ +} while (0) + +/* Description : average with rounding (in0 + in1 + 1) / 2. + * Arguments : Inputs - in0, in1, in2, in3, + * Outputs - out0, out1 + * Return Type - as per RTYPE + * Details : Each unsigned byte element from 'in0' vector is added with + * each unsigned byte element from 'in1' vector. Then the average + * with rounding is calculated and written to 'out0' + */ +#define AVER_UB2(RTYPE, in0, in1, in2, in3, out0, out1) do { \ + out0 = (RTYPE)__msa_aver_u_b((v16u8)in0, (v16u8)in1); \ + out1 = (RTYPE)__msa_aver_u_b((v16u8)in2, (v16u8)in3); \ +} while (0) +#define AVER_UB2_UB(...) AVER_UB2(v16u8, __VA_ARGS__) #endif /* WEBP_DSP_MSA_MACRO_H_ */ diff --git a/thirdparty/libwebp/dsp/neon.h b/thirdparty/libwebp/dsp/neon.h index 0a06266848..3b548a6855 100644 --- a/thirdparty/libwebp/dsp/neon.h +++ b/thirdparty/libwebp/dsp/neon.h @@ -79,4 +79,22 @@ static WEBP_INLINE int32x4x4_t Transpose4x4(const int32x4x4_t rows) { } } +#if 0 // Useful debug macro. +#include +#define PRINT_REG(REG, SIZE) do { \ + int i; \ + printf("%s \t[%d]: 0x", #REG, SIZE); \ + if (SIZE == 8) { \ + uint8_t _tmp[8]; \ + vst1_u8(_tmp, (REG)); \ + for (i = 0; i < 8; ++i) printf("%.2x ", _tmp[i]); \ + } else if (SIZE == 16) { \ + uint16_t _tmp[4]; \ + vst1_u16(_tmp, (REG)); \ + for (i = 0; i < 4; ++i) printf("%.4x ", _tmp[i]); \ + } \ + printf("\n"); \ +} while (0) +#endif + #endif // WEBP_DSP_NEON_H_ diff --git a/thirdparty/libwebp/dsp/rescaler.c b/thirdparty/libwebp/dsp/rescaler.c index f5b07756cf..0f54502352 100644 --- a/thirdparty/libwebp/dsp/rescaler.c +++ b/thirdparty/libwebp/dsp/rescaler.c @@ -14,7 +14,7 @@ #include #include "./dsp.h" -#include "../utils/rescaler.h" +#include "../utils/rescaler_utils.h" //------------------------------------------------------------------------------ // Implementations of critical functions ImportRow / ExportRow @@ -199,6 +199,7 @@ WebPRescalerExportRowFunc WebPRescalerExportRowShrink; extern void WebPRescalerDspInitSSE2(void); extern void WebPRescalerDspInitMIPS32(void); extern void WebPRescalerDspInitMIPSdspR2(void); +extern void WebPRescalerDspInitMSA(void); extern void WebPRescalerDspInitNEON(void); static volatile VP8CPUInfo rescaler_last_cpuinfo_used = @@ -232,6 +233,11 @@ WEBP_TSAN_IGNORE_FUNCTION void WebPRescalerDspInit(void) { if (VP8GetCPUInfo(kMIPSdspR2)) { WebPRescalerDspInitMIPSdspR2(); } +#endif +#if defined(WEBP_USE_MSA) + if (VP8GetCPUInfo(kMSA)) { + WebPRescalerDspInitMSA(); + } #endif } rescaler_last_cpuinfo_used = VP8GetCPUInfo; diff --git a/thirdparty/libwebp/dsp/rescaler_mips32.c b/thirdparty/libwebp/dsp/rescaler_mips32.c index ddaa391336..e09ad5d19f 100644 --- a/thirdparty/libwebp/dsp/rescaler_mips32.c +++ b/thirdparty/libwebp/dsp/rescaler_mips32.c @@ -16,7 +16,7 @@ #if defined(WEBP_USE_MIPS32) #include -#include "../utils/rescaler.h" +#include "../utils/rescaler_utils.h" //------------------------------------------------------------------------------ // Row import diff --git a/thirdparty/libwebp/dsp/rescaler_mips_dsp_r2.c b/thirdparty/libwebp/dsp/rescaler_mips_dsp_r2.c index b457d0a30a..2308d64544 100644 --- a/thirdparty/libwebp/dsp/rescaler_mips_dsp_r2.c +++ b/thirdparty/libwebp/dsp/rescaler_mips_dsp_r2.c @@ -16,7 +16,7 @@ #if defined(WEBP_USE_MIPS_DSP_R2) #include -#include "../utils/rescaler.h" +#include "../utils/rescaler_utils.h" #define ROUNDER (WEBP_RESCALER_ONE >> 1) #define MULT_FIX(x, y) (((uint64_t)(x) * (y) + ROUNDER) >> WEBP_RESCALER_RFIX) diff --git a/thirdparty/libwebp/dsp/rescaler_msa.c b/thirdparty/libwebp/dsp/rescaler_msa.c new file mode 100644 index 0000000000..2c10e55d8c --- /dev/null +++ b/thirdparty/libwebp/dsp/rescaler_msa.c @@ -0,0 +1,444 @@ +// Copyright 2016 Google Inc. All Rights Reserved. +// +// Use of this source code is governed by a BSD-style license +// that can be found in the COPYING file in the root of the source +// tree. An additional intellectual property rights grant can be found +// in the file PATENTS. All contributing project authors may +// be found in the AUTHORS file in the root of the source tree. +// ----------------------------------------------------------------------------- +// +// MSA version of rescaling functions +// +// Author: Prashant Patil (prashant.patil@imgtec.com) + +#include "./dsp.h" + +#if defined(WEBP_USE_MSA) + +#include + +#include "../utils/rescaler_utils.h" +#include "./msa_macro.h" + +#define ROUNDER (WEBP_RESCALER_ONE >> 1) +#define MULT_FIX(x, y) (((uint64_t)(x) * (y) + ROUNDER) >> WEBP_RESCALER_RFIX) + +#define CALC_MULT_FIX_16(in0, in1, in2, in3, scale, shift, dst) do { \ + v4u32 tmp0, tmp1, tmp2, tmp3; \ + v16u8 t0, t1, t2, t3, t4, t5; \ + v2u64 out0, out1, out2, out3; \ + ILVRL_W2_UW(zero, in0, tmp0, tmp1); \ + ILVRL_W2_UW(zero, in1, tmp2, tmp3); \ + DOTP_UW2_UD(tmp0, tmp1, scale, scale, out0, out1); \ + DOTP_UW2_UD(tmp2, tmp3, scale, scale, out2, out3); \ + SRAR_D4_UD(out0, out1, out2, out3, shift); \ + PCKEV_B2_UB(out1, out0, out3, out2, t0, t1); \ + ILVRL_W2_UW(zero, in2, tmp0, tmp1); \ + ILVRL_W2_UW(zero, in3, tmp2, tmp3); \ + DOTP_UW2_UD(tmp0, tmp1, scale, scale, out0, out1); \ + DOTP_UW2_UD(tmp2, tmp3, scale, scale, out2, out3); \ + SRAR_D4_UD(out0, out1, out2, out3, shift); \ + PCKEV_B2_UB(out1, out0, out3, out2, t2, t3); \ + PCKEV_B2_UB(t1, t0, t3, t2, t4, t5); \ + dst = (v16u8)__msa_pckev_b((v16i8)t5, (v16i8)t4); \ +} while (0) + +#define CALC_MULT_FIX_4(in0, scale, shift, dst) do { \ + v4u32 tmp0, tmp1; \ + v16i8 t0, t1; \ + v2u64 out0, out1; \ + ILVRL_W2_UW(zero, in0, tmp0, tmp1); \ + DOTP_UW2_UD(tmp0, tmp1, scale, scale, out0, out1); \ + SRAR_D2_UD(out0, out1, shift); \ + t0 = __msa_pckev_b((v16i8)out1, (v16i8)out0); \ + t1 = __msa_pckev_b(t0, t0); \ + t0 = __msa_pckev_b(t1, t1); \ + dst = __msa_copy_s_w((v4i32)t0, 0); \ +} while (0) + +#define CALC_MULT_FIX1_16(in0, in1, in2, in3, fyscale, shift, \ + dst0, dst1, dst2, dst3) do { \ + v4u32 tmp0, tmp1, tmp2, tmp3; \ + v2u64 out0, out1, out2, out3; \ + ILVRL_W2_UW(zero, in0, tmp0, tmp1); \ + ILVRL_W2_UW(zero, in1, tmp2, tmp3); \ + DOTP_UW2_UD(tmp0, tmp1, fyscale, fyscale, out0, out1); \ + DOTP_UW2_UD(tmp2, tmp3, fyscale, fyscale, out2, out3); \ + SRAR_D4_UD(out0, out1, out2, out3, shift); \ + PCKEV_W2_UW(out1, out0, out3, out2, dst0, dst1); \ + ILVRL_W2_UW(zero, in2, tmp0, tmp1); \ + ILVRL_W2_UW(zero, in3, tmp2, tmp3); \ + DOTP_UW2_UD(tmp0, tmp1, fyscale, fyscale, out0, out1); \ + DOTP_UW2_UD(tmp2, tmp3, fyscale, fyscale, out2, out3); \ + SRAR_D4_UD(out0, out1, out2, out3, shift); \ + PCKEV_W2_UW(out1, out0, out3, out2, dst2, dst3); \ +} while (0) + +#define CALC_MULT_FIX1_4(in0, scale, shift, dst) do { \ + v4u32 tmp0, tmp1; \ + v2u64 out0, out1; \ + ILVRL_W2_UW(zero, in0, tmp0, tmp1); \ + DOTP_UW2_UD(tmp0, tmp1, scale, scale, out0, out1); \ + SRAR_D2_UD(out0, out1, shift); \ + dst = (v4u32)__msa_pckev_w((v4i32)out1, (v4i32)out0); \ +} while (0) + +#define CALC_MULT_FIX2_16(in0, in1, in2, in3, mult, scale, shift, \ + dst0, dst1) do { \ + v4u32 tmp0, tmp1, tmp2, tmp3; \ + v2u64 out0, out1, out2, out3; \ + ILVRL_W2_UW(in0, in2, tmp0, tmp1); \ + ILVRL_W2_UW(in1, in3, tmp2, tmp3); \ + DOTP_UW2_UD(tmp0, tmp1, mult, mult, out0, out1); \ + DOTP_UW2_UD(tmp2, tmp3, mult, mult, out2, out3); \ + SRAR_D4_UD(out0, out1, out2, out3, shift); \ + DOTP_UW2_UD(out0, out1, scale, scale, out0, out1); \ + DOTP_UW2_UD(out2, out3, scale, scale, out2, out3); \ + SRAR_D4_UD(out0, out1, out2, out3, shift); \ + PCKEV_B2_UB(out1, out0, out3, out2, dst0, dst1); \ +} while (0) + +#define CALC_MULT_FIX2_4(in0, in1, mult, scale, shift, dst) do { \ + v4u32 tmp0, tmp1; \ + v2u64 out0, out1; \ + v16i8 t0, t1; \ + ILVRL_W2_UW(in0, in1, tmp0, tmp1); \ + DOTP_UW2_UD(tmp0, tmp1, mult, mult, out0, out1); \ + SRAR_D2_UD(out0, out1, shift); \ + DOTP_UW2_UD(out0, out1, scale, scale, out0, out1); \ + SRAR_D2_UD(out0, out1, shift); \ + t0 = __msa_pckev_b((v16i8)out1, (v16i8)out0); \ + t1 = __msa_pckev_b(t0, t0); \ + t0 = __msa_pckev_b(t1, t1); \ + dst = __msa_copy_s_w((v4i32)t0, 0); \ +} while (0) + +static WEBP_INLINE void ExportRowExpand_0(const uint32_t* frow, uint8_t* dst, + int length, + WebPRescaler* const wrk) { + const v4u32 scale = (v4u32)__msa_fill_w(wrk->fy_scale); + const v4u32 shift = (v4u32)__msa_fill_w(WEBP_RESCALER_RFIX); + const v4i32 zero = { 0 }; + + while (length >= 16) { + v4u32 src0, src1, src2, src3; + v16u8 out; + LD_UW4(frow, 4, src0, src1, src2, src3); + CALC_MULT_FIX_16(src0, src1, src2, src3, scale, shift, out); + ST_UB(out, dst); + length -= 16; + frow += 16; + dst += 16; + } + if (length > 0) { + int x_out; + if (length >= 12) { + uint32_t val0_m, val1_m, val2_m; + v4u32 src0, src1, src2; + LD_UW3(frow, 4, src0, src1, src2); + CALC_MULT_FIX_4(src0, scale, shift, val0_m); + CALC_MULT_FIX_4(src1, scale, shift, val1_m); + CALC_MULT_FIX_4(src2, scale, shift, val2_m); + SW3(val0_m, val1_m, val2_m, dst, 4); + length -= 12; + frow += 12; + dst += 12; + } else if (length >= 8) { + uint32_t val0_m, val1_m; + v4u32 src0, src1; + LD_UW2(frow, 4, src0, src1); + CALC_MULT_FIX_4(src0, scale, shift, val0_m); + CALC_MULT_FIX_4(src1, scale, shift, val1_m); + SW2(val0_m, val1_m, dst, 4); + length -= 8; + frow += 8; + dst += 8; + } else if (length >= 4) { + uint32_t val0_m; + const v4u32 src0 = LD_UW(frow); + CALC_MULT_FIX_4(src0, scale, shift, val0_m); + SW(val0_m, dst); + length -= 4; + frow += 4; + dst += 4; + } + for (x_out = 0; x_out < length; ++x_out) { + const uint32_t J = frow[x_out]; + const int v = (int)MULT_FIX(J, wrk->fy_scale); + assert(v >= 0 && v <= 255); + dst[x_out] = v; + } + } +} + +static WEBP_INLINE void ExportRowExpand_1(const uint32_t* frow, uint32_t* irow, + uint8_t* dst, int length, + WebPRescaler* const wrk) { + const uint32_t B = WEBP_RESCALER_FRAC(-wrk->y_accum, wrk->y_sub); + const uint32_t A = (uint32_t)(WEBP_RESCALER_ONE - B); + const v4i32 B1 = __msa_fill_w(B); + const v4i32 A1 = __msa_fill_w(A); + const v4i32 AB = __msa_ilvr_w(A1, B1); + const v4u32 scale = (v4u32)__msa_fill_w(wrk->fy_scale); + const v4u32 shift = (v4u32)__msa_fill_w(WEBP_RESCALER_RFIX); + + while (length >= 16) { + v4u32 frow0, frow1, frow2, frow3, irow0, irow1, irow2, irow3; + v16u8 t0, t1, t2, t3, t4, t5; + LD_UW4(frow, 4, frow0, frow1, frow2, frow3); + LD_UW4(irow, 4, irow0, irow1, irow2, irow3); + CALC_MULT_FIX2_16(frow0, frow1, irow0, irow1, AB, scale, shift, t0, t1); + CALC_MULT_FIX2_16(frow2, frow3, irow2, irow3, AB, scale, shift, t2, t3); + PCKEV_B2_UB(t1, t0, t3, t2, t4, t5); + t0 = (v16u8)__msa_pckev_b((v16i8)t5, (v16i8)t4); + ST_UB(t0, dst); + frow += 16; + irow += 16; + dst += 16; + length -= 16; + } + if (length > 0) { + int x_out; + if (length >= 12) { + uint32_t val0_m, val1_m, val2_m; + v4u32 frow0, frow1, frow2, irow0, irow1, irow2; + LD_UW3(frow, 4, frow0, frow1, frow2); + LD_UW3(irow, 4, irow0, irow1, irow2); + CALC_MULT_FIX2_4(frow0, irow0, AB, scale, shift, val0_m); + CALC_MULT_FIX2_4(frow1, irow1, AB, scale, shift, val1_m); + CALC_MULT_FIX2_4(frow2, irow2, AB, scale, shift, val2_m); + SW3(val0_m, val1_m, val2_m, dst, 4); + frow += 12; + irow += 12; + dst += 12; + length -= 12; + } else if (length >= 8) { + uint32_t val0_m, val1_m; + v4u32 frow0, frow1, irow0, irow1; + LD_UW2(frow, 4, frow0, frow1); + LD_UW2(irow, 4, irow0, irow1); + CALC_MULT_FIX2_4(frow0, irow0, AB, scale, shift, val0_m); + CALC_MULT_FIX2_4(frow1, irow1, AB, scale, shift, val1_m); + SW2(val0_m, val1_m, dst, 4); + frow += 4; + irow += 4; + dst += 4; + length -= 4; + } else if (length >= 4) { + uint32_t val0_m; + const v4u32 frow0 = LD_UW(frow + 0); + const v4u32 irow0 = LD_UW(irow + 0); + CALC_MULT_FIX2_4(frow0, irow0, AB, scale, shift, val0_m); + SW(val0_m, dst); + frow += 4; + irow += 4; + dst += 4; + length -= 4; + } + for (x_out = 0; x_out < length; ++x_out) { + const uint64_t I = (uint64_t)A * frow[x_out] + + (uint64_t)B * irow[x_out]; + const uint32_t J = (uint32_t)((I + ROUNDER) >> WEBP_RESCALER_RFIX); + const int v = (int)MULT_FIX(J, wrk->fy_scale); + assert(v >= 0 && v <= 255); + dst[x_out] = v; + } + } +} + +static void RescalerExportRowExpand(WebPRescaler* const wrk) { + uint8_t* dst = wrk->dst; + rescaler_t* irow = wrk->irow; + const int x_out_max = wrk->dst_width * wrk->num_channels; + const rescaler_t* frow = wrk->frow; + assert(!WebPRescalerOutputDone(wrk)); + assert(wrk->y_accum <= 0); + assert(wrk->y_expand); + assert(wrk->y_sub != 0); + if (wrk->y_accum == 0) { + ExportRowExpand_0(frow, dst, x_out_max, wrk); + } else { + ExportRowExpand_1(frow, irow, dst, x_out_max, wrk); + } +} + +static WEBP_INLINE void ExportRowShrink_0(const uint32_t* frow, uint32_t* irow, + uint8_t* dst, int length, + const uint32_t yscale, + WebPRescaler* const wrk) { + const v4u32 y_scale = (v4u32)__msa_fill_w(yscale); + const v4u32 fxyscale = (v4u32)__msa_fill_w(wrk->fxy_scale); + const v4u32 shiftval = (v4u32)__msa_fill_w(WEBP_RESCALER_RFIX); + const v4i32 zero = { 0 }; + + while (length >= 16) { + v4u32 src0, src1, src2, src3, frac0, frac1, frac2, frac3; + v16u8 out; + LD_UW4(frow, 4, src0, src1, src2, src3); + CALC_MULT_FIX1_16(src0, src1, src2, src3, y_scale, shiftval, + frac0, frac1, frac2, frac3); + LD_UW4(irow, 4, src0, src1, src2, src3); + SUB4(src0, frac0, src1, frac1, src2, frac2, src3, frac3, + src0, src1, src2, src3); + CALC_MULT_FIX_16(src0, src1, src2, src3, fxyscale, shiftval, out); + ST_UB(out, dst); + ST_UW4(frac0, frac1, frac2, frac3, irow, 4); + frow += 16; + irow += 16; + dst += 16; + length -= 16; + } + if (length > 0) { + int x_out; + if (length >= 12) { + uint32_t val0_m, val1_m, val2_m; + v4u32 src0, src1, src2, frac0, frac1, frac2; + LD_UW3(frow, 4, src0, src1, src2); + CALC_MULT_FIX1_4(src0, y_scale, shiftval, frac0); + CALC_MULT_FIX1_4(src1, y_scale, shiftval, frac1); + CALC_MULT_FIX1_4(src2, y_scale, shiftval, frac2); + LD_UW3(irow, 4, src0, src1, src2); + SUB3(src0, frac0, src1, frac1, src2, frac2, src0, src1, src2); + CALC_MULT_FIX_4(src0, fxyscale, shiftval, val0_m); + CALC_MULT_FIX_4(src1, fxyscale, shiftval, val1_m); + CALC_MULT_FIX_4(src2, fxyscale, shiftval, val2_m); + SW3(val0_m, val1_m, val2_m, dst, 4); + ST_UW3(frac0, frac1, frac2, irow, 4); + frow += 12; + irow += 12; + dst += 12; + length -= 12; + } else if (length >= 8) { + uint32_t val0_m, val1_m; + v4u32 src0, src1, frac0, frac1; + LD_UW2(frow, 4, src0, src1); + CALC_MULT_FIX1_4(src0, y_scale, shiftval, frac0); + CALC_MULT_FIX1_4(src1, y_scale, shiftval, frac1); + LD_UW2(irow, 4, src0, src1); + SUB2(src0, frac0, src1, frac1, src0, src1); + CALC_MULT_FIX_4(src0, fxyscale, shiftval, val0_m); + CALC_MULT_FIX_4(src1, fxyscale, shiftval, val1_m); + SW2(val0_m, val1_m, dst, 4); + ST_UW2(frac0, frac1, irow, 4); + frow += 8; + irow += 8; + dst += 8; + length -= 8; + } else if (length >= 4) { + uint32_t val0_m; + v4u32 frac0; + v4u32 src0 = LD_UW(frow); + CALC_MULT_FIX1_4(src0, y_scale, shiftval, frac0); + src0 = LD_UW(irow); + src0 = src0 - frac0; + CALC_MULT_FIX_4(src0, fxyscale, shiftval, val0_m); + SW(val0_m, dst); + ST_UW(frac0, irow); + frow += 4; + irow += 4; + dst += 4; + length -= 4; + } + for (x_out = 0; x_out < length; ++x_out) { + const uint32_t frac = (uint32_t)MULT_FIX(frow[x_out], yscale); + const int v = (int)MULT_FIX(irow[x_out] - frac, wrk->fxy_scale); + assert(v >= 0 && v <= 255); + dst[x_out] = v; + irow[x_out] = frac; + } + } +} + +static WEBP_INLINE void ExportRowShrink_1(uint32_t* irow, uint8_t* dst, + int length, + WebPRescaler* const wrk) { + const v4u32 scale = (v4u32)__msa_fill_w(wrk->fxy_scale); + const v4u32 shift = (v4u32)__msa_fill_w(WEBP_RESCALER_RFIX); + const v4i32 zero = { 0 }; + + while (length >= 16) { + v4u32 src0, src1, src2, src3; + v16u8 dst0; + LD_UW4(irow, 4, src0, src1, src2, src3); + CALC_MULT_FIX_16(src0, src1, src2, src3, scale, shift, dst0); + ST_UB(dst0, dst); + ST_SW4(zero, zero, zero, zero, irow, 4); + length -= 16; + irow += 16; + dst += 16; + } + if (length > 0) { + int x_out; + if (length >= 12) { + uint32_t val0_m, val1_m, val2_m; + v4u32 src0, src1, src2; + LD_UW3(irow, 4, src0, src1, src2); + CALC_MULT_FIX_4(src0, scale, shift, val0_m); + CALC_MULT_FIX_4(src1, scale, shift, val1_m); + CALC_MULT_FIX_4(src2, scale, shift, val2_m); + SW3(val0_m, val1_m, val2_m, dst, 4); + ST_SW3(zero, zero, zero, irow, 4); + length -= 12; + irow += 12; + dst += 12; + } else if (length >= 8) { + uint32_t val0_m, val1_m; + v4u32 src0, src1; + LD_UW2(irow, 4, src0, src1); + CALC_MULT_FIX_4(src0, scale, shift, val0_m); + CALC_MULT_FIX_4(src1, scale, shift, val1_m); + SW2(val0_m, val1_m, dst, 4); + ST_SW2(zero, zero, irow, 4); + length -= 8; + irow += 8; + dst += 8; + } else if (length >= 4) { + uint32_t val0_m; + const v4u32 src0 = LD_UW(irow + 0); + CALC_MULT_FIX_4(src0, scale, shift, val0_m); + SW(val0_m, dst); + ST_SW(zero, irow); + length -= 4; + irow += 4; + dst += 4; + } + for (x_out = 0; x_out < length; ++x_out) { + const int v = (int)MULT_FIX(irow[x_out], wrk->fxy_scale); + assert(v >= 0 && v <= 255); + dst[x_out] = v; + irow[x_out] = 0; + } + } +} + +static void RescalerExportRowShrink(WebPRescaler* const wrk) { + uint8_t* dst = wrk->dst; + rescaler_t* irow = wrk->irow; + const int x_out_max = wrk->dst_width * wrk->num_channels; + const rescaler_t* frow = wrk->frow; + const uint32_t yscale = wrk->fy_scale * (-wrk->y_accum); + assert(!WebPRescalerOutputDone(wrk)); + assert(wrk->y_accum <= 0); + assert(!wrk->y_expand); + if (yscale) { + ExportRowShrink_0(frow, irow, dst, x_out_max, yscale, wrk); + } else { + ExportRowShrink_1(irow, dst, x_out_max, wrk); + } +} + +//------------------------------------------------------------------------------ +// Entry point + +extern void WebPRescalerDspInitMSA(void); + +WEBP_TSAN_IGNORE_FUNCTION void WebPRescalerDspInitMSA(void) { + WebPRescalerExportRowExpand = RescalerExportRowExpand; + WebPRescalerExportRowShrink = RescalerExportRowShrink; +} + +#else // !WEBP_USE_MSA + +WEBP_DSP_INIT_STUB(WebPRescalerDspInitMSA) + +#endif // WEBP_USE_MSA diff --git a/thirdparty/libwebp/dsp/rescaler_neon.c b/thirdparty/libwebp/dsp/rescaler_neon.c index 16fd450ea3..b2dd8f30cc 100644 --- a/thirdparty/libwebp/dsp/rescaler_neon.c +++ b/thirdparty/libwebp/dsp/rescaler_neon.c @@ -18,7 +18,7 @@ #include #include #include "./neon.h" -#include "../utils/rescaler.h" +#include "../utils/rescaler_utils.h" #define ROUNDER (WEBP_RESCALER_ONE >> 1) #define MULT_FIX_C(x, y) (((uint64_t)(x) * (y) + ROUNDER) >> WEBP_RESCALER_RFIX) diff --git a/thirdparty/libwebp/dsp/rescaler_sse2.c b/thirdparty/libwebp/dsp/rescaler_sse2.c index 5b9702817c..8271c22e05 100644 --- a/thirdparty/libwebp/dsp/rescaler_sse2.c +++ b/thirdparty/libwebp/dsp/rescaler_sse2.c @@ -17,7 +17,7 @@ #include #include -#include "../utils/rescaler.h" +#include "../utils/rescaler_utils.h" #include "../utils/utils.h" //------------------------------------------------------------------------------ diff --git a/thirdparty/libwebp/dsp/upsampling.c b/thirdparty/libwebp/dsp/upsampling.c index 651274fcee..265e722c10 100644 --- a/thirdparty/libwebp/dsp/upsampling.c +++ b/thirdparty/libwebp/dsp/upsampling.c @@ -215,6 +215,7 @@ WEBP_TSAN_IGNORE_FUNCTION void WebPInitYUV444Converters(void) { extern void WebPInitUpsamplersSSE2(void); extern void WebPInitUpsamplersNEON(void); extern void WebPInitUpsamplersMIPSdspR2(void); +extern void WebPInitUpsamplersMSA(void); static volatile VP8CPUInfo upsampling_last_cpuinfo_used2 = (VP8CPUInfo)&upsampling_last_cpuinfo_used2; @@ -251,6 +252,11 @@ WEBP_TSAN_IGNORE_FUNCTION void WebPInitUpsamplers(void) { if (VP8GetCPUInfo(kMIPSdspR2)) { WebPInitUpsamplersMIPSdspR2(); } +#endif +#if defined(WEBP_USE_MSA) + if (VP8GetCPUInfo(kMSA)) { + WebPInitUpsamplersMSA(); + } #endif } #endif // FANCY_UPSAMPLING diff --git a/thirdparty/libwebp/dsp/upsampling_msa.c b/thirdparty/libwebp/dsp/upsampling_msa.c new file mode 100644 index 0000000000..f24926fa94 --- /dev/null +++ b/thirdparty/libwebp/dsp/upsampling_msa.c @@ -0,0 +1,678 @@ +// Copyright 2016 Google Inc. All Rights Reserved. +// +// Use of this source code is governed by a BSD-style license +// that can be found in the COPYING file in the root of the source +// tree. An additional intellectual property rights grant can be found +// in the file PATENTS. All contributing project authors may +// be found in the AUTHORS file in the root of the source tree. +// ----------------------------------------------------------------------------- +// +// MSA version of YUV to RGB upsampling functions. +// +// Author: Prashant Patil (prashant.patil@imgtec.com) + +#include +#include "./dsp.h" + +#if defined(WEBP_USE_MSA) + +#include "./msa_macro.h" +#include "./yuv.h" + +#ifdef FANCY_UPSAMPLING + +#define ILVR_UW2(in, out0, out1) do { \ + const v8i16 t0 = (v8i16)__msa_ilvr_b((v16i8)zero, (v16i8)in); \ + out0 = (v4u32)__msa_ilvr_h((v8i16)zero, t0); \ + out1 = (v4u32)__msa_ilvl_h((v8i16)zero, t0); \ +} while (0) + +#define ILVRL_UW4(in, out0, out1, out2, out3) do { \ + v16u8 t0, t1; \ + ILVRL_B2_UB(zero, in, t0, t1); \ + ILVRL_H2_UW(zero, t0, out0, out1); \ + ILVRL_H2_UW(zero, t1, out2, out3); \ +} while (0) + +#define MULTHI_16(in0, in1, in2, in3, cnst, out0, out1) do { \ + const v4i32 const0 = (v4i32)__msa_fill_w(cnst * 256); \ + v4u32 temp0, temp1, temp2, temp3; \ + MUL4(in0, const0, in1, const0, in2, const0, in3, const0, \ + temp0, temp1, temp2, temp3); \ + PCKOD_H2_UH(temp1, temp0, temp3, temp2, out0, out1); \ +} while (0) + +#define MULTHI_8(in0, in1, cnst, out0) do { \ + const v4i32 const0 = (v4i32)__msa_fill_w(cnst * 256); \ + v4u32 temp0, temp1; \ + MUL2(in0, const0, in1, const0, temp0, temp1); \ + out0 = (v8u16)__msa_pckod_h((v8i16)temp1, (v8i16)temp0); \ +} while (0) + +#define CALC_R16(y0, y1, v0, v1, dst) do { \ + const v8i16 const_a = (v8i16)__msa_fill_h(14234); \ + const v8i16 a0 = __msa_adds_s_h((v8i16)y0, (v8i16)v0); \ + const v8i16 a1 = __msa_adds_s_h((v8i16)y1, (v8i16)v1); \ + v8i16 b0 = __msa_subs_s_h(a0, const_a); \ + v8i16 b1 = __msa_subs_s_h(a1, const_a); \ + SRAI_H2_SH(b0, b1, 6); \ + CLIP_SH2_0_255(b0, b1); \ + dst = (v16u8)__msa_pckev_b((v16i8)b1, (v16i8)b0); \ +} while (0) + +#define CALC_R8(y0, v0, dst) do { \ + const v8i16 const_a = (v8i16)__msa_fill_h(14234); \ + const v8i16 a0 = __msa_adds_s_h((v8i16)y0, (v8i16)v0); \ + v8i16 b0 = __msa_subs_s_h(a0, const_a); \ + b0 = SRAI_H(b0, 6); \ + CLIP_SH_0_255(b0); \ + dst = (v16u8)__msa_pckev_b((v16i8)b0, (v16i8)b0); \ +} while (0) + +#define CALC_G16(y0, y1, u0, u1, v0, v1, dst) do { \ + const v8i16 const_a = (v8i16)__msa_fill_h(8708); \ + v8i16 a0 = __msa_subs_s_h((v8i16)y0, (v8i16)u0); \ + v8i16 a1 = __msa_subs_s_h((v8i16)y1, (v8i16)u1); \ + const v8i16 b0 = __msa_subs_s_h(a0, (v8i16)v0); \ + const v8i16 b1 = __msa_subs_s_h(a1, (v8i16)v1); \ + a0 = __msa_adds_s_h(b0, const_a); \ + a1 = __msa_adds_s_h(b1, const_a); \ + SRAI_H2_SH(a0, a1, 6); \ + CLIP_SH2_0_255(a0, a1); \ + dst = (v16u8)__msa_pckev_b((v16i8)a1, (v16i8)a0); \ +} while (0) + +#define CALC_G8(y0, u0, v0, dst) do { \ + const v8i16 const_a = (v8i16)__msa_fill_h(8708); \ + v8i16 a0 = __msa_subs_s_h((v8i16)y0, (v8i16)u0); \ + const v8i16 b0 = __msa_subs_s_h(a0, (v8i16)v0); \ + a0 = __msa_adds_s_h(b0, const_a); \ + a0 = SRAI_H(a0, 6); \ + CLIP_SH_0_255(a0); \ + dst = (v16u8)__msa_pckev_b((v16i8)a0, (v16i8)a0); \ +} while (0) + +#define CALC_B16(y0, y1, u0, u1, dst) do { \ + const v8u16 const_a = (v8u16)__msa_fill_h(17685); \ + const v8u16 a0 = __msa_adds_u_h((v8u16)y0, u0); \ + const v8u16 a1 = __msa_adds_u_h((v8u16)y1, u1); \ + v8u16 b0 = __msa_subs_u_h(a0, const_a); \ + v8u16 b1 = __msa_subs_u_h(a1, const_a); \ + SRAI_H2_UH(b0, b1, 6); \ + CLIP_UH2_0_255(b0, b1); \ + dst = (v16u8)__msa_pckev_b((v16i8)b1, (v16i8)b0); \ +} while (0) + +#define CALC_B8(y0, u0, dst) do { \ + const v8u16 const_a = (v8u16)__msa_fill_h(17685); \ + const v8u16 a0 = __msa_adds_u_h((v8u16)y0, u0); \ + v8u16 b0 = __msa_subs_u_h(a0, const_a); \ + b0 = SRAI_H(b0, 6); \ + CLIP_UH_0_255(b0); \ + dst = (v16u8)__msa_pckev_b((v16i8)b0, (v16i8)b0); \ +} while (0) + +#define CALC_RGB16(y, u, v, R, G, B) do { \ + const v16u8 zero = { 0 }; \ + v8u16 y0, y1, u0, u1, v0, v1; \ + v4u32 p0, p1, p2, p3; \ + const v16u8 in_y = LD_UB(y); \ + const v16u8 in_u = LD_UB(u); \ + const v16u8 in_v = LD_UB(v); \ + ILVRL_UW4(in_y, p0, p1, p2, p3); \ + MULTHI_16(p0, p1, p2, p3, 19077, y0, y1); \ + ILVRL_UW4(in_v, p0, p1, p2, p3); \ + MULTHI_16(p0, p1, p2, p3, 26149, v0, v1); \ + CALC_R16(y0, y1, v0, v1, R); \ + MULTHI_16(p0, p1, p2, p3, 13320, v0, v1); \ + ILVRL_UW4(in_u, p0, p1, p2, p3); \ + MULTHI_16(p0, p1, p2, p3, 6419, u0, u1); \ + CALC_G16(y0, y1, u0, u1, v0, v1, G); \ + MULTHI_16(p0, p1, p2, p3, 33050, u0, u1); \ + CALC_B16(y0, y1, u0, u1, B); \ +} while (0) + +#define CALC_RGB8(y, u, v, R, G, B) do { \ + const v16u8 zero = { 0 }; \ + v8u16 y0, u0, v0; \ + v4u32 p0, p1; \ + const v16u8 in_y = LD_UB(y); \ + const v16u8 in_u = LD_UB(u); \ + const v16u8 in_v = LD_UB(v); \ + ILVR_UW2(in_y, p0, p1); \ + MULTHI_8(p0, p1, 19077, y0); \ + ILVR_UW2(in_v, p0, p1); \ + MULTHI_8(p0, p1, 26149, v0); \ + CALC_R8(y0, v0, R); \ + MULTHI_8(p0, p1, 13320, v0); \ + ILVR_UW2(in_u, p0, p1); \ + MULTHI_8(p0, p1, 6419, u0); \ + CALC_G8(y0, u0, v0, G); \ + MULTHI_8(p0, p1, 33050, u0); \ + CALC_B8(y0, u0, B); \ +} while (0) + +#define STORE16_3(a0, a1, a2, dst) do { \ + const v16u8 mask0 = { 0, 1, 16, 2, 3, 17, 4, 5, 18, 6, 7, 19, \ + 8, 9, 20, 10 }; \ + const v16u8 mask1 = { 0, 21, 1, 2, 22, 3, 4, 23, 5, 6, 24, 7, \ + 8, 25, 9, 10 }; \ + const v16u8 mask2 = { 26, 0, 1, 27, 2, 3, 28, 4, 5, 29, 6, 7, \ + 30, 8, 9, 31 }; \ + v16u8 out0, out1, out2, tmp0, tmp1, tmp2; \ + ILVRL_B2_UB(a1, a0, tmp0, tmp1); \ + out0 = VSHF_UB(tmp0, a2, mask0); \ + tmp2 = SLDI_UB(tmp1, tmp0, 11); \ + out1 = VSHF_UB(tmp2, a2, mask1); \ + tmp2 = SLDI_UB(tmp1, tmp1, 6); \ + out2 = VSHF_UB(tmp2, a2, mask2); \ + ST_UB(out0, dst + 0); \ + ST_UB(out1, dst + 16); \ + ST_UB(out2, dst + 32); \ +} while (0) + +#define STORE8_3(a0, a1, a2, dst) do { \ + int64_t out_m; \ + const v16u8 mask0 = { 0, 1, 16, 2, 3, 17, 4, 5, 18, 6, 7, 19, \ + 8, 9, 20, 10 }; \ + const v16u8 mask1 = { 11, 21, 12, 13, 22, 14, 15, 23, \ + 255, 255, 255, 255, 255, 255, 255, 255 }; \ + const v16u8 tmp0 = (v16u8)__msa_ilvr_b((v16i8)a1, (v16i8)a0); \ + v16u8 out0, out1; \ + VSHF_B2_UB(tmp0, a2, tmp0, a2, mask0, mask1, out0, out1); \ + ST_UB(out0, dst); \ + out_m = __msa_copy_s_d((v2i64)out1, 0); \ + SD(out_m, dst + 16); \ +} while (0) + +#define STORE16_4(a0, a1, a2, a3, dst) do { \ + v16u8 tmp0, tmp1, tmp2, tmp3; \ + v16u8 out0, out1, out2, out3; \ + ILVRL_B2_UB(a1, a0, tmp0, tmp1); \ + ILVRL_B2_UB(a3, a2, tmp2, tmp3); \ + ILVRL_H2_UB(tmp2, tmp0, out0, out1); \ + ILVRL_H2_UB(tmp3, tmp1, out2, out3); \ + ST_UB(out0, dst + 0); \ + ST_UB(out1, dst + 16); \ + ST_UB(out2, dst + 32); \ + ST_UB(out3, dst + 48); \ +} while (0) + +#define STORE8_4(a0, a1, a2, a3, dst) do { \ + v16u8 tmp0, tmp1, tmp2, tmp3; \ + ILVR_B2_UB(a1, a0, a3, a2, tmp0, tmp1); \ + ILVRL_H2_UB(tmp1, tmp0, tmp2, tmp3); \ + ST_UB(tmp2, dst + 0); \ + ST_UB(tmp3, dst + 16); \ +} while (0) + +#define STORE2_16(a0, a1, dst) do { \ + v16u8 out0, out1; \ + ILVRL_B2_UB(a1, a0, out0, out1); \ + ST_UB(out0, dst + 0); \ + ST_UB(out1, dst + 16); \ +} while (0) + +#define STORE2_8(a0, a1, dst) do { \ + const v16u8 out0 = (v16u8)__msa_ilvr_b((v16i8)a1, (v16i8)a0); \ + ST_UB(out0, dst); \ +} while (0) + +#define CALC_RGBA4444(y, u, v, out0, out1, N, dst) do { \ + CALC_RGB##N(y, u, v, R, G, B); \ + tmp0 = ANDI_B(R, 0xf0); \ + tmp1 = SRAI_B(G, 4); \ + RG = tmp0 | tmp1; \ + tmp0 = ANDI_B(B, 0xf0); \ + BA = ORI_B(tmp0, 0x0f); \ + STORE2_##N(out0, out1, dst); \ +} while (0) + +#define CALC_RGB565(y, u, v, out0, out1, N, dst) do { \ + CALC_RGB##N(y, u, v, R, G, B); \ + tmp0 = ANDI_B(R, 0xf8); \ + tmp1 = SRAI_B(G, 5); \ + RG = tmp0 | tmp1; \ + tmp0 = SLLI_B(G, 3); \ + tmp1 = ANDI_B(tmp0, 0xe0); \ + tmp0 = SRAI_B(B, 3); \ + GB = tmp0 | tmp1; \ + STORE2_##N(out0, out1, dst); \ +} while (0) + +static WEBP_INLINE int Clip8(int v) { + return v < 0 ? 0 : v > 255 ? 255 : v; +} + +static void YuvToRgb(int y, int u, int v, uint8_t* const rgb) { + const int y1 = MultHi(y, 19077); + const int r1 = y1 + MultHi(v, 26149) - 14234; + const int g1 = y1 - MultHi(u, 6419) - MultHi(v, 13320) + 8708; + const int b1 = y1 + MultHi(u, 33050) - 17685; + rgb[0] = Clip8(r1 >> 6); + rgb[1] = Clip8(g1 >> 6); + rgb[2] = Clip8(b1 >> 6); +} + +static void YuvToBgr(int y, int u, int v, uint8_t* const bgr) { + const int y1 = MultHi(y, 19077); + const int r1 = y1 + MultHi(v, 26149) - 14234; + const int g1 = y1 - MultHi(u, 6419) - MultHi(v, 13320) + 8708; + const int b1 = y1 + MultHi(u, 33050) - 17685; + bgr[0] = Clip8(b1 >> 6); + bgr[1] = Clip8(g1 >> 6); + bgr[2] = Clip8(r1 >> 6); +} + +static void YuvToRgb565(int y, int u, int v, uint8_t* const rgb) { + const int y1 = MultHi(y, 19077); + const int r1 = y1 + MultHi(v, 26149) - 14234; + const int g1 = y1 - MultHi(u, 6419) - MultHi(v, 13320) + 8708; + const int b1 = y1 + MultHi(u, 33050) - 17685; + const int r = Clip8(r1 >> 6); + const int g = Clip8(g1 >> 6); + const int b = Clip8(b1 >> 6); + const int rg = (r & 0xf8) | (g >> 5); + const int gb = ((g << 3) & 0xe0) | (b >> 3); +#ifdef WEBP_SWAP_16BIT_CSP + rgb[0] = gb; + rgb[1] = rg; +#else + rgb[0] = rg; + rgb[1] = gb; +#endif +} + +static void YuvToRgba4444(int y, int u, int v, uint8_t* const argb) { + const int y1 = MultHi(y, 19077); + const int r1 = y1 + MultHi(v, 26149) - 14234; + const int g1 = y1 - MultHi(u, 6419) - MultHi(v, 13320) + 8708; + const int b1 = y1 + MultHi(u, 33050) - 17685; + const int r = Clip8(r1 >> 6); + const int g = Clip8(g1 >> 6); + const int b = Clip8(b1 >> 6); + const int rg = (r & 0xf0) | (g >> 4); + const int ba = (b & 0xf0) | 0x0f; // overwrite the lower 4 bits +#ifdef WEBP_SWAP_16BIT_CSP + argb[0] = ba; + argb[1] = rg; +#else + argb[0] = rg; + argb[1] = ba; +#endif +} + +static void YuvToArgb(uint8_t y, uint8_t u, uint8_t v, uint8_t* const argb) { + argb[0] = 0xff; + YuvToRgb(y, u, v, argb + 1); +} + +static void YuvToBgra(uint8_t y, uint8_t u, uint8_t v, uint8_t* const bgra) { + YuvToBgr(y, u, v, bgra); + bgra[3] = 0xff; +} + +static void YuvToRgba(uint8_t y, uint8_t u, uint8_t v, uint8_t* const rgba) { + YuvToRgb(y, u, v, rgba); + rgba[3] = 0xff; +} + +static void YuvToRgbLine(const uint8_t* y, const uint8_t* u, + const uint8_t* v, uint8_t* dst, int length) { + v16u8 R, G, B; + while (length >= 16) { + CALC_RGB16(y, u, v, R, G, B); + STORE16_3(R, G, B, dst); + y += 16; + u += 16; + v += 16; + dst += 16 * 3; + length -= 16; + } + if (length > 8) { + uint8_t temp[3 * 16] = { 0 }; + memcpy(temp, y, length * sizeof(*temp)); + CALC_RGB16(temp, u, v, R, G, B); + STORE16_3(R, G, B, temp); + memcpy(dst, temp, length * 3 * sizeof(*dst)); + } else if (length > 0) { + uint8_t temp[3 * 8] = { 0 }; + memcpy(temp, y, length * sizeof(*temp)); + CALC_RGB8(temp, u, v, R, G, B); + STORE8_3(R, G, B, temp); + memcpy(dst, temp, length * 3 * sizeof(*dst)); + } +} + +static void YuvToBgrLine(const uint8_t* y, const uint8_t* u, + const uint8_t* v, uint8_t* dst, int length) { + v16u8 R, G, B; + while (length >= 16) { + CALC_RGB16(y, u, v, R, G, B); + STORE16_3(B, G, R, dst); + y += 16; + u += 16; + v += 16; + dst += 16 * 3; + length -= 16; + } + if (length > 8) { + uint8_t temp[3 * 16] = { 0 }; + memcpy(temp, y, length * sizeof(*temp)); + CALC_RGB16(temp, u, v, R, G, B); + STORE16_3(B, G, R, temp); + memcpy(dst, temp, length * 3 * sizeof(*dst)); + } else if (length > 0) { + uint8_t temp[3 * 8] = { 0 }; + memcpy(temp, y, length * sizeof(*temp)); + CALC_RGB8(temp, u, v, R, G, B); + STORE8_3(B, G, R, temp); + memcpy(dst, temp, length * 3 * sizeof(*dst)); + } +} + +static void YuvToRgbaLine(const uint8_t* y, const uint8_t* u, + const uint8_t* v, uint8_t* dst, int length) { + v16u8 R, G, B; + const v16u8 A = (v16u8)__msa_ldi_b(0xff); + while (length >= 16) { + CALC_RGB16(y, u, v, R, G, B); + STORE16_4(R, G, B, A, dst); + y += 16; + u += 16; + v += 16; + dst += 16 * 4; + length -= 16; + } + if (length > 8) { + uint8_t temp[4 * 16] = { 0 }; + memcpy(temp, y, length * sizeof(*temp)); + CALC_RGB16(&temp[0], u, v, R, G, B); + STORE16_4(R, G, B, A, temp); + memcpy(dst, temp, length * 4 * sizeof(*dst)); + } else if (length > 0) { + uint8_t temp[4 * 8] = { 0 }; + memcpy(temp, y, length * sizeof(*temp)); + CALC_RGB8(temp, u, v, R, G, B); + STORE8_4(R, G, B, A, temp); + memcpy(dst, temp, length * 4 * sizeof(*dst)); + } +} + +static void YuvToBgraLine(const uint8_t* y, const uint8_t* u, + const uint8_t* v, uint8_t* dst, int length) { + v16u8 R, G, B; + const v16u8 A = (v16u8)__msa_ldi_b(0xff); + while (length >= 16) { + CALC_RGB16(y, u, v, R, G, B); + STORE16_4(B, G, R, A, dst); + y += 16; + u += 16; + v += 16; + dst += 16 * 4; + length -= 16; + } + if (length > 8) { + uint8_t temp[4 * 16] = { 0 }; + memcpy(temp, y, length * sizeof(*temp)); + CALC_RGB16(temp, u, v, R, G, B); + STORE16_4(B, G, R, A, temp); + memcpy(dst, temp, length * 4 * sizeof(*dst)); + } else if (length > 0) { + uint8_t temp[4 * 8] = { 0 }; + memcpy(temp, y, length * sizeof(*temp)); + CALC_RGB8(temp, u, v, R, G, B); + STORE8_4(B, G, R, A, temp); + memcpy(dst, temp, length * 4 * sizeof(*dst)); + } +} + +static void YuvToArgbLine(const uint8_t* y, const uint8_t* u, + const uint8_t* v, uint8_t* dst, int length) { + v16u8 R, G, B; + const v16u8 A = (v16u8)__msa_ldi_b(0xff); + while (length >= 16) { + CALC_RGB16(y, u, v, R, G, B); + STORE16_4(A, R, G, B, dst); + y += 16; + u += 16; + v += 16; + dst += 16 * 4; + length -= 16; + } + if (length > 8) { + uint8_t temp[4 * 16] = { 0 }; + memcpy(temp, y, length * sizeof(*temp)); + CALC_RGB16(temp, u, v, R, G, B); + STORE16_4(A, R, G, B, temp); + memcpy(dst, temp, length * 4 * sizeof(*dst)); + } else if (length > 0) { + uint8_t temp[4 * 8] = { 0 }; + memcpy(temp, y, length * sizeof(*temp)); + CALC_RGB8(temp, u, v, R, G, B); + STORE8_4(A, R, G, B, temp); + memcpy(dst, temp, length * 4 * sizeof(*dst)); + } +} + +static void YuvToRgba4444Line(const uint8_t* y, const uint8_t* u, + const uint8_t* v, uint8_t* dst, int length) { + v16u8 R, G, B, RG, BA, tmp0, tmp1; + while (length >= 16) { + #ifdef WEBP_SWAP_16BIT_CSP + CALC_RGBA4444(y, u, v, BA, RG, 16, dst); + #else + CALC_RGBA4444(y, u, v, RG, BA, 16, dst); + #endif + y += 16; + u += 16; + v += 16; + dst += 16 * 2; + length -= 16; + } + if (length > 8) { + uint8_t temp[2 * 16] = { 0 }; + memcpy(temp, y, length * sizeof(*temp)); +#ifdef WEBP_SWAP_16BIT_CSP + CALC_RGBA4444(temp, u, v, BA, RG, 16, temp); +#else + CALC_RGBA4444(temp, u, v, RG, BA, 16, temp); +#endif + memcpy(dst, temp, length * 2 * sizeof(*dst)); + } else if (length > 0) { + uint8_t temp[2 * 8] = { 0 }; + memcpy(temp, y, length * sizeof(*temp)); +#ifdef WEBP_SWAP_16BIT_CSP + CALC_RGBA4444(temp, u, v, BA, RG, 8, temp); +#else + CALC_RGBA4444(temp, u, v, RG, BA, 8, temp); +#endif + memcpy(dst, temp, length * 2 * sizeof(*dst)); + } +} + +static void YuvToRgb565Line(const uint8_t* y, const uint8_t* u, + const uint8_t* v, uint8_t* dst, int length) { + v16u8 R, G, B, RG, GB, tmp0, tmp1; + while (length >= 16) { + #ifdef WEBP_SWAP_16BIT_CSP + CALC_RGB565(y, u, v, GB, RG, 16, dst); + #else + CALC_RGB565(y, u, v, RG, GB, 16, dst); + #endif + y += 16; + u += 16; + v += 16; + dst += 16 * 2; + length -= 16; + } + if (length > 8) { + uint8_t temp[2 * 16] = { 0 }; + memcpy(temp, y, length * sizeof(*temp)); +#ifdef WEBP_SWAP_16BIT_CSP + CALC_RGB565(temp, u, v, GB, RG, 16, temp); +#else + CALC_RGB565(temp, u, v, RG, GB, 16, temp); +#endif + memcpy(dst, temp, length * 2 * sizeof(*dst)); + } else if (length > 0) { + uint8_t temp[2 * 8] = { 0 }; + memcpy(temp, y, length * sizeof(*temp)); +#ifdef WEBP_SWAP_16BIT_CSP + CALC_RGB565(temp, u, v, GB, RG, 8, temp); +#else + CALC_RGB565(temp, u, v, RG, GB, 8, temp); +#endif + memcpy(dst, temp, length * 2 * sizeof(*dst)); + } +} + +#define UPSAMPLE_32PIXELS(a, b, c, d) do { \ + v16u8 s = __msa_aver_u_b(a, d); \ + v16u8 t = __msa_aver_u_b(b, c); \ + const v16u8 st = s ^ t; \ + v16u8 ad = a ^ d; \ + v16u8 bc = b ^ c; \ + v16u8 t0 = ad | bc; \ + v16u8 t1 = t0 | st; \ + v16u8 t2 = ANDI_B(t1, 1); \ + v16u8 t3 = __msa_aver_u_b(s, t); \ + const v16u8 k = t3 - t2; \ + v16u8 diag1, diag2; \ + AVER_UB2_UB(t, k, s, k, t0, t1); \ + bc = bc & st; \ + ad = ad & st; \ + t = t ^ k; \ + s = s ^ k; \ + t2 = bc | t; \ + t3 = ad | s; \ + t2 = ANDI_B(t2, 1); \ + t3 = ANDI_B(t3, 1); \ + SUB2(t0, t2, t1, t3, diag1, diag2); \ + AVER_UB2_UB(a, diag1, b, diag2, t0, t1); \ + ILVRL_B2_UB(t1, t0, a, b); \ + if (pbot_y != NULL) { \ + AVER_UB2_UB(c, diag2, d, diag1, t0, t1); \ + ILVRL_B2_UB(t1, t0, c, d); \ + } \ +} while (0) + +#define UPSAMPLE_FUNC(FUNC_NAME, FUNC, XSTEP) \ +static void FUNC_NAME(const uint8_t* top_y, const uint8_t* bot_y, \ + const uint8_t* top_u, const uint8_t* top_v, \ + const uint8_t* cur_u, const uint8_t* cur_v, \ + uint8_t* top_dst, uint8_t* bot_dst, int len) \ +{ \ + int size = (len - 1) >> 1; \ + uint8_t temp_u[64]; \ + uint8_t temp_v[64]; \ + const uint32_t tl_uv = ((top_u[0]) | ((top_v[0]) << 16)); \ + const uint32_t l_uv = ((cur_u[0]) | ((cur_v[0]) << 16)); \ + const uint32_t uv0 = (3 * tl_uv + l_uv + 0x00020002u) >> 2; \ + const uint8_t* ptop_y = &top_y[1]; \ + uint8_t *ptop_dst = top_dst + XSTEP; \ + const uint8_t* pbot_y = &bot_y[1]; \ + uint8_t *pbot_dst = bot_dst + XSTEP; \ + \ + FUNC(top_y[0], uv0 & 0xff, (uv0 >> 16), top_dst); \ + if (bot_y != NULL) { \ + const uint32_t uv1 = (3 * l_uv + tl_uv + 0x00020002u) >> 2; \ + FUNC(bot_y[0], uv1 & 0xff, (uv1 >> 16), bot_dst); \ + } \ + while (size >= 16) { \ + v16u8 tu0, tu1, tv0, tv1, cu0, cu1, cv0, cv1; \ + LD_UB2(top_u, 1, tu0, tu1); \ + LD_UB2(cur_u, 1, cu0, cu1); \ + LD_UB2(top_v, 1, tv0, tv1); \ + LD_UB2(cur_v, 1, cv0, cv1); \ + UPSAMPLE_32PIXELS(tu0, tu1, cu0, cu1); \ + UPSAMPLE_32PIXELS(tv0, tv1, cv0, cv1); \ + ST_UB4(tu0, tu1, cu0, cu1, &temp_u[0], 16); \ + ST_UB4(tv0, tv1, cv0, cv1, &temp_v[0], 16); \ + FUNC##Line(ptop_y, &temp_u[ 0], &temp_v[0], ptop_dst, 32); \ + if (bot_y != NULL) { \ + FUNC##Line(pbot_y, &temp_u[32], &temp_v[32], pbot_dst, 32); \ + } \ + ptop_y += 32; \ + pbot_y += 32; \ + ptop_dst += XSTEP * 32; \ + pbot_dst += XSTEP * 32; \ + top_u += 16; \ + top_v += 16; \ + cur_u += 16; \ + cur_v += 16; \ + size -= 16; \ + } \ + if (size > 0) { \ + v16u8 tu0, tu1, tv0, tv1, cu0, cu1, cv0, cv1; \ + memcpy(&temp_u[ 0], top_u, 17 * sizeof(uint8_t)); \ + memcpy(&temp_u[32], cur_u, 17 * sizeof(uint8_t)); \ + memcpy(&temp_v[ 0], top_v, 17 * sizeof(uint8_t)); \ + memcpy(&temp_v[32], cur_v, 17 * sizeof(uint8_t)); \ + LD_UB2(&temp_u[ 0], 1, tu0, tu1); \ + LD_UB2(&temp_u[32], 1, cu0, cu1); \ + LD_UB2(&temp_v[ 0], 1, tv0, tv1); \ + LD_UB2(&temp_v[32], 1, cv0, cv1); \ + UPSAMPLE_32PIXELS(tu0, tu1, cu0, cu1); \ + UPSAMPLE_32PIXELS(tv0, tv1, cv0, cv1); \ + ST_UB4(tu0, tu1, cu0, cu1, &temp_u[0], 16); \ + ST_UB4(tv0, tv1, cv0, cv1, &temp_v[0], 16); \ + FUNC##Line(ptop_y, &temp_u[ 0], &temp_v[0], ptop_dst, size * 2); \ + if (bot_y != NULL) { \ + FUNC##Line(pbot_y, &temp_u[32], &temp_v[32], pbot_dst, size * 2); \ + } \ + top_u += size; \ + top_v += size; \ + cur_u += size; \ + cur_v += size; \ + } \ + if (!(len & 1)) { \ + const uint32_t t0 = ((top_u[0]) | ((top_v[0]) << 16)); \ + const uint32_t c0 = ((cur_u[0]) | ((cur_v[0]) << 16)); \ + const uint32_t tmp0 = (3 * t0 + c0 + 0x00020002u) >> 2; \ + FUNC(top_y[len - 1], tmp0 & 0xff, (tmp0 >> 16), \ + top_dst + (len - 1) * XSTEP); \ + if (bot_y != NULL) { \ + const uint32_t tmp1 = (3 * c0 + t0 + 0x00020002u) >> 2; \ + FUNC(bot_y[len - 1], tmp1 & 0xff, (tmp1 >> 16), \ + bot_dst + (len - 1) * XSTEP); \ + } \ + } \ +} + +UPSAMPLE_FUNC(UpsampleRgbLinePair, YuvToRgb, 3) +UPSAMPLE_FUNC(UpsampleBgrLinePair, YuvToBgr, 3) +UPSAMPLE_FUNC(UpsampleRgbaLinePair, YuvToRgba, 4) +UPSAMPLE_FUNC(UpsampleBgraLinePair, YuvToBgra, 4) +UPSAMPLE_FUNC(UpsampleArgbLinePair, YuvToArgb, 4) +UPSAMPLE_FUNC(UpsampleRgba4444LinePair, YuvToRgba4444, 2) +UPSAMPLE_FUNC(UpsampleRgb565LinePair, YuvToRgb565, 2) + +//------------------------------------------------------------------------------ +// Entry point + +extern WebPUpsampleLinePairFunc WebPUpsamplers[/* MODE_LAST */]; + +extern void WebPInitUpsamplersMSA(void); + +WEBP_TSAN_IGNORE_FUNCTION void WebPInitUpsamplersMSA(void) { + WebPUpsamplers[MODE_RGB] = UpsampleRgbLinePair; + WebPUpsamplers[MODE_RGBA] = UpsampleRgbaLinePair; + WebPUpsamplers[MODE_BGR] = UpsampleBgrLinePair; + WebPUpsamplers[MODE_BGRA] = UpsampleBgraLinePair; + WebPUpsamplers[MODE_ARGB] = UpsampleArgbLinePair; + WebPUpsamplers[MODE_rgbA] = UpsampleRgbaLinePair; + WebPUpsamplers[MODE_bgrA] = UpsampleBgraLinePair; + WebPUpsamplers[MODE_Argb] = UpsampleArgbLinePair; + WebPUpsamplers[MODE_RGB_565] = UpsampleRgb565LinePair; + WebPUpsamplers[MODE_RGBA_4444] = UpsampleRgba4444LinePair; + WebPUpsamplers[MODE_rgbA_4444] = UpsampleRgba4444LinePair; +} + +#endif // FANCY_UPSAMPLING + +#endif // WEBP_USE_MSA + +#if !(defined(FANCY_UPSAMPLING) && defined(WEBP_USE_MSA)) +WEBP_DSP_INIT_STUB(WebPInitUpsamplersMSA) +#endif diff --git a/thirdparty/libwebp/dsp/upsampling_neon.c b/thirdparty/libwebp/dsp/upsampling_neon.c index 2b0c99bddb..d371a834ff 100644 --- a/thirdparty/libwebp/dsp/upsampling_neon.c +++ b/thirdparty/libwebp/dsp/upsampling_neon.c @@ -28,47 +28,34 @@ // U/V upsampling // Loads 9 pixels each from rows r1 and r2 and generates 16 pixels. -#define UPSAMPLE_16PIXELS(r1, r2, out) { \ - uint8x8_t a = vld1_u8(r1); \ - uint8x8_t b = vld1_u8(r1 + 1); \ - uint8x8_t c = vld1_u8(r2); \ - uint8x8_t d = vld1_u8(r2 + 1); \ - \ - uint16x8_t al = vshll_n_u8(a, 1); \ - uint16x8_t bl = vshll_n_u8(b, 1); \ - uint16x8_t cl = vshll_n_u8(c, 1); \ - uint16x8_t dl = vshll_n_u8(d, 1); \ - \ - uint8x8_t diag1, diag2; \ - uint16x8_t sl; \ - \ +#define UPSAMPLE_16PIXELS(r1, r2, out) do { \ + const uint8x8_t a = vld1_u8(r1 + 0); \ + const uint8x8_t b = vld1_u8(r1 + 1); \ + const uint8x8_t c = vld1_u8(r2 + 0); \ + const uint8x8_t d = vld1_u8(r2 + 1); \ /* a + b + c + d */ \ - sl = vaddl_u8(a, b); \ - sl = vaddw_u8(sl, c); \ - sl = vaddw_u8(sl, d); \ - \ - al = vaddq_u16(sl, al); /* 3a + b + c + d */ \ - bl = vaddq_u16(sl, bl); /* a + 3b + c + d */ \ - \ - al = vaddq_u16(al, dl); /* 3a + b + c + 3d */ \ - bl = vaddq_u16(bl, cl); /* a + 3b + 3c + d */ \ + const uint16x8_t ad = vaddl_u8(a, d); \ + const uint16x8_t bc = vaddl_u8(b, c); \ + const uint16x8_t abcd = vaddq_u16(ad, bc); \ + /* 3a + b + c + 3d */ \ + const uint16x8_t al = vaddq_u16(abcd, vshlq_n_u16(ad, 1)); \ + /* a + 3b + 3c + d */ \ + const uint16x8_t bl = vaddq_u16(abcd, vshlq_n_u16(bc, 1)); \ \ - diag2 = vshrn_n_u16(al, 3); \ - diag1 = vshrn_n_u16(bl, 3); \ + const uint8x8_t diag2 = vshrn_n_u16(al, 3); \ + const uint8x8_t diag1 = vshrn_n_u16(bl, 3); \ \ - a = vrhadd_u8(a, diag1); \ - b = vrhadd_u8(b, diag2); \ - c = vrhadd_u8(c, diag2); \ - d = vrhadd_u8(d, diag1); \ + const uint8x8_t A = vrhadd_u8(a, diag1); \ + const uint8x8_t B = vrhadd_u8(b, diag2); \ + const uint8x8_t C = vrhadd_u8(c, diag2); \ + const uint8x8_t D = vrhadd_u8(d, diag1); \ \ - { \ - uint8x8x2_t a_b, c_d; \ - INIT_VECTOR2(a_b, a, b); \ - INIT_VECTOR2(c_d, c, d); \ - vst2_u8(out, a_b); \ - vst2_u8(out + 32, c_d); \ - } \ -} + uint8x8x2_t A_B, C_D; \ + INIT_VECTOR2(A_B, A, B); \ + INIT_VECTOR2(C_D, C, D); \ + vst2_u8(out + 0, A_B); \ + vst2_u8(out + 32, C_D); \ +} while (0) // Turn the macro into a function for reducing code-size when non-critical static void Upsample16Pixels(const uint8_t *r1, const uint8_t *r2, @@ -93,7 +80,6 @@ static void Upsample16Pixels(const uint8_t *r1, const uint8_t *r2, static const int16_t kCoeffs1[4] = { 19077, 26149, 6419, 13320 }; #define v255 vdup_n_u8(255) -#define v_0x0f vdup_n_u8(15) #define STORE_Rgb(out, r, g, b) do { \ uint8x8x3_t r_g_b; \ @@ -132,21 +118,16 @@ static const int16_t kCoeffs1[4] = { 19077, 26149, 6419, 13320 }; #endif #define STORE_Rgba4444(out, r, g, b) do { \ - const uint8x8_t r1 = vshl_n_u8(vshr_n_u8(r, 4), 4); /* 4bits */ \ - const uint8x8_t g1 = vshr_n_u8(g, 4); \ - const uint8x8_t ba = vorr_u8(b, v_0x0f); \ - const uint8x8_t rg = vorr_u8(r1, g1); \ + const uint8x8_t rg = vsri_n_u8(r, g, 4); /* shift g, insert r */ \ + const uint8x8_t ba = vsri_n_u8(b, v255, 4); /* shift a, insert b */ \ const uint8x8x2_t rgba4444 = ZIP_U8(rg, ba); \ vst1q_u8(out, vcombine_u8(rgba4444.val[0], rgba4444.val[1])); \ } while (0) #define STORE_Rgb565(out, r, g, b) do { \ - const uint8x8_t r1 = vshl_n_u8(vshr_n_u8(r, 3), 3); /* 5bits */ \ - const uint8x8_t g1 = vshr_n_u8(g, 5); /* upper 3bits */\ - const uint8x8_t g2 = vshl_n_u8(vshr_n_u8(g, 2), 5); /* lower 3bits */\ - const uint8x8_t b1 = vshr_n_u8(b, 3); /* 5bits */ \ - const uint8x8_t rg = vorr_u8(r1, g1); \ - const uint8x8_t gb = vorr_u8(g2, b1); \ + const uint8x8_t rg = vsri_n_u8(r, g, 5); /* shift g and insert r */ \ + const uint8x8_t g1 = vshl_n_u8(g, 3); /* pre-shift g: 3bits */ \ + const uint8x8_t gb = vsri_n_u8(g1, b, 3); /* shift b and insert g */ \ const uint8x8x2_t rgb565 = ZIP_U8(rg, gb); \ vst1q_u8(out, vcombine_u8(rgb565.val[0], rgb565.val[1])); \ } while (0) diff --git a/thirdparty/libwebp/dsp/yuv.c b/thirdparty/libwebp/dsp/yuv.c index f50a253168..dd7d9dedfa 100644 --- a/thirdparty/libwebp/dsp/yuv.c +++ b/thirdparty/libwebp/dsp/yuv.c @@ -13,6 +13,8 @@ #include "./yuv.h" +#include + #if defined(WEBP_YUV_USE_TABLE) static int done = 0; @@ -244,6 +246,48 @@ void WebPConvertRGBA32ToUV_C(const uint16_t* rgb, //----------------------------------------------------------------------------- +#define MAX_Y ((1 << 10) - 1) // 10b precision over 16b-arithmetic +static uint16_t clip_y(int v) { + return (v < 0) ? 0 : (v > MAX_Y) ? MAX_Y : (uint16_t)v; +} + +static uint64_t SharpYUVUpdateY_C(const uint16_t* ref, const uint16_t* src, + uint16_t* dst, int len) { + uint64_t diff = 0; + int i; + for (i = 0; i < len; ++i) { + const int diff_y = ref[i] - src[i]; + const int new_y = (int)dst[i] + diff_y; + dst[i] = clip_y(new_y); + diff += (uint64_t)abs(diff_y); + } + return diff; +} + +static void SharpYUVUpdateRGB_C(const int16_t* ref, const int16_t* src, + int16_t* dst, int len) { + int i; + for (i = 0; i < len; ++i) { + const int diff_uv = ref[i] - src[i]; + dst[i] += diff_uv; + } +} + +static void SharpYUVFilterRow_C(const int16_t* A, const int16_t* B, int len, + const uint16_t* best_y, uint16_t* out) { + int i; + for (i = 0; i < len; ++i, ++A, ++B) { + const int v0 = (A[0] * 9 + A[1] * 3 + B[0] * 3 + B[1] + 8) >> 4; + const int v1 = (A[1] * 9 + A[0] * 3 + B[1] * 3 + B[0] + 8) >> 4; + out[2 * i + 0] = clip_y(best_y[2 * i + 0] + v0); + out[2 * i + 1] = clip_y(best_y[2 * i + 1] + v1); + } +} + +#undef MAX_Y + +//----------------------------------------------------------------------------- + void (*WebPConvertRGB24ToY)(const uint8_t* rgb, uint8_t* y, int width); void (*WebPConvertBGR24ToY)(const uint8_t* bgr, uint8_t* y, int width); void (*WebPConvertRGBA32ToUV)(const uint16_t* rgb, @@ -253,10 +297,18 @@ void (*WebPConvertARGBToY)(const uint32_t* argb, uint8_t* y, int width); void (*WebPConvertARGBToUV)(const uint32_t* argb, uint8_t* u, uint8_t* v, int src_width, int do_store); +uint64_t (*WebPSharpYUVUpdateY)(const uint16_t* ref, const uint16_t* src, + uint16_t* dst, int len); +void (*WebPSharpYUVUpdateRGB)(const int16_t* ref, const int16_t* src, + int16_t* dst, int len); +void (*WebPSharpYUVFilterRow)(const int16_t* A, const int16_t* B, int len, + const uint16_t* best_y, uint16_t* out); + static volatile VP8CPUInfo rgba_to_yuv_last_cpuinfo_used = (VP8CPUInfo)&rgba_to_yuv_last_cpuinfo_used; extern void WebPInitConvertARGBToYUVSSE2(void); +extern void WebPInitSharpYUVSSE2(void); WEBP_TSAN_IGNORE_FUNCTION void WebPInitConvertARGBToYUV(void) { if (rgba_to_yuv_last_cpuinfo_used == VP8GetCPUInfo) return; @@ -269,10 +321,15 @@ WEBP_TSAN_IGNORE_FUNCTION void WebPInitConvertARGBToYUV(void) { WebPConvertRGBA32ToUV = WebPConvertRGBA32ToUV_C; + WebPSharpYUVUpdateY = SharpYUVUpdateY_C; + WebPSharpYUVUpdateRGB = SharpYUVUpdateRGB_C; + WebPSharpYUVFilterRow = SharpYUVFilterRow_C; + if (VP8GetCPUInfo != NULL) { #if defined(WEBP_USE_SSE2) if (VP8GetCPUInfo(kSSE2)) { WebPInitConvertARGBToYUVSSE2(); + WebPInitSharpYUVSSE2(); } #endif // WEBP_USE_SSE2 } diff --git a/thirdparty/libwebp/dsp/yuv.h b/thirdparty/libwebp/dsp/yuv.h index 01c40fcb84..1d33b5863b 100644 --- a/thirdparty/libwebp/dsp/yuv.h +++ b/thirdparty/libwebp/dsp/yuv.h @@ -36,7 +36,7 @@ #define WEBP_DSP_YUV_H_ #include "./dsp.h" -#include "../dec/decode_vp8.h" +#include "../dec/vp8_dec.h" #if defined(WEBP_EXPERIMENTAL_FEATURES) // Do NOT activate this feature for real compression. This is only experimental! diff --git a/thirdparty/libwebp/dsp/yuv_sse2.c b/thirdparty/libwebp/dsp/yuv_sse2.c index e19bddff6c..e33c2bbafd 100644 --- a/thirdparty/libwebp/dsp/yuv_sse2.c +++ b/thirdparty/libwebp/dsp/yuv_sse2.c @@ -15,6 +15,8 @@ #if defined(WEBP_USE_SSE2) +#include "./common_sse2.h" +#include #include //----------------------------------------------------------------------------- @@ -155,30 +157,13 @@ static WEBP_INLINE void PackAndStore565(const __m128i* const R, _mm_storeu_si128((__m128i*)dst, rgb565); } -// Function used several times in PlanarTo24b. -// It samples the in buffer as follows: one every two unsigned char is stored -// at the beginning of the buffer, while the other half is stored at the end. -static WEBP_INLINE void PlanarTo24bHelper(const __m128i* const in /*in[6]*/, - __m128i* const out /*out[6]*/) { - const __m128i v_mask = _mm_set1_epi16(0x00ff); - - // Take one every two upper 8b values. - out[0] = _mm_packus_epi16(_mm_and_si128(in[0], v_mask), - _mm_and_si128(in[1], v_mask)); - out[1] = _mm_packus_epi16(_mm_and_si128(in[2], v_mask), - _mm_and_si128(in[3], v_mask)); - out[2] = _mm_packus_epi16(_mm_and_si128(in[4], v_mask), - _mm_and_si128(in[5], v_mask)); - // Take one every two lower 8b values. - out[3] = _mm_packus_epi16(_mm_srli_epi16(in[0], 8), _mm_srli_epi16(in[1], 8)); - out[4] = _mm_packus_epi16(_mm_srli_epi16(in[2], 8), _mm_srli_epi16(in[3], 8)); - out[5] = _mm_packus_epi16(_mm_srli_epi16(in[4], 8), _mm_srli_epi16(in[5], 8)); -} - // Pack the planar buffers // rrrr... rrrr... gggg... gggg... bbbb... bbbb.... // triplet by triplet in the output buffer rgb as rgbrgbrgbrgb ... -static WEBP_INLINE void PlanarTo24b(__m128i* const in /*in[6]*/, uint8_t* rgb) { +static WEBP_INLINE void PlanarTo24b(__m128i* const in0, __m128i* const in1, + __m128i* const in2, __m128i* const in3, + __m128i* const in4, __m128i* const in5, + uint8_t* const rgb) { // The input is 6 registers of sixteen 8b but for the sake of explanation, // let's take 6 registers of four 8b values. // To pack, we will keep taking one every two 8b integer and move it @@ -191,22 +176,15 @@ static WEBP_INLINE void PlanarTo24b(__m128i* const in /*in[6]*/, uint8_t* rgb) { // Repeat the same permutations twice more: // r0r4g0g4 | b0b4r1r5 | g1g5b1b5 | r2r6g2g6 | b2b6r3r7 | g3g7b3b7 // r0g0b0r1 | g1b1r2g2 | b2r3g3b3 | r4g4b4r5 | g5b5r6g6 | b6r7g7b7 - __m128i tmp[6]; - PlanarTo24bHelper(in, tmp); - PlanarTo24bHelper(tmp, in); - PlanarTo24bHelper(in, tmp); - // We need to do it two more times than the example as we have sixteen bytes. - PlanarTo24bHelper(tmp, in); - PlanarTo24bHelper(in, tmp); - - _mm_storeu_si128((__m128i*)(rgb + 0), tmp[0]); - _mm_storeu_si128((__m128i*)(rgb + 16), tmp[1]); - _mm_storeu_si128((__m128i*)(rgb + 32), tmp[2]); - _mm_storeu_si128((__m128i*)(rgb + 48), tmp[3]); - _mm_storeu_si128((__m128i*)(rgb + 64), tmp[4]); - _mm_storeu_si128((__m128i*)(rgb + 80), tmp[5]); -} -#undef MK_UINT32 + VP8PlanarTo24b(in0, in1, in2, in3, in4, in5); + + _mm_storeu_si128((__m128i*)(rgb + 0), *in0); + _mm_storeu_si128((__m128i*)(rgb + 16), *in1); + _mm_storeu_si128((__m128i*)(rgb + 32), *in2); + _mm_storeu_si128((__m128i*)(rgb + 48), *in3); + _mm_storeu_si128((__m128i*)(rgb + 64), *in4); + _mm_storeu_si128((__m128i*)(rgb + 80), *in5); +} void VP8YuvToRgba32(const uint8_t* y, const uint8_t* u, const uint8_t* v, uint8_t* dst) { @@ -265,29 +243,29 @@ void VP8YuvToRgb56532(const uint8_t* y, const uint8_t* u, const uint8_t* v, void VP8YuvToRgb32(const uint8_t* y, const uint8_t* u, const uint8_t* v, uint8_t* dst) { __m128i R0, R1, R2, R3, G0, G1, G2, G3, B0, B1, B2, B3; - __m128i rgb[6]; + __m128i rgb0, rgb1, rgb2, rgb3, rgb4, rgb5; - YUV444ToRGB(y + 0, u + 0, v + 0, &R0, &G0, &B0); - YUV444ToRGB(y + 8, u + 8, v + 8, &R1, &G1, &B1); + YUV444ToRGB(y + 0, u + 0, v + 0, &R0, &G0, &B0); + YUV444ToRGB(y + 8, u + 8, v + 8, &R1, &G1, &B1); YUV444ToRGB(y + 16, u + 16, v + 16, &R2, &G2, &B2); YUV444ToRGB(y + 24, u + 24, v + 24, &R3, &G3, &B3); // Cast to 8b and store as RRRRGGGGBBBB. - rgb[0] = _mm_packus_epi16(R0, R1); - rgb[1] = _mm_packus_epi16(R2, R3); - rgb[2] = _mm_packus_epi16(G0, G1); - rgb[3] = _mm_packus_epi16(G2, G3); - rgb[4] = _mm_packus_epi16(B0, B1); - rgb[5] = _mm_packus_epi16(B2, B3); + rgb0 = _mm_packus_epi16(R0, R1); + rgb1 = _mm_packus_epi16(R2, R3); + rgb2 = _mm_packus_epi16(G0, G1); + rgb3 = _mm_packus_epi16(G2, G3); + rgb4 = _mm_packus_epi16(B0, B1); + rgb5 = _mm_packus_epi16(B2, B3); // Pack as RGBRGBRGBRGB. - PlanarTo24b(rgb, dst); + PlanarTo24b(&rgb0, &rgb1, &rgb2, &rgb3, &rgb4, &rgb5, dst); } void VP8YuvToBgr32(const uint8_t* y, const uint8_t* u, const uint8_t* v, uint8_t* dst) { __m128i R0, R1, R2, R3, G0, G1, G2, G3, B0, B1, B2, B3; - __m128i bgr[6]; + __m128i bgr0, bgr1, bgr2, bgr3, bgr4, bgr5; YUV444ToRGB(y + 0, u + 0, v + 0, &R0, &G0, &B0); YUV444ToRGB(y + 8, u + 8, v + 8, &R1, &G1, &B1); @@ -295,15 +273,15 @@ void VP8YuvToBgr32(const uint8_t* y, const uint8_t* u, const uint8_t* v, YUV444ToRGB(y + 24, u + 24, v + 24, &R3, &G3, &B3); // Cast to 8b and store as BBBBGGGGRRRR. - bgr[0] = _mm_packus_epi16(B0, B1); - bgr[1] = _mm_packus_epi16(B2, B3); - bgr[2] = _mm_packus_epi16(G0, G1); - bgr[3] = _mm_packus_epi16(G2, G3); - bgr[4] = _mm_packus_epi16(R0, R1); - bgr[5] = _mm_packus_epi16(R2, R3); + bgr0 = _mm_packus_epi16(B0, B1); + bgr1 = _mm_packus_epi16(B2, B3); + bgr2 = _mm_packus_epi16(G0, G1); + bgr3 = _mm_packus_epi16(G2, G3); + bgr4 = _mm_packus_epi16(R0, R1); + bgr5= _mm_packus_epi16(R2, R3); // Pack as BGRBGRBGRBGR. - PlanarTo24b(bgr, dst); + PlanarTo24b(&bgr0, &bgr1, &bgr2, &bgr3, &bgr4, &bgr5, dst); } //----------------------------------------------------------------------------- @@ -377,7 +355,7 @@ static void YuvToRgbRow(const uint8_t* y, const uint8_t* u, const uint8_t* v, int n; for (n = 0; n + 32 <= len; n += 32, dst += 32 * 3) { __m128i R0, R1, R2, R3, G0, G1, G2, G3, B0, B1, B2, B3; - __m128i rgb[6]; + __m128i rgb0, rgb1, rgb2, rgb3, rgb4, rgb5; YUV420ToRGB(y + 0, u + 0, v + 0, &R0, &G0, &B0); YUV420ToRGB(y + 8, u + 4, v + 4, &R1, &G1, &B1); @@ -385,15 +363,15 @@ static void YuvToRgbRow(const uint8_t* y, const uint8_t* u, const uint8_t* v, YUV420ToRGB(y + 24, u + 12, v + 12, &R3, &G3, &B3); // Cast to 8b and store as RRRRGGGGBBBB. - rgb[0] = _mm_packus_epi16(R0, R1); - rgb[1] = _mm_packus_epi16(R2, R3); - rgb[2] = _mm_packus_epi16(G0, G1); - rgb[3] = _mm_packus_epi16(G2, G3); - rgb[4] = _mm_packus_epi16(B0, B1); - rgb[5] = _mm_packus_epi16(B2, B3); + rgb0 = _mm_packus_epi16(R0, R1); + rgb1 = _mm_packus_epi16(R2, R3); + rgb2 = _mm_packus_epi16(G0, G1); + rgb3 = _mm_packus_epi16(G2, G3); + rgb4 = _mm_packus_epi16(B0, B1); + rgb5 = _mm_packus_epi16(B2, B3); // Pack as RGBRGBRGBRGB. - PlanarTo24b(rgb, dst); + PlanarTo24b(&rgb0, &rgb1, &rgb2, &rgb3, &rgb4, &rgb5, dst); y += 32; u += 16; @@ -413,7 +391,7 @@ static void YuvToBgrRow(const uint8_t* y, const uint8_t* u, const uint8_t* v, int n; for (n = 0; n + 32 <= len; n += 32, dst += 32 * 3) { __m128i R0, R1, R2, R3, G0, G1, G2, G3, B0, B1, B2, B3; - __m128i bgr[6]; + __m128i bgr0, bgr1, bgr2, bgr3, bgr4, bgr5; YUV420ToRGB(y + 0, u + 0, v + 0, &R0, &G0, &B0); YUV420ToRGB(y + 8, u + 4, v + 4, &R1, &G1, &B1); @@ -421,15 +399,15 @@ static void YuvToBgrRow(const uint8_t* y, const uint8_t* u, const uint8_t* v, YUV420ToRGB(y + 24, u + 12, v + 12, &R3, &G3, &B3); // Cast to 8b and store as BBBBGGGGRRRR. - bgr[0] = _mm_packus_epi16(B0, B1); - bgr[1] = _mm_packus_epi16(B2, B3); - bgr[2] = _mm_packus_epi16(G0, G1); - bgr[3] = _mm_packus_epi16(G2, G3); - bgr[4] = _mm_packus_epi16(R0, R1); - bgr[5] = _mm_packus_epi16(R2, R3); + bgr0 = _mm_packus_epi16(B0, B1); + bgr1 = _mm_packus_epi16(B2, B3); + bgr2 = _mm_packus_epi16(G0, G1); + bgr3 = _mm_packus_epi16(G2, G3); + bgr4 = _mm_packus_epi16(R0, R1); + bgr5 = _mm_packus_epi16(R2, R3); // Pack as BGRBGRBGRBGR. - PlanarTo24b(bgr, dst); + PlanarTo24b(&bgr0, &bgr1, &bgr2, &bgr3, &bgr4, &bgr5, dst); y += 32; u += 16; @@ -499,25 +477,19 @@ static WEBP_INLINE void RGB24PackedToPlanar(const uint8_t* const rgb, // Convert 8 packed ARGB to r[], g[], b[] static WEBP_INLINE void RGB32PackedToPlanar(const uint32_t* const argb, - __m128i* const r, - __m128i* const g, - __m128i* const b) { + __m128i* const rgb /*in[6]*/) { const __m128i zero = _mm_setzero_si128(); - const __m128i in0 = LOAD_16(argb + 0); // argb3 | argb2 | argb1 | argb0 - const __m128i in1 = LOAD_16(argb + 4); // argb7 | argb6 | argb5 | argb4 - // column-wise transpose - const __m128i A0 = _mm_unpacklo_epi8(in0, in1); - const __m128i A1 = _mm_unpackhi_epi8(in0, in1); - const __m128i B0 = _mm_unpacklo_epi8(A0, A1); - const __m128i B1 = _mm_unpackhi_epi8(A0, A1); - // C0 = g7 g6 ... g1 g0 | b7 b6 ... b1 b0 - // C1 = a7 a6 ... a1 a0 | r7 r6 ... r1 r0 - const __m128i C0 = _mm_unpacklo_epi8(B0, B1); - const __m128i C1 = _mm_unpackhi_epi8(B0, B1); - // store 16b - *r = _mm_unpacklo_epi8(C1, zero); - *g = _mm_unpackhi_epi8(C0, zero); - *b = _mm_unpacklo_epi8(C0, zero); + __m128i a0 = LOAD_16(argb + 0); + __m128i a1 = LOAD_16(argb + 4); + __m128i a2 = LOAD_16(argb + 8); + __m128i a3 = LOAD_16(argb + 12); + VP8L32bToPlanar(&a0, &a1, &a2, &a3); + rgb[0] = _mm_unpacklo_epi8(a1, zero); + rgb[1] = _mm_unpackhi_epi8(a1, zero); + rgb[2] = _mm_unpacklo_epi8(a2, zero); + rgb[3] = _mm_unpackhi_epi8(a2, zero); + rgb[4] = _mm_unpacklo_epi8(a3, zero); + rgb[5] = _mm_unpackhi_epi8(a3, zero); } // This macro computes (RG * MULT_RG + GB * MULT_GB + ROUNDER) >> DESCALE_FIX @@ -649,11 +621,10 @@ static void ConvertARGBToY(const uint32_t* argb, uint8_t* y, int width) { const int max_width = width & ~15; int i; for (i = 0; i < max_width; i += 16) { - __m128i r, g, b, Y0, Y1; - RGB32PackedToPlanar(&argb[i + 0], &r, &g, &b); - ConvertRGBToY(&r, &g, &b, &Y0); - RGB32PackedToPlanar(&argb[i + 8], &r, &g, &b); - ConvertRGBToY(&r, &g, &b, &Y1); + __m128i Y0, Y1, rgb[6]; + RGB32PackedToPlanar(&argb[i], rgb); + ConvertRGBToY(&rgb[0], &rgb[2], &rgb[4], &Y0); + ConvertRGBToY(&rgb[1], &rgb[3], &rgb[5], &Y1); STORE_16(_mm_packus_epi16(Y0, Y1), y + i); } for (; i < width; ++i) { // left-over @@ -678,20 +649,18 @@ static void ConvertARGBToUV(const uint32_t* argb, uint8_t* u, uint8_t* v, const int max_width = src_width & ~31; int i; for (i = 0; i < max_width; i += 32, u += 16, v += 16) { - __m128i r0, g0, b0, r1, g1, b1, U0, V0, U1, V1; - RGB32PackedToPlanar(&argb[i + 0], &r0, &g0, &b0); - RGB32PackedToPlanar(&argb[i + 8], &r1, &g1, &b1); - HorizontalAddPack(&r0, &r1, &r0); - HorizontalAddPack(&g0, &g1, &g0); - HorizontalAddPack(&b0, &b1, &b0); - ConvertRGBToUV(&r0, &g0, &b0, &U0, &V0); - - RGB32PackedToPlanar(&argb[i + 16], &r0, &g0, &b0); - RGB32PackedToPlanar(&argb[i + 24], &r1, &g1, &b1); - HorizontalAddPack(&r0, &r1, &r0); - HorizontalAddPack(&g0, &g1, &g0); - HorizontalAddPack(&b0, &b1, &b0); - ConvertRGBToUV(&r0, &g0, &b0, &U1, &V1); + __m128i rgb[6], U0, V0, U1, V1; + RGB32PackedToPlanar(&argb[i], rgb); + HorizontalAddPack(&rgb[0], &rgb[1], &rgb[0]); + HorizontalAddPack(&rgb[2], &rgb[3], &rgb[2]); + HorizontalAddPack(&rgb[4], &rgb[5], &rgb[4]); + ConvertRGBToUV(&rgb[0], &rgb[2], &rgb[4], &U0, &V0); + + RGB32PackedToPlanar(&argb[i + 16], rgb); + HorizontalAddPack(&rgb[0], &rgb[1], &rgb[0]); + HorizontalAddPack(&rgb[2], &rgb[3], &rgb[2]); + HorizontalAddPack(&rgb[4], &rgb[5], &rgb[4]); + ConvertRGBToUV(&rgb[0], &rgb[2], &rgb[4], &U1, &V1); U0 = _mm_packus_epi16(U0, U1); V0 = _mm_packus_epi16(V0, V1); @@ -767,9 +736,128 @@ WEBP_TSAN_IGNORE_FUNCTION void WebPInitConvertARGBToYUVSSE2(void) { WebPConvertRGBA32ToUV = ConvertRGBA32ToUV; } +//------------------------------------------------------------------------------ + +#define MAX_Y ((1 << 10) - 1) // 10b precision over 16b-arithmetic +static uint16_t clip_y(int v) { + return (v < 0) ? 0 : (v > MAX_Y) ? MAX_Y : (uint16_t)v; +} + +static uint64_t SharpYUVUpdateY_SSE2(const uint16_t* ref, const uint16_t* src, + uint16_t* dst, int len) { + uint64_t diff = 0; + uint32_t tmp[4]; + int i; + const __m128i zero = _mm_setzero_si128(); + const __m128i max = _mm_set1_epi16(MAX_Y); + const __m128i one = _mm_set1_epi16(1); + __m128i sum = zero; + + for (i = 0; i + 8 <= len; i += 8) { + const __m128i A = _mm_loadu_si128((const __m128i*)(ref + i)); + const __m128i B = _mm_loadu_si128((const __m128i*)(src + i)); + const __m128i C = _mm_loadu_si128((const __m128i*)(dst + i)); + const __m128i D = _mm_sub_epi16(A, B); // diff_y + const __m128i E = _mm_cmpgt_epi16(zero, D); // sign (-1 or 0) + const __m128i F = _mm_add_epi16(C, D); // new_y + const __m128i G = _mm_or_si128(E, one); // -1 or 1 + const __m128i H = _mm_max_epi16(_mm_min_epi16(F, max), zero); + const __m128i I = _mm_madd_epi16(D, G); // sum(abs(...)) + _mm_storeu_si128((__m128i*)(dst + i), H); + sum = _mm_add_epi32(sum, I); + } + _mm_storeu_si128((__m128i*)tmp, sum); + diff = tmp[3] + tmp[2] + tmp[1] + tmp[0]; + for (; i < len; ++i) { + const int diff_y = ref[i] - src[i]; + const int new_y = (int)dst[i] + diff_y; + dst[i] = clip_y(new_y); + diff += (uint64_t)abs(diff_y); + } + return diff; +} + +static void SharpYUVUpdateRGB_SSE2(const int16_t* ref, const int16_t* src, + int16_t* dst, int len) { + int i = 0; + for (i = 0; i + 8 <= len; i += 8) { + const __m128i A = _mm_loadu_si128((const __m128i*)(ref + i)); + const __m128i B = _mm_loadu_si128((const __m128i*)(src + i)); + const __m128i C = _mm_loadu_si128((const __m128i*)(dst + i)); + const __m128i D = _mm_sub_epi16(A, B); // diff_uv + const __m128i E = _mm_add_epi16(C, D); // new_uv + _mm_storeu_si128((__m128i*)(dst + i), E); + } + for (; i < len; ++i) { + const int diff_uv = ref[i] - src[i]; + dst[i] += diff_uv; + } +} + +static void SharpYUVFilterRow_SSE2(const int16_t* A, const int16_t* B, int len, + const uint16_t* best_y, uint16_t* out) { + int i; + const __m128i kCst8 = _mm_set1_epi16(8); + const __m128i max = _mm_set1_epi16(MAX_Y); + const __m128i zero = _mm_setzero_si128(); + for (i = 0; i + 8 <= len; i += 8) { + const __m128i a0 = _mm_loadu_si128((const __m128i*)(A + i + 0)); + const __m128i a1 = _mm_loadu_si128((const __m128i*)(A + i + 1)); + const __m128i b0 = _mm_loadu_si128((const __m128i*)(B + i + 0)); + const __m128i b1 = _mm_loadu_si128((const __m128i*)(B + i + 1)); + const __m128i a0b1 = _mm_add_epi16(a0, b1); + const __m128i a1b0 = _mm_add_epi16(a1, b0); + const __m128i a0a1b0b1 = _mm_add_epi16(a0b1, a1b0); // A0+A1+B0+B1 + const __m128i a0a1b0b1_8 = _mm_add_epi16(a0a1b0b1, kCst8); + const __m128i a0b1_2 = _mm_add_epi16(a0b1, a0b1); // 2*(A0+B1) + const __m128i a1b0_2 = _mm_add_epi16(a1b0, a1b0); // 2*(A1+B0) + const __m128i c0 = _mm_srai_epi16(_mm_add_epi16(a0b1_2, a0a1b0b1_8), 3); + const __m128i c1 = _mm_srai_epi16(_mm_add_epi16(a1b0_2, a0a1b0b1_8), 3); + const __m128i d0 = _mm_add_epi16(c1, a0); + const __m128i d1 = _mm_add_epi16(c0, a1); + const __m128i e0 = _mm_srai_epi16(d0, 1); + const __m128i e1 = _mm_srai_epi16(d1, 1); + const __m128i f0 = _mm_unpacklo_epi16(e0, e1); + const __m128i f1 = _mm_unpackhi_epi16(e0, e1); + const __m128i g0 = _mm_loadu_si128((const __m128i*)(best_y + 2 * i + 0)); + const __m128i g1 = _mm_loadu_si128((const __m128i*)(best_y + 2 * i + 8)); + const __m128i h0 = _mm_add_epi16(g0, f0); + const __m128i h1 = _mm_add_epi16(g1, f1); + const __m128i i0 = _mm_max_epi16(_mm_min_epi16(h0, max), zero); + const __m128i i1 = _mm_max_epi16(_mm_min_epi16(h1, max), zero); + _mm_storeu_si128((__m128i*)(out + 2 * i + 0), i0); + _mm_storeu_si128((__m128i*)(out + 2 * i + 8), i1); + } + for (; i < len; ++i) { + // (9 * A0 + 3 * A1 + 3 * B0 + B1 + 8) >> 4 = + // = (8 * A0 + 2 * (A1 + B0) + (A0 + A1 + B0 + B1 + 8)) >> 4 + // We reuse the common sub-expressions. + const int a0b1 = A[i + 0] + B[i + 1]; + const int a1b0 = A[i + 1] + B[i + 0]; + const int a0a1b0b1 = a0b1 + a1b0 + 8; + const int v0 = (8 * A[i + 0] + 2 * a1b0 + a0a1b0b1) >> 4; + const int v1 = (8 * A[i + 1] + 2 * a0b1 + a0a1b0b1) >> 4; + out[2 * i + 0] = clip_y(best_y[2 * i + 0] + v0); + out[2 * i + 1] = clip_y(best_y[2 * i + 1] + v1); + } +} + +#undef MAX_Y + +//------------------------------------------------------------------------------ + +extern void WebPInitSharpYUVSSE2(void); + +WEBP_TSAN_IGNORE_FUNCTION void WebPInitSharpYUVSSE2(void) { + WebPSharpYUVUpdateY = SharpYUVUpdateY_SSE2; + WebPSharpYUVUpdateRGB = SharpYUVUpdateRGB_SSE2; + WebPSharpYUVFilterRow = SharpYUVFilterRow_SSE2; +} + #else // !WEBP_USE_SSE2 WEBP_DSP_INIT_STUB(WebPInitSamplersSSE2) WEBP_DSP_INIT_STUB(WebPInitConvertARGBToYUVSSE2) +WEBP_DSP_INIT_STUB(WebPInitSharpYUVSSE2) #endif // WEBP_USE_SSE2 diff --git a/thirdparty/libwebp/dsp/yuv_tables_sse2.h b/thirdparty/libwebp/dsp/yuv_tables_sse2.h deleted file mode 100644 index 2b0f057518..0000000000 --- a/thirdparty/libwebp/dsp/yuv_tables_sse2.h +++ /dev/null @@ -1,536 +0,0 @@ -// Copyright 2014 Google Inc. All Rights Reserved. -// -// Use of this source code is governed by a BSD-style license -// that can be found in the COPYING file in the root of the source -// tree. An additional intellectual property rights grant can be found -// in the file PATENTS. All contributing project authors may -// be found in the AUTHORS file in the root of the source tree. -// ----------------------------------------------------------------------------- -// -// SSE2 tables for YUV->RGB conversion (12kB overall) -// -// Author: Skal (pascal.massimino@gmail.com) - -// This file is not compiled, but #include'd directly from yuv.c -// Only used if WEBP_YUV_USE_SSE2_TABLES is defined. - -static const VP8kCstSSE2 VP8kYtoRGBA[256] = { - {{0xfffb77b0, 0xfffb77b0, 0xfffb77b0, 0x003fc000}}, - {{0xfffbc235, 0xfffbc235, 0xfffbc235, 0x003fc000}}, - {{0xfffc0cba, 0xfffc0cba, 0xfffc0cba, 0x003fc000}}, - {{0xfffc573f, 0xfffc573f, 0xfffc573f, 0x003fc000}}, - {{0xfffca1c4, 0xfffca1c4, 0xfffca1c4, 0x003fc000}}, - {{0xfffcec49, 0xfffcec49, 0xfffcec49, 0x003fc000}}, - {{0xfffd36ce, 0xfffd36ce, 0xfffd36ce, 0x003fc000}}, - {{0xfffd8153, 0xfffd8153, 0xfffd8153, 0x003fc000}}, - {{0xfffdcbd8, 0xfffdcbd8, 0xfffdcbd8, 0x003fc000}}, - {{0xfffe165d, 0xfffe165d, 0xfffe165d, 0x003fc000}}, - {{0xfffe60e2, 0xfffe60e2, 0xfffe60e2, 0x003fc000}}, - {{0xfffeab67, 0xfffeab67, 0xfffeab67, 0x003fc000}}, - {{0xfffef5ec, 0xfffef5ec, 0xfffef5ec, 0x003fc000}}, - {{0xffff4071, 0xffff4071, 0xffff4071, 0x003fc000}}, - {{0xffff8af6, 0xffff8af6, 0xffff8af6, 0x003fc000}}, - {{0xffffd57b, 0xffffd57b, 0xffffd57b, 0x003fc000}}, - {{0x00002000, 0x00002000, 0x00002000, 0x003fc000}}, - {{0x00006a85, 0x00006a85, 0x00006a85, 0x003fc000}}, - {{0x0000b50a, 0x0000b50a, 0x0000b50a, 0x003fc000}}, - {{0x0000ff8f, 0x0000ff8f, 0x0000ff8f, 0x003fc000}}, - {{0x00014a14, 0x00014a14, 0x00014a14, 0x003fc000}}, - {{0x00019499, 0x00019499, 0x00019499, 0x003fc000}}, - {{0x0001df1e, 0x0001df1e, 0x0001df1e, 0x003fc000}}, - {{0x000229a3, 0x000229a3, 0x000229a3, 0x003fc000}}, - {{0x00027428, 0x00027428, 0x00027428, 0x003fc000}}, - {{0x0002bead, 0x0002bead, 0x0002bead, 0x003fc000}}, - {{0x00030932, 0x00030932, 0x00030932, 0x003fc000}}, - {{0x000353b7, 0x000353b7, 0x000353b7, 0x003fc000}}, - {{0x00039e3c, 0x00039e3c, 0x00039e3c, 0x003fc000}}, - {{0x0003e8c1, 0x0003e8c1, 0x0003e8c1, 0x003fc000}}, - {{0x00043346, 0x00043346, 0x00043346, 0x003fc000}}, - {{0x00047dcb, 0x00047dcb, 0x00047dcb, 0x003fc000}}, - {{0x0004c850, 0x0004c850, 0x0004c850, 0x003fc000}}, - {{0x000512d5, 0x000512d5, 0x000512d5, 0x003fc000}}, - {{0x00055d5a, 0x00055d5a, 0x00055d5a, 0x003fc000}}, - {{0x0005a7df, 0x0005a7df, 0x0005a7df, 0x003fc000}}, - {{0x0005f264, 0x0005f264, 0x0005f264, 0x003fc000}}, - {{0x00063ce9, 0x00063ce9, 0x00063ce9, 0x003fc000}}, - {{0x0006876e, 0x0006876e, 0x0006876e, 0x003fc000}}, - {{0x0006d1f3, 0x0006d1f3, 0x0006d1f3, 0x003fc000}}, - {{0x00071c78, 0x00071c78, 0x00071c78, 0x003fc000}}, - {{0x000766fd, 0x000766fd, 0x000766fd, 0x003fc000}}, - {{0x0007b182, 0x0007b182, 0x0007b182, 0x003fc000}}, - {{0x0007fc07, 0x0007fc07, 0x0007fc07, 0x003fc000}}, - {{0x0008468c, 0x0008468c, 0x0008468c, 0x003fc000}}, - {{0x00089111, 0x00089111, 0x00089111, 0x003fc000}}, - {{0x0008db96, 0x0008db96, 0x0008db96, 0x003fc000}}, - {{0x0009261b, 0x0009261b, 0x0009261b, 0x003fc000}}, - {{0x000970a0, 0x000970a0, 0x000970a0, 0x003fc000}}, - {{0x0009bb25, 0x0009bb25, 0x0009bb25, 0x003fc000}}, - {{0x000a05aa, 0x000a05aa, 0x000a05aa, 0x003fc000}}, - {{0x000a502f, 0x000a502f, 0x000a502f, 0x003fc000}}, - {{0x000a9ab4, 0x000a9ab4, 0x000a9ab4, 0x003fc000}}, - {{0x000ae539, 0x000ae539, 0x000ae539, 0x003fc000}}, - {{0x000b2fbe, 0x000b2fbe, 0x000b2fbe, 0x003fc000}}, - {{0x000b7a43, 0x000b7a43, 0x000b7a43, 0x003fc000}}, - {{0x000bc4c8, 0x000bc4c8, 0x000bc4c8, 0x003fc000}}, - {{0x000c0f4d, 0x000c0f4d, 0x000c0f4d, 0x003fc000}}, - {{0x000c59d2, 0x000c59d2, 0x000c59d2, 0x003fc000}}, - {{0x000ca457, 0x000ca457, 0x000ca457, 0x003fc000}}, - {{0x000ceedc, 0x000ceedc, 0x000ceedc, 0x003fc000}}, - {{0x000d3961, 0x000d3961, 0x000d3961, 0x003fc000}}, - {{0x000d83e6, 0x000d83e6, 0x000d83e6, 0x003fc000}}, - {{0x000dce6b, 0x000dce6b, 0x000dce6b, 0x003fc000}}, - {{0x000e18f0, 0x000e18f0, 0x000e18f0, 0x003fc000}}, - {{0x000e6375, 0x000e6375, 0x000e6375, 0x003fc000}}, - {{0x000eadfa, 0x000eadfa, 0x000eadfa, 0x003fc000}}, - {{0x000ef87f, 0x000ef87f, 0x000ef87f, 0x003fc000}}, - {{0x000f4304, 0x000f4304, 0x000f4304, 0x003fc000}}, - {{0x000f8d89, 0x000f8d89, 0x000f8d89, 0x003fc000}}, - {{0x000fd80e, 0x000fd80e, 0x000fd80e, 0x003fc000}}, - {{0x00102293, 0x00102293, 0x00102293, 0x003fc000}}, - {{0x00106d18, 0x00106d18, 0x00106d18, 0x003fc000}}, - {{0x0010b79d, 0x0010b79d, 0x0010b79d, 0x003fc000}}, - {{0x00110222, 0x00110222, 0x00110222, 0x003fc000}}, - {{0x00114ca7, 0x00114ca7, 0x00114ca7, 0x003fc000}}, - {{0x0011972c, 0x0011972c, 0x0011972c, 0x003fc000}}, - {{0x0011e1b1, 0x0011e1b1, 0x0011e1b1, 0x003fc000}}, - {{0x00122c36, 0x00122c36, 0x00122c36, 0x003fc000}}, - {{0x001276bb, 0x001276bb, 0x001276bb, 0x003fc000}}, - {{0x0012c140, 0x0012c140, 0x0012c140, 0x003fc000}}, - {{0x00130bc5, 0x00130bc5, 0x00130bc5, 0x003fc000}}, - {{0x0013564a, 0x0013564a, 0x0013564a, 0x003fc000}}, - {{0x0013a0cf, 0x0013a0cf, 0x0013a0cf, 0x003fc000}}, - {{0x0013eb54, 0x0013eb54, 0x0013eb54, 0x003fc000}}, - {{0x001435d9, 0x001435d9, 0x001435d9, 0x003fc000}}, - {{0x0014805e, 0x0014805e, 0x0014805e, 0x003fc000}}, - {{0x0014cae3, 0x0014cae3, 0x0014cae3, 0x003fc000}}, - {{0x00151568, 0x00151568, 0x00151568, 0x003fc000}}, - {{0x00155fed, 0x00155fed, 0x00155fed, 0x003fc000}}, - {{0x0015aa72, 0x0015aa72, 0x0015aa72, 0x003fc000}}, - {{0x0015f4f7, 0x0015f4f7, 0x0015f4f7, 0x003fc000}}, - {{0x00163f7c, 0x00163f7c, 0x00163f7c, 0x003fc000}}, - {{0x00168a01, 0x00168a01, 0x00168a01, 0x003fc000}}, - {{0x0016d486, 0x0016d486, 0x0016d486, 0x003fc000}}, - {{0x00171f0b, 0x00171f0b, 0x00171f0b, 0x003fc000}}, - {{0x00176990, 0x00176990, 0x00176990, 0x003fc000}}, - {{0x0017b415, 0x0017b415, 0x0017b415, 0x003fc000}}, - {{0x0017fe9a, 0x0017fe9a, 0x0017fe9a, 0x003fc000}}, - {{0x0018491f, 0x0018491f, 0x0018491f, 0x003fc000}}, - {{0x001893a4, 0x001893a4, 0x001893a4, 0x003fc000}}, - {{0x0018de29, 0x0018de29, 0x0018de29, 0x003fc000}}, - {{0x001928ae, 0x001928ae, 0x001928ae, 0x003fc000}}, - {{0x00197333, 0x00197333, 0x00197333, 0x003fc000}}, - {{0x0019bdb8, 0x0019bdb8, 0x0019bdb8, 0x003fc000}}, - {{0x001a083d, 0x001a083d, 0x001a083d, 0x003fc000}}, - {{0x001a52c2, 0x001a52c2, 0x001a52c2, 0x003fc000}}, - {{0x001a9d47, 0x001a9d47, 0x001a9d47, 0x003fc000}}, - {{0x001ae7cc, 0x001ae7cc, 0x001ae7cc, 0x003fc000}}, - {{0x001b3251, 0x001b3251, 0x001b3251, 0x003fc000}}, - {{0x001b7cd6, 0x001b7cd6, 0x001b7cd6, 0x003fc000}}, - {{0x001bc75b, 0x001bc75b, 0x001bc75b, 0x003fc000}}, - {{0x001c11e0, 0x001c11e0, 0x001c11e0, 0x003fc000}}, - {{0x001c5c65, 0x001c5c65, 0x001c5c65, 0x003fc000}}, - {{0x001ca6ea, 0x001ca6ea, 0x001ca6ea, 0x003fc000}}, - {{0x001cf16f, 0x001cf16f, 0x001cf16f, 0x003fc000}}, - {{0x001d3bf4, 0x001d3bf4, 0x001d3bf4, 0x003fc000}}, - {{0x001d8679, 0x001d8679, 0x001d8679, 0x003fc000}}, - {{0x001dd0fe, 0x001dd0fe, 0x001dd0fe, 0x003fc000}}, - {{0x001e1b83, 0x001e1b83, 0x001e1b83, 0x003fc000}}, - {{0x001e6608, 0x001e6608, 0x001e6608, 0x003fc000}}, - {{0x001eb08d, 0x001eb08d, 0x001eb08d, 0x003fc000}}, - {{0x001efb12, 0x001efb12, 0x001efb12, 0x003fc000}}, - {{0x001f4597, 0x001f4597, 0x001f4597, 0x003fc000}}, - {{0x001f901c, 0x001f901c, 0x001f901c, 0x003fc000}}, - {{0x001fdaa1, 0x001fdaa1, 0x001fdaa1, 0x003fc000}}, - {{0x00202526, 0x00202526, 0x00202526, 0x003fc000}}, - {{0x00206fab, 0x00206fab, 0x00206fab, 0x003fc000}}, - {{0x0020ba30, 0x0020ba30, 0x0020ba30, 0x003fc000}}, - {{0x002104b5, 0x002104b5, 0x002104b5, 0x003fc000}}, - {{0x00214f3a, 0x00214f3a, 0x00214f3a, 0x003fc000}}, - {{0x002199bf, 0x002199bf, 0x002199bf, 0x003fc000}}, - {{0x0021e444, 0x0021e444, 0x0021e444, 0x003fc000}}, - {{0x00222ec9, 0x00222ec9, 0x00222ec9, 0x003fc000}}, - {{0x0022794e, 0x0022794e, 0x0022794e, 0x003fc000}}, - {{0x0022c3d3, 0x0022c3d3, 0x0022c3d3, 0x003fc000}}, - {{0x00230e58, 0x00230e58, 0x00230e58, 0x003fc000}}, - {{0x002358dd, 0x002358dd, 0x002358dd, 0x003fc000}}, - {{0x0023a362, 0x0023a362, 0x0023a362, 0x003fc000}}, - {{0x0023ede7, 0x0023ede7, 0x0023ede7, 0x003fc000}}, - {{0x0024386c, 0x0024386c, 0x0024386c, 0x003fc000}}, - {{0x002482f1, 0x002482f1, 0x002482f1, 0x003fc000}}, - {{0x0024cd76, 0x0024cd76, 0x0024cd76, 0x003fc000}}, - {{0x002517fb, 0x002517fb, 0x002517fb, 0x003fc000}}, - {{0x00256280, 0x00256280, 0x00256280, 0x003fc000}}, - {{0x0025ad05, 0x0025ad05, 0x0025ad05, 0x003fc000}}, - {{0x0025f78a, 0x0025f78a, 0x0025f78a, 0x003fc000}}, - {{0x0026420f, 0x0026420f, 0x0026420f, 0x003fc000}}, - {{0x00268c94, 0x00268c94, 0x00268c94, 0x003fc000}}, - {{0x0026d719, 0x0026d719, 0x0026d719, 0x003fc000}}, - {{0x0027219e, 0x0027219e, 0x0027219e, 0x003fc000}}, - {{0x00276c23, 0x00276c23, 0x00276c23, 0x003fc000}}, - {{0x0027b6a8, 0x0027b6a8, 0x0027b6a8, 0x003fc000}}, - {{0x0028012d, 0x0028012d, 0x0028012d, 0x003fc000}}, - {{0x00284bb2, 0x00284bb2, 0x00284bb2, 0x003fc000}}, - {{0x00289637, 0x00289637, 0x00289637, 0x003fc000}}, - {{0x0028e0bc, 0x0028e0bc, 0x0028e0bc, 0x003fc000}}, - {{0x00292b41, 0x00292b41, 0x00292b41, 0x003fc000}}, - {{0x002975c6, 0x002975c6, 0x002975c6, 0x003fc000}}, - {{0x0029c04b, 0x0029c04b, 0x0029c04b, 0x003fc000}}, - {{0x002a0ad0, 0x002a0ad0, 0x002a0ad0, 0x003fc000}}, - {{0x002a5555, 0x002a5555, 0x002a5555, 0x003fc000}}, - {{0x002a9fda, 0x002a9fda, 0x002a9fda, 0x003fc000}}, - {{0x002aea5f, 0x002aea5f, 0x002aea5f, 0x003fc000}}, - {{0x002b34e4, 0x002b34e4, 0x002b34e4, 0x003fc000}}, - {{0x002b7f69, 0x002b7f69, 0x002b7f69, 0x003fc000}}, - {{0x002bc9ee, 0x002bc9ee, 0x002bc9ee, 0x003fc000}}, - {{0x002c1473, 0x002c1473, 0x002c1473, 0x003fc000}}, - {{0x002c5ef8, 0x002c5ef8, 0x002c5ef8, 0x003fc000}}, - {{0x002ca97d, 0x002ca97d, 0x002ca97d, 0x003fc000}}, - {{0x002cf402, 0x002cf402, 0x002cf402, 0x003fc000}}, - {{0x002d3e87, 0x002d3e87, 0x002d3e87, 0x003fc000}}, - {{0x002d890c, 0x002d890c, 0x002d890c, 0x003fc000}}, - {{0x002dd391, 0x002dd391, 0x002dd391, 0x003fc000}}, - {{0x002e1e16, 0x002e1e16, 0x002e1e16, 0x003fc000}}, - {{0x002e689b, 0x002e689b, 0x002e689b, 0x003fc000}}, - {{0x002eb320, 0x002eb320, 0x002eb320, 0x003fc000}}, - {{0x002efda5, 0x002efda5, 0x002efda5, 0x003fc000}}, - {{0x002f482a, 0x002f482a, 0x002f482a, 0x003fc000}}, - {{0x002f92af, 0x002f92af, 0x002f92af, 0x003fc000}}, - {{0x002fdd34, 0x002fdd34, 0x002fdd34, 0x003fc000}}, - {{0x003027b9, 0x003027b9, 0x003027b9, 0x003fc000}}, - {{0x0030723e, 0x0030723e, 0x0030723e, 0x003fc000}}, - {{0x0030bcc3, 0x0030bcc3, 0x0030bcc3, 0x003fc000}}, - {{0x00310748, 0x00310748, 0x00310748, 0x003fc000}}, - {{0x003151cd, 0x003151cd, 0x003151cd, 0x003fc000}}, - {{0x00319c52, 0x00319c52, 0x00319c52, 0x003fc000}}, - {{0x0031e6d7, 0x0031e6d7, 0x0031e6d7, 0x003fc000}}, - {{0x0032315c, 0x0032315c, 0x0032315c, 0x003fc000}}, - {{0x00327be1, 0x00327be1, 0x00327be1, 0x003fc000}}, - {{0x0032c666, 0x0032c666, 0x0032c666, 0x003fc000}}, - {{0x003310eb, 0x003310eb, 0x003310eb, 0x003fc000}}, - {{0x00335b70, 0x00335b70, 0x00335b70, 0x003fc000}}, - {{0x0033a5f5, 0x0033a5f5, 0x0033a5f5, 0x003fc000}}, - {{0x0033f07a, 0x0033f07a, 0x0033f07a, 0x003fc000}}, - {{0x00343aff, 0x00343aff, 0x00343aff, 0x003fc000}}, - {{0x00348584, 0x00348584, 0x00348584, 0x003fc000}}, - {{0x0034d009, 0x0034d009, 0x0034d009, 0x003fc000}}, - {{0x00351a8e, 0x00351a8e, 0x00351a8e, 0x003fc000}}, - {{0x00356513, 0x00356513, 0x00356513, 0x003fc000}}, - {{0x0035af98, 0x0035af98, 0x0035af98, 0x003fc000}}, - {{0x0035fa1d, 0x0035fa1d, 0x0035fa1d, 0x003fc000}}, - {{0x003644a2, 0x003644a2, 0x003644a2, 0x003fc000}}, - {{0x00368f27, 0x00368f27, 0x00368f27, 0x003fc000}}, - {{0x0036d9ac, 0x0036d9ac, 0x0036d9ac, 0x003fc000}}, - {{0x00372431, 0x00372431, 0x00372431, 0x003fc000}}, - {{0x00376eb6, 0x00376eb6, 0x00376eb6, 0x003fc000}}, - {{0x0037b93b, 0x0037b93b, 0x0037b93b, 0x003fc000}}, - {{0x003803c0, 0x003803c0, 0x003803c0, 0x003fc000}}, - {{0x00384e45, 0x00384e45, 0x00384e45, 0x003fc000}}, - {{0x003898ca, 0x003898ca, 0x003898ca, 0x003fc000}}, - {{0x0038e34f, 0x0038e34f, 0x0038e34f, 0x003fc000}}, - {{0x00392dd4, 0x00392dd4, 0x00392dd4, 0x003fc000}}, - {{0x00397859, 0x00397859, 0x00397859, 0x003fc000}}, - {{0x0039c2de, 0x0039c2de, 0x0039c2de, 0x003fc000}}, - {{0x003a0d63, 0x003a0d63, 0x003a0d63, 0x003fc000}}, - {{0x003a57e8, 0x003a57e8, 0x003a57e8, 0x003fc000}}, - {{0x003aa26d, 0x003aa26d, 0x003aa26d, 0x003fc000}}, - {{0x003aecf2, 0x003aecf2, 0x003aecf2, 0x003fc000}}, - {{0x003b3777, 0x003b3777, 0x003b3777, 0x003fc000}}, - {{0x003b81fc, 0x003b81fc, 0x003b81fc, 0x003fc000}}, - {{0x003bcc81, 0x003bcc81, 0x003bcc81, 0x003fc000}}, - {{0x003c1706, 0x003c1706, 0x003c1706, 0x003fc000}}, - {{0x003c618b, 0x003c618b, 0x003c618b, 0x003fc000}}, - {{0x003cac10, 0x003cac10, 0x003cac10, 0x003fc000}}, - {{0x003cf695, 0x003cf695, 0x003cf695, 0x003fc000}}, - {{0x003d411a, 0x003d411a, 0x003d411a, 0x003fc000}}, - {{0x003d8b9f, 0x003d8b9f, 0x003d8b9f, 0x003fc000}}, - {{0x003dd624, 0x003dd624, 0x003dd624, 0x003fc000}}, - {{0x003e20a9, 0x003e20a9, 0x003e20a9, 0x003fc000}}, - {{0x003e6b2e, 0x003e6b2e, 0x003e6b2e, 0x003fc000}}, - {{0x003eb5b3, 0x003eb5b3, 0x003eb5b3, 0x003fc000}}, - {{0x003f0038, 0x003f0038, 0x003f0038, 0x003fc000}}, - {{0x003f4abd, 0x003f4abd, 0x003f4abd, 0x003fc000}}, - {{0x003f9542, 0x003f9542, 0x003f9542, 0x003fc000}}, - {{0x003fdfc7, 0x003fdfc7, 0x003fdfc7, 0x003fc000}}, - {{0x00402a4c, 0x00402a4c, 0x00402a4c, 0x003fc000}}, - {{0x004074d1, 0x004074d1, 0x004074d1, 0x003fc000}}, - {{0x0040bf56, 0x0040bf56, 0x0040bf56, 0x003fc000}}, - {{0x004109db, 0x004109db, 0x004109db, 0x003fc000}}, - {{0x00415460, 0x00415460, 0x00415460, 0x003fc000}}, - {{0x00419ee5, 0x00419ee5, 0x00419ee5, 0x003fc000}}, - {{0x0041e96a, 0x0041e96a, 0x0041e96a, 0x003fc000}}, - {{0x004233ef, 0x004233ef, 0x004233ef, 0x003fc000}}, - {{0x00427e74, 0x00427e74, 0x00427e74, 0x003fc000}}, - {{0x0042c8f9, 0x0042c8f9, 0x0042c8f9, 0x003fc000}}, - {{0x0043137e, 0x0043137e, 0x0043137e, 0x003fc000}}, - {{0x00435e03, 0x00435e03, 0x00435e03, 0x003fc000}}, - {{0x0043a888, 0x0043a888, 0x0043a888, 0x003fc000}}, - {{0x0043f30d, 0x0043f30d, 0x0043f30d, 0x003fc000}}, - {{0x00443d92, 0x00443d92, 0x00443d92, 0x003fc000}}, - {{0x00448817, 0x00448817, 0x00448817, 0x003fc000}}, - {{0x0044d29c, 0x0044d29c, 0x0044d29c, 0x003fc000}}, - {{0x00451d21, 0x00451d21, 0x00451d21, 0x003fc000}}, - {{0x004567a6, 0x004567a6, 0x004567a6, 0x003fc000}}, - {{0x0045b22b, 0x0045b22b, 0x0045b22b, 0x003fc000}} -}; - -static const VP8kCstSSE2 VP8kUtoRGBA[256] = { - {{0, 0x000c8980, 0xffbf7300, 0}}, {{0, 0x000c706d, 0xffbff41a, 0}}, - {{0, 0x000c575a, 0xffc07534, 0}}, {{0, 0x000c3e47, 0xffc0f64e, 0}}, - {{0, 0x000c2534, 0xffc17768, 0}}, {{0, 0x000c0c21, 0xffc1f882, 0}}, - {{0, 0x000bf30e, 0xffc2799c, 0}}, {{0, 0x000bd9fb, 0xffc2fab6, 0}}, - {{0, 0x000bc0e8, 0xffc37bd0, 0}}, {{0, 0x000ba7d5, 0xffc3fcea, 0}}, - {{0, 0x000b8ec2, 0xffc47e04, 0}}, {{0, 0x000b75af, 0xffc4ff1e, 0}}, - {{0, 0x000b5c9c, 0xffc58038, 0}}, {{0, 0x000b4389, 0xffc60152, 0}}, - {{0, 0x000b2a76, 0xffc6826c, 0}}, {{0, 0x000b1163, 0xffc70386, 0}}, - {{0, 0x000af850, 0xffc784a0, 0}}, {{0, 0x000adf3d, 0xffc805ba, 0}}, - {{0, 0x000ac62a, 0xffc886d4, 0}}, {{0, 0x000aad17, 0xffc907ee, 0}}, - {{0, 0x000a9404, 0xffc98908, 0}}, {{0, 0x000a7af1, 0xffca0a22, 0}}, - {{0, 0x000a61de, 0xffca8b3c, 0}}, {{0, 0x000a48cb, 0xffcb0c56, 0}}, - {{0, 0x000a2fb8, 0xffcb8d70, 0}}, {{0, 0x000a16a5, 0xffcc0e8a, 0}}, - {{0, 0x0009fd92, 0xffcc8fa4, 0}}, {{0, 0x0009e47f, 0xffcd10be, 0}}, - {{0, 0x0009cb6c, 0xffcd91d8, 0}}, {{0, 0x0009b259, 0xffce12f2, 0}}, - {{0, 0x00099946, 0xffce940c, 0}}, {{0, 0x00098033, 0xffcf1526, 0}}, - {{0, 0x00096720, 0xffcf9640, 0}}, {{0, 0x00094e0d, 0xffd0175a, 0}}, - {{0, 0x000934fa, 0xffd09874, 0}}, {{0, 0x00091be7, 0xffd1198e, 0}}, - {{0, 0x000902d4, 0xffd19aa8, 0}}, {{0, 0x0008e9c1, 0xffd21bc2, 0}}, - {{0, 0x0008d0ae, 0xffd29cdc, 0}}, {{0, 0x0008b79b, 0xffd31df6, 0}}, - {{0, 0x00089e88, 0xffd39f10, 0}}, {{0, 0x00088575, 0xffd4202a, 0}}, - {{0, 0x00086c62, 0xffd4a144, 0}}, {{0, 0x0008534f, 0xffd5225e, 0}}, - {{0, 0x00083a3c, 0xffd5a378, 0}}, {{0, 0x00082129, 0xffd62492, 0}}, - {{0, 0x00080816, 0xffd6a5ac, 0}}, {{0, 0x0007ef03, 0xffd726c6, 0}}, - {{0, 0x0007d5f0, 0xffd7a7e0, 0}}, {{0, 0x0007bcdd, 0xffd828fa, 0}}, - {{0, 0x0007a3ca, 0xffd8aa14, 0}}, {{0, 0x00078ab7, 0xffd92b2e, 0}}, - {{0, 0x000771a4, 0xffd9ac48, 0}}, {{0, 0x00075891, 0xffda2d62, 0}}, - {{0, 0x00073f7e, 0xffdaae7c, 0}}, {{0, 0x0007266b, 0xffdb2f96, 0}}, - {{0, 0x00070d58, 0xffdbb0b0, 0}}, {{0, 0x0006f445, 0xffdc31ca, 0}}, - {{0, 0x0006db32, 0xffdcb2e4, 0}}, {{0, 0x0006c21f, 0xffdd33fe, 0}}, - {{0, 0x0006a90c, 0xffddb518, 0}}, {{0, 0x00068ff9, 0xffde3632, 0}}, - {{0, 0x000676e6, 0xffdeb74c, 0}}, {{0, 0x00065dd3, 0xffdf3866, 0}}, - {{0, 0x000644c0, 0xffdfb980, 0}}, {{0, 0x00062bad, 0xffe03a9a, 0}}, - {{0, 0x0006129a, 0xffe0bbb4, 0}}, {{0, 0x0005f987, 0xffe13cce, 0}}, - {{0, 0x0005e074, 0xffe1bde8, 0}}, {{0, 0x0005c761, 0xffe23f02, 0}}, - {{0, 0x0005ae4e, 0xffe2c01c, 0}}, {{0, 0x0005953b, 0xffe34136, 0}}, - {{0, 0x00057c28, 0xffe3c250, 0}}, {{0, 0x00056315, 0xffe4436a, 0}}, - {{0, 0x00054a02, 0xffe4c484, 0}}, {{0, 0x000530ef, 0xffe5459e, 0}}, - {{0, 0x000517dc, 0xffe5c6b8, 0}}, {{0, 0x0004fec9, 0xffe647d2, 0}}, - {{0, 0x0004e5b6, 0xffe6c8ec, 0}}, {{0, 0x0004cca3, 0xffe74a06, 0}}, - {{0, 0x0004b390, 0xffe7cb20, 0}}, {{0, 0x00049a7d, 0xffe84c3a, 0}}, - {{0, 0x0004816a, 0xffe8cd54, 0}}, {{0, 0x00046857, 0xffe94e6e, 0}}, - {{0, 0x00044f44, 0xffe9cf88, 0}}, {{0, 0x00043631, 0xffea50a2, 0}}, - {{0, 0x00041d1e, 0xffead1bc, 0}}, {{0, 0x0004040b, 0xffeb52d6, 0}}, - {{0, 0x0003eaf8, 0xffebd3f0, 0}}, {{0, 0x0003d1e5, 0xffec550a, 0}}, - {{0, 0x0003b8d2, 0xffecd624, 0}}, {{0, 0x00039fbf, 0xffed573e, 0}}, - {{0, 0x000386ac, 0xffedd858, 0}}, {{0, 0x00036d99, 0xffee5972, 0}}, - {{0, 0x00035486, 0xffeeda8c, 0}}, {{0, 0x00033b73, 0xffef5ba6, 0}}, - {{0, 0x00032260, 0xffefdcc0, 0}}, {{0, 0x0003094d, 0xfff05dda, 0}}, - {{0, 0x0002f03a, 0xfff0def4, 0}}, {{0, 0x0002d727, 0xfff1600e, 0}}, - {{0, 0x0002be14, 0xfff1e128, 0}}, {{0, 0x0002a501, 0xfff26242, 0}}, - {{0, 0x00028bee, 0xfff2e35c, 0}}, {{0, 0x000272db, 0xfff36476, 0}}, - {{0, 0x000259c8, 0xfff3e590, 0}}, {{0, 0x000240b5, 0xfff466aa, 0}}, - {{0, 0x000227a2, 0xfff4e7c4, 0}}, {{0, 0x00020e8f, 0xfff568de, 0}}, - {{0, 0x0001f57c, 0xfff5e9f8, 0}}, {{0, 0x0001dc69, 0xfff66b12, 0}}, - {{0, 0x0001c356, 0xfff6ec2c, 0}}, {{0, 0x0001aa43, 0xfff76d46, 0}}, - {{0, 0x00019130, 0xfff7ee60, 0}}, {{0, 0x0001781d, 0xfff86f7a, 0}}, - {{0, 0x00015f0a, 0xfff8f094, 0}}, {{0, 0x000145f7, 0xfff971ae, 0}}, - {{0, 0x00012ce4, 0xfff9f2c8, 0}}, {{0, 0x000113d1, 0xfffa73e2, 0}}, - {{0, 0x0000fabe, 0xfffaf4fc, 0}}, {{0, 0x0000e1ab, 0xfffb7616, 0}}, - {{0, 0x0000c898, 0xfffbf730, 0}}, {{0, 0x0000af85, 0xfffc784a, 0}}, - {{0, 0x00009672, 0xfffcf964, 0}}, {{0, 0x00007d5f, 0xfffd7a7e, 0}}, - {{0, 0x0000644c, 0xfffdfb98, 0}}, {{0, 0x00004b39, 0xfffe7cb2, 0}}, - {{0, 0x00003226, 0xfffefdcc, 0}}, {{0, 0x00001913, 0xffff7ee6, 0}}, - {{0, 0x00000000, 0x00000000, 0}}, {{0, 0xffffe6ed, 0x0000811a, 0}}, - {{0, 0xffffcdda, 0x00010234, 0}}, {{0, 0xffffb4c7, 0x0001834e, 0}}, - {{0, 0xffff9bb4, 0x00020468, 0}}, {{0, 0xffff82a1, 0x00028582, 0}}, - {{0, 0xffff698e, 0x0003069c, 0}}, {{0, 0xffff507b, 0x000387b6, 0}}, - {{0, 0xffff3768, 0x000408d0, 0}}, {{0, 0xffff1e55, 0x000489ea, 0}}, - {{0, 0xffff0542, 0x00050b04, 0}}, {{0, 0xfffeec2f, 0x00058c1e, 0}}, - {{0, 0xfffed31c, 0x00060d38, 0}}, {{0, 0xfffeba09, 0x00068e52, 0}}, - {{0, 0xfffea0f6, 0x00070f6c, 0}}, {{0, 0xfffe87e3, 0x00079086, 0}}, - {{0, 0xfffe6ed0, 0x000811a0, 0}}, {{0, 0xfffe55bd, 0x000892ba, 0}}, - {{0, 0xfffe3caa, 0x000913d4, 0}}, {{0, 0xfffe2397, 0x000994ee, 0}}, - {{0, 0xfffe0a84, 0x000a1608, 0}}, {{0, 0xfffdf171, 0x000a9722, 0}}, - {{0, 0xfffdd85e, 0x000b183c, 0}}, {{0, 0xfffdbf4b, 0x000b9956, 0}}, - {{0, 0xfffda638, 0x000c1a70, 0}}, {{0, 0xfffd8d25, 0x000c9b8a, 0}}, - {{0, 0xfffd7412, 0x000d1ca4, 0}}, {{0, 0xfffd5aff, 0x000d9dbe, 0}}, - {{0, 0xfffd41ec, 0x000e1ed8, 0}}, {{0, 0xfffd28d9, 0x000e9ff2, 0}}, - {{0, 0xfffd0fc6, 0x000f210c, 0}}, {{0, 0xfffcf6b3, 0x000fa226, 0}}, - {{0, 0xfffcdda0, 0x00102340, 0}}, {{0, 0xfffcc48d, 0x0010a45a, 0}}, - {{0, 0xfffcab7a, 0x00112574, 0}}, {{0, 0xfffc9267, 0x0011a68e, 0}}, - {{0, 0xfffc7954, 0x001227a8, 0}}, {{0, 0xfffc6041, 0x0012a8c2, 0}}, - {{0, 0xfffc472e, 0x001329dc, 0}}, {{0, 0xfffc2e1b, 0x0013aaf6, 0}}, - {{0, 0xfffc1508, 0x00142c10, 0}}, {{0, 0xfffbfbf5, 0x0014ad2a, 0}}, - {{0, 0xfffbe2e2, 0x00152e44, 0}}, {{0, 0xfffbc9cf, 0x0015af5e, 0}}, - {{0, 0xfffbb0bc, 0x00163078, 0}}, {{0, 0xfffb97a9, 0x0016b192, 0}}, - {{0, 0xfffb7e96, 0x001732ac, 0}}, {{0, 0xfffb6583, 0x0017b3c6, 0}}, - {{0, 0xfffb4c70, 0x001834e0, 0}}, {{0, 0xfffb335d, 0x0018b5fa, 0}}, - {{0, 0xfffb1a4a, 0x00193714, 0}}, {{0, 0xfffb0137, 0x0019b82e, 0}}, - {{0, 0xfffae824, 0x001a3948, 0}}, {{0, 0xfffacf11, 0x001aba62, 0}}, - {{0, 0xfffab5fe, 0x001b3b7c, 0}}, {{0, 0xfffa9ceb, 0x001bbc96, 0}}, - {{0, 0xfffa83d8, 0x001c3db0, 0}}, {{0, 0xfffa6ac5, 0x001cbeca, 0}}, - {{0, 0xfffa51b2, 0x001d3fe4, 0}}, {{0, 0xfffa389f, 0x001dc0fe, 0}}, - {{0, 0xfffa1f8c, 0x001e4218, 0}}, {{0, 0xfffa0679, 0x001ec332, 0}}, - {{0, 0xfff9ed66, 0x001f444c, 0}}, {{0, 0xfff9d453, 0x001fc566, 0}}, - {{0, 0xfff9bb40, 0x00204680, 0}}, {{0, 0xfff9a22d, 0x0020c79a, 0}}, - {{0, 0xfff9891a, 0x002148b4, 0}}, {{0, 0xfff97007, 0x0021c9ce, 0}}, - {{0, 0xfff956f4, 0x00224ae8, 0}}, {{0, 0xfff93de1, 0x0022cc02, 0}}, - {{0, 0xfff924ce, 0x00234d1c, 0}}, {{0, 0xfff90bbb, 0x0023ce36, 0}}, - {{0, 0xfff8f2a8, 0x00244f50, 0}}, {{0, 0xfff8d995, 0x0024d06a, 0}}, - {{0, 0xfff8c082, 0x00255184, 0}}, {{0, 0xfff8a76f, 0x0025d29e, 0}}, - {{0, 0xfff88e5c, 0x002653b8, 0}}, {{0, 0xfff87549, 0x0026d4d2, 0}}, - {{0, 0xfff85c36, 0x002755ec, 0}}, {{0, 0xfff84323, 0x0027d706, 0}}, - {{0, 0xfff82a10, 0x00285820, 0}}, {{0, 0xfff810fd, 0x0028d93a, 0}}, - {{0, 0xfff7f7ea, 0x00295a54, 0}}, {{0, 0xfff7ded7, 0x0029db6e, 0}}, - {{0, 0xfff7c5c4, 0x002a5c88, 0}}, {{0, 0xfff7acb1, 0x002adda2, 0}}, - {{0, 0xfff7939e, 0x002b5ebc, 0}}, {{0, 0xfff77a8b, 0x002bdfd6, 0}}, - {{0, 0xfff76178, 0x002c60f0, 0}}, {{0, 0xfff74865, 0x002ce20a, 0}}, - {{0, 0xfff72f52, 0x002d6324, 0}}, {{0, 0xfff7163f, 0x002de43e, 0}}, - {{0, 0xfff6fd2c, 0x002e6558, 0}}, {{0, 0xfff6e419, 0x002ee672, 0}}, - {{0, 0xfff6cb06, 0x002f678c, 0}}, {{0, 0xfff6b1f3, 0x002fe8a6, 0}}, - {{0, 0xfff698e0, 0x003069c0, 0}}, {{0, 0xfff67fcd, 0x0030eada, 0}}, - {{0, 0xfff666ba, 0x00316bf4, 0}}, {{0, 0xfff64da7, 0x0031ed0e, 0}}, - {{0, 0xfff63494, 0x00326e28, 0}}, {{0, 0xfff61b81, 0x0032ef42, 0}}, - {{0, 0xfff6026e, 0x0033705c, 0}}, {{0, 0xfff5e95b, 0x0033f176, 0}}, - {{0, 0xfff5d048, 0x00347290, 0}}, {{0, 0xfff5b735, 0x0034f3aa, 0}}, - {{0, 0xfff59e22, 0x003574c4, 0}}, {{0, 0xfff5850f, 0x0035f5de, 0}}, - {{0, 0xfff56bfc, 0x003676f8, 0}}, {{0, 0xfff552e9, 0x0036f812, 0}}, - {{0, 0xfff539d6, 0x0037792c, 0}}, {{0, 0xfff520c3, 0x0037fa46, 0}}, - {{0, 0xfff507b0, 0x00387b60, 0}}, {{0, 0xfff4ee9d, 0x0038fc7a, 0}}, - {{0, 0xfff4d58a, 0x00397d94, 0}}, {{0, 0xfff4bc77, 0x0039feae, 0}}, - {{0, 0xfff4a364, 0x003a7fc8, 0}}, {{0, 0xfff48a51, 0x003b00e2, 0}}, - {{0, 0xfff4713e, 0x003b81fc, 0}}, {{0, 0xfff4582b, 0x003c0316, 0}}, - {{0, 0xfff43f18, 0x003c8430, 0}}, {{0, 0xfff42605, 0x003d054a, 0}}, - {{0, 0xfff40cf2, 0x003d8664, 0}}, {{0, 0xfff3f3df, 0x003e077e, 0}}, - {{0, 0xfff3dacc, 0x003e8898, 0}}, {{0, 0xfff3c1b9, 0x003f09b2, 0}}, - {{0, 0xfff3a8a6, 0x003f8acc, 0}}, {{0, 0xfff38f93, 0x00400be6, 0}} -}; - -static VP8kCstSSE2 VP8kVtoRGBA[256] = { - {{0xffcced80, 0x001a0400, 0, 0}}, {{0xffcd53a5, 0x0019cff8, 0, 0}}, - {{0xffcdb9ca, 0x00199bf0, 0, 0}}, {{0xffce1fef, 0x001967e8, 0, 0}}, - {{0xffce8614, 0x001933e0, 0, 0}}, {{0xffceec39, 0x0018ffd8, 0, 0}}, - {{0xffcf525e, 0x0018cbd0, 0, 0}}, {{0xffcfb883, 0x001897c8, 0, 0}}, - {{0xffd01ea8, 0x001863c0, 0, 0}}, {{0xffd084cd, 0x00182fb8, 0, 0}}, - {{0xffd0eaf2, 0x0017fbb0, 0, 0}}, {{0xffd15117, 0x0017c7a8, 0, 0}}, - {{0xffd1b73c, 0x001793a0, 0, 0}}, {{0xffd21d61, 0x00175f98, 0, 0}}, - {{0xffd28386, 0x00172b90, 0, 0}}, {{0xffd2e9ab, 0x0016f788, 0, 0}}, - {{0xffd34fd0, 0x0016c380, 0, 0}}, {{0xffd3b5f5, 0x00168f78, 0, 0}}, - {{0xffd41c1a, 0x00165b70, 0, 0}}, {{0xffd4823f, 0x00162768, 0, 0}}, - {{0xffd4e864, 0x0015f360, 0, 0}}, {{0xffd54e89, 0x0015bf58, 0, 0}}, - {{0xffd5b4ae, 0x00158b50, 0, 0}}, {{0xffd61ad3, 0x00155748, 0, 0}}, - {{0xffd680f8, 0x00152340, 0, 0}}, {{0xffd6e71d, 0x0014ef38, 0, 0}}, - {{0xffd74d42, 0x0014bb30, 0, 0}}, {{0xffd7b367, 0x00148728, 0, 0}}, - {{0xffd8198c, 0x00145320, 0, 0}}, {{0xffd87fb1, 0x00141f18, 0, 0}}, - {{0xffd8e5d6, 0x0013eb10, 0, 0}}, {{0xffd94bfb, 0x0013b708, 0, 0}}, - {{0xffd9b220, 0x00138300, 0, 0}}, {{0xffda1845, 0x00134ef8, 0, 0}}, - {{0xffda7e6a, 0x00131af0, 0, 0}}, {{0xffdae48f, 0x0012e6e8, 0, 0}}, - {{0xffdb4ab4, 0x0012b2e0, 0, 0}}, {{0xffdbb0d9, 0x00127ed8, 0, 0}}, - {{0xffdc16fe, 0x00124ad0, 0, 0}}, {{0xffdc7d23, 0x001216c8, 0, 0}}, - {{0xffdce348, 0x0011e2c0, 0, 0}}, {{0xffdd496d, 0x0011aeb8, 0, 0}}, - {{0xffddaf92, 0x00117ab0, 0, 0}}, {{0xffde15b7, 0x001146a8, 0, 0}}, - {{0xffde7bdc, 0x001112a0, 0, 0}}, {{0xffdee201, 0x0010de98, 0, 0}}, - {{0xffdf4826, 0x0010aa90, 0, 0}}, {{0xffdfae4b, 0x00107688, 0, 0}}, - {{0xffe01470, 0x00104280, 0, 0}}, {{0xffe07a95, 0x00100e78, 0, 0}}, - {{0xffe0e0ba, 0x000fda70, 0, 0}}, {{0xffe146df, 0x000fa668, 0, 0}}, - {{0xffe1ad04, 0x000f7260, 0, 0}}, {{0xffe21329, 0x000f3e58, 0, 0}}, - {{0xffe2794e, 0x000f0a50, 0, 0}}, {{0xffe2df73, 0x000ed648, 0, 0}}, - {{0xffe34598, 0x000ea240, 0, 0}}, {{0xffe3abbd, 0x000e6e38, 0, 0}}, - {{0xffe411e2, 0x000e3a30, 0, 0}}, {{0xffe47807, 0x000e0628, 0, 0}}, - {{0xffe4de2c, 0x000dd220, 0, 0}}, {{0xffe54451, 0x000d9e18, 0, 0}}, - {{0xffe5aa76, 0x000d6a10, 0, 0}}, {{0xffe6109b, 0x000d3608, 0, 0}}, - {{0xffe676c0, 0x000d0200, 0, 0}}, {{0xffe6dce5, 0x000ccdf8, 0, 0}}, - {{0xffe7430a, 0x000c99f0, 0, 0}}, {{0xffe7a92f, 0x000c65e8, 0, 0}}, - {{0xffe80f54, 0x000c31e0, 0, 0}}, {{0xffe87579, 0x000bfdd8, 0, 0}}, - {{0xffe8db9e, 0x000bc9d0, 0, 0}}, {{0xffe941c3, 0x000b95c8, 0, 0}}, - {{0xffe9a7e8, 0x000b61c0, 0, 0}}, {{0xffea0e0d, 0x000b2db8, 0, 0}}, - {{0xffea7432, 0x000af9b0, 0, 0}}, {{0xffeada57, 0x000ac5a8, 0, 0}}, - {{0xffeb407c, 0x000a91a0, 0, 0}}, {{0xffeba6a1, 0x000a5d98, 0, 0}}, - {{0xffec0cc6, 0x000a2990, 0, 0}}, {{0xffec72eb, 0x0009f588, 0, 0}}, - {{0xffecd910, 0x0009c180, 0, 0}}, {{0xffed3f35, 0x00098d78, 0, 0}}, - {{0xffeda55a, 0x00095970, 0, 0}}, {{0xffee0b7f, 0x00092568, 0, 0}}, - {{0xffee71a4, 0x0008f160, 0, 0}}, {{0xffeed7c9, 0x0008bd58, 0, 0}}, - {{0xffef3dee, 0x00088950, 0, 0}}, {{0xffefa413, 0x00085548, 0, 0}}, - {{0xfff00a38, 0x00082140, 0, 0}}, {{0xfff0705d, 0x0007ed38, 0, 0}}, - {{0xfff0d682, 0x0007b930, 0, 0}}, {{0xfff13ca7, 0x00078528, 0, 0}}, - {{0xfff1a2cc, 0x00075120, 0, 0}}, {{0xfff208f1, 0x00071d18, 0, 0}}, - {{0xfff26f16, 0x0006e910, 0, 0}}, {{0xfff2d53b, 0x0006b508, 0, 0}}, - {{0xfff33b60, 0x00068100, 0, 0}}, {{0xfff3a185, 0x00064cf8, 0, 0}}, - {{0xfff407aa, 0x000618f0, 0, 0}}, {{0xfff46dcf, 0x0005e4e8, 0, 0}}, - {{0xfff4d3f4, 0x0005b0e0, 0, 0}}, {{0xfff53a19, 0x00057cd8, 0, 0}}, - {{0xfff5a03e, 0x000548d0, 0, 0}}, {{0xfff60663, 0x000514c8, 0, 0}}, - {{0xfff66c88, 0x0004e0c0, 0, 0}}, {{0xfff6d2ad, 0x0004acb8, 0, 0}}, - {{0xfff738d2, 0x000478b0, 0, 0}}, {{0xfff79ef7, 0x000444a8, 0, 0}}, - {{0xfff8051c, 0x000410a0, 0, 0}}, {{0xfff86b41, 0x0003dc98, 0, 0}}, - {{0xfff8d166, 0x0003a890, 0, 0}}, {{0xfff9378b, 0x00037488, 0, 0}}, - {{0xfff99db0, 0x00034080, 0, 0}}, {{0xfffa03d5, 0x00030c78, 0, 0}}, - {{0xfffa69fa, 0x0002d870, 0, 0}}, {{0xfffad01f, 0x0002a468, 0, 0}}, - {{0xfffb3644, 0x00027060, 0, 0}}, {{0xfffb9c69, 0x00023c58, 0, 0}}, - {{0xfffc028e, 0x00020850, 0, 0}}, {{0xfffc68b3, 0x0001d448, 0, 0}}, - {{0xfffcced8, 0x0001a040, 0, 0}}, {{0xfffd34fd, 0x00016c38, 0, 0}}, - {{0xfffd9b22, 0x00013830, 0, 0}}, {{0xfffe0147, 0x00010428, 0, 0}}, - {{0xfffe676c, 0x0000d020, 0, 0}}, {{0xfffecd91, 0x00009c18, 0, 0}}, - {{0xffff33b6, 0x00006810, 0, 0}}, {{0xffff99db, 0x00003408, 0, 0}}, - {{0x00000000, 0x00000000, 0, 0}}, {{0x00006625, 0xffffcbf8, 0, 0}}, - {{0x0000cc4a, 0xffff97f0, 0, 0}}, {{0x0001326f, 0xffff63e8, 0, 0}}, - {{0x00019894, 0xffff2fe0, 0, 0}}, {{0x0001feb9, 0xfffefbd8, 0, 0}}, - {{0x000264de, 0xfffec7d0, 0, 0}}, {{0x0002cb03, 0xfffe93c8, 0, 0}}, - {{0x00033128, 0xfffe5fc0, 0, 0}}, {{0x0003974d, 0xfffe2bb8, 0, 0}}, - {{0x0003fd72, 0xfffdf7b0, 0, 0}}, {{0x00046397, 0xfffdc3a8, 0, 0}}, - {{0x0004c9bc, 0xfffd8fa0, 0, 0}}, {{0x00052fe1, 0xfffd5b98, 0, 0}}, - {{0x00059606, 0xfffd2790, 0, 0}}, {{0x0005fc2b, 0xfffcf388, 0, 0}}, - {{0x00066250, 0xfffcbf80, 0, 0}}, {{0x0006c875, 0xfffc8b78, 0, 0}}, - {{0x00072e9a, 0xfffc5770, 0, 0}}, {{0x000794bf, 0xfffc2368, 0, 0}}, - {{0x0007fae4, 0xfffbef60, 0, 0}}, {{0x00086109, 0xfffbbb58, 0, 0}}, - {{0x0008c72e, 0xfffb8750, 0, 0}}, {{0x00092d53, 0xfffb5348, 0, 0}}, - {{0x00099378, 0xfffb1f40, 0, 0}}, {{0x0009f99d, 0xfffaeb38, 0, 0}}, - {{0x000a5fc2, 0xfffab730, 0, 0}}, {{0x000ac5e7, 0xfffa8328, 0, 0}}, - {{0x000b2c0c, 0xfffa4f20, 0, 0}}, {{0x000b9231, 0xfffa1b18, 0, 0}}, - {{0x000bf856, 0xfff9e710, 0, 0}}, {{0x000c5e7b, 0xfff9b308, 0, 0}}, - {{0x000cc4a0, 0xfff97f00, 0, 0}}, {{0x000d2ac5, 0xfff94af8, 0, 0}}, - {{0x000d90ea, 0xfff916f0, 0, 0}}, {{0x000df70f, 0xfff8e2e8, 0, 0}}, - {{0x000e5d34, 0xfff8aee0, 0, 0}}, {{0x000ec359, 0xfff87ad8, 0, 0}}, - {{0x000f297e, 0xfff846d0, 0, 0}}, {{0x000f8fa3, 0xfff812c8, 0, 0}}, - {{0x000ff5c8, 0xfff7dec0, 0, 0}}, {{0x00105bed, 0xfff7aab8, 0, 0}}, - {{0x0010c212, 0xfff776b0, 0, 0}}, {{0x00112837, 0xfff742a8, 0, 0}}, - {{0x00118e5c, 0xfff70ea0, 0, 0}}, {{0x0011f481, 0xfff6da98, 0, 0}}, - {{0x00125aa6, 0xfff6a690, 0, 0}}, {{0x0012c0cb, 0xfff67288, 0, 0}}, - {{0x001326f0, 0xfff63e80, 0, 0}}, {{0x00138d15, 0xfff60a78, 0, 0}}, - {{0x0013f33a, 0xfff5d670, 0, 0}}, {{0x0014595f, 0xfff5a268, 0, 0}}, - {{0x0014bf84, 0xfff56e60, 0, 0}}, {{0x001525a9, 0xfff53a58, 0, 0}}, - {{0x00158bce, 0xfff50650, 0, 0}}, {{0x0015f1f3, 0xfff4d248, 0, 0}}, - {{0x00165818, 0xfff49e40, 0, 0}}, {{0x0016be3d, 0xfff46a38, 0, 0}}, - {{0x00172462, 0xfff43630, 0, 0}}, {{0x00178a87, 0xfff40228, 0, 0}}, - {{0x0017f0ac, 0xfff3ce20, 0, 0}}, {{0x001856d1, 0xfff39a18, 0, 0}}, - {{0x0018bcf6, 0xfff36610, 0, 0}}, {{0x0019231b, 0xfff33208, 0, 0}}, - {{0x00198940, 0xfff2fe00, 0, 0}}, {{0x0019ef65, 0xfff2c9f8, 0, 0}}, - {{0x001a558a, 0xfff295f0, 0, 0}}, {{0x001abbaf, 0xfff261e8, 0, 0}}, - {{0x001b21d4, 0xfff22de0, 0, 0}}, {{0x001b87f9, 0xfff1f9d8, 0, 0}}, - {{0x001bee1e, 0xfff1c5d0, 0, 0}}, {{0x001c5443, 0xfff191c8, 0, 0}}, - {{0x001cba68, 0xfff15dc0, 0, 0}}, {{0x001d208d, 0xfff129b8, 0, 0}}, - {{0x001d86b2, 0xfff0f5b0, 0, 0}}, {{0x001decd7, 0xfff0c1a8, 0, 0}}, - {{0x001e52fc, 0xfff08da0, 0, 0}}, {{0x001eb921, 0xfff05998, 0, 0}}, - {{0x001f1f46, 0xfff02590, 0, 0}}, {{0x001f856b, 0xffeff188, 0, 0}}, - {{0x001feb90, 0xffefbd80, 0, 0}}, {{0x002051b5, 0xffef8978, 0, 0}}, - {{0x0020b7da, 0xffef5570, 0, 0}}, {{0x00211dff, 0xffef2168, 0, 0}}, - {{0x00218424, 0xffeeed60, 0, 0}}, {{0x0021ea49, 0xffeeb958, 0, 0}}, - {{0x0022506e, 0xffee8550, 0, 0}}, {{0x0022b693, 0xffee5148, 0, 0}}, - {{0x00231cb8, 0xffee1d40, 0, 0}}, {{0x002382dd, 0xffede938, 0, 0}}, - {{0x0023e902, 0xffedb530, 0, 0}}, {{0x00244f27, 0xffed8128, 0, 0}}, - {{0x0024b54c, 0xffed4d20, 0, 0}}, {{0x00251b71, 0xffed1918, 0, 0}}, - {{0x00258196, 0xffece510, 0, 0}}, {{0x0025e7bb, 0xffecb108, 0, 0}}, - {{0x00264de0, 0xffec7d00, 0, 0}}, {{0x0026b405, 0xffec48f8, 0, 0}}, - {{0x00271a2a, 0xffec14f0, 0, 0}}, {{0x0027804f, 0xffebe0e8, 0, 0}}, - {{0x0027e674, 0xffebace0, 0, 0}}, {{0x00284c99, 0xffeb78d8, 0, 0}}, - {{0x0028b2be, 0xffeb44d0, 0, 0}}, {{0x002918e3, 0xffeb10c8, 0, 0}}, - {{0x00297f08, 0xffeadcc0, 0, 0}}, {{0x0029e52d, 0xffeaa8b8, 0, 0}}, - {{0x002a4b52, 0xffea74b0, 0, 0}}, {{0x002ab177, 0xffea40a8, 0, 0}}, - {{0x002b179c, 0xffea0ca0, 0, 0}}, {{0x002b7dc1, 0xffe9d898, 0, 0}}, - {{0x002be3e6, 0xffe9a490, 0, 0}}, {{0x002c4a0b, 0xffe97088, 0, 0}}, - {{0x002cb030, 0xffe93c80, 0, 0}}, {{0x002d1655, 0xffe90878, 0, 0}}, - {{0x002d7c7a, 0xffe8d470, 0, 0}}, {{0x002de29f, 0xffe8a068, 0, 0}}, - {{0x002e48c4, 0xffe86c60, 0, 0}}, {{0x002eaee9, 0xffe83858, 0, 0}}, - {{0x002f150e, 0xffe80450, 0, 0}}, {{0x002f7b33, 0xffe7d048, 0, 0}}, - {{0x002fe158, 0xffe79c40, 0, 0}}, {{0x0030477d, 0xffe76838, 0, 0}}, - {{0x0030ada2, 0xffe73430, 0, 0}}, {{0x003113c7, 0xffe70028, 0, 0}}, - {{0x003179ec, 0xffe6cc20, 0, 0}}, {{0x0031e011, 0xffe69818, 0, 0}}, - {{0x00324636, 0xffe66410, 0, 0}}, {{0x0032ac5b, 0xffe63008, 0, 0}} -}; diff --git a/thirdparty/libwebp/enc/alpha.c b/thirdparty/libwebp/enc/alpha.c deleted file mode 100644 index 03e3ad07f5..0000000000 --- a/thirdparty/libwebp/enc/alpha.c +++ /dev/null @@ -1,433 +0,0 @@ -// Copyright 2011 Google Inc. All Rights Reserved. -// -// Use of this source code is governed by a BSD-style license -// that can be found in the COPYING file in the root of the source -// tree. An additional intellectual property rights grant can be found -// in the file PATENTS. All contributing project authors may -// be found in the AUTHORS file in the root of the source tree. -// ----------------------------------------------------------------------------- -// -// Alpha-plane compression. -// -// Author: Skal (pascal.massimino@gmail.com) - -#include -#include - -#include "./vp8enci.h" -#include "../dsp/dsp.h" -#include "../utils/filters.h" -#include "../utils/quant_levels.h" -#include "../utils/utils.h" -#include "../webp/format_constants.h" - -// ----------------------------------------------------------------------------- -// Encodes the given alpha data via specified compression method 'method'. -// The pre-processing (quantization) is performed if 'quality' is less than 100. -// For such cases, the encoding is lossy. The valid range is [0, 100] for -// 'quality' and [0, 1] for 'method': -// 'method = 0' - No compression; -// 'method = 1' - Use lossless coder on the alpha plane only -// 'filter' values [0, 4] correspond to prediction modes none, horizontal, -// vertical & gradient filters. The prediction mode 4 will try all the -// prediction modes 0 to 3 and pick the best one. -// 'effort_level': specifies how much effort must be spent to try and reduce -// the compressed output size. In range 0 (quick) to 6 (slow). -// -// 'output' corresponds to the buffer containing compressed alpha data. -// This buffer is allocated by this method and caller should call -// WebPSafeFree(*output) when done. -// 'output_size' corresponds to size of this compressed alpha buffer. -// -// Returns 1 on successfully encoding the alpha and -// 0 if either: -// invalid quality or method, or -// memory allocation for the compressed data fails. - -#include "../enc/vp8li.h" - -static int EncodeLossless(const uint8_t* const data, int width, int height, - int effort_level, // in [0..6] range - VP8LBitWriter* const bw, - WebPAuxStats* const stats) { - int ok = 0; - WebPConfig config; - WebPPicture picture; - - WebPPictureInit(&picture); - picture.width = width; - picture.height = height; - picture.use_argb = 1; - picture.stats = stats; - if (!WebPPictureAlloc(&picture)) return 0; - - // Transfer the alpha values to the green channel. - WebPDispatchAlphaToGreen(data, width, picture.width, picture.height, - picture.argb, picture.argb_stride); - - WebPConfigInit(&config); - config.lossless = 1; - // Enable exact, or it would alter RGB values of transparent alpha, which is - // normally OK but not here since we are not encoding the input image but an - // internal encoding-related image containing necessary exact information in - // RGB channels. - config.exact = 1; - config.method = effort_level; // impact is very small - // Set a low default quality for encoding alpha. Ensure that Alpha quality at - // lower methods (3 and below) is less than the threshold for triggering - // costly 'BackwardReferencesTraceBackwards'. - config.quality = 8.f * effort_level; - assert(config.quality >= 0 && config.quality <= 100.f); - - // TODO(urvang): Temporary fix to avoid generating images that trigger - // a decoder bug related to alpha with color cache. - // See: https://code.google.com/p/webp/issues/detail?id=239 - // Need to re-enable this later. - ok = (VP8LEncodeStream(&config, &picture, bw, 0 /*use_cache*/) == VP8_ENC_OK); - WebPPictureFree(&picture); - ok = ok && !bw->error_; - if (!ok) { - VP8LBitWriterWipeOut(bw); - return 0; - } - return 1; -} - -// ----------------------------------------------------------------------------- - -// Small struct to hold the result of a filter mode compression attempt. -typedef struct { - size_t score; - VP8BitWriter bw; - WebPAuxStats stats; -} FilterTrial; - -// This function always returns an initialized 'bw' object, even upon error. -static int EncodeAlphaInternal(const uint8_t* const data, int width, int height, - int method, int filter, int reduce_levels, - int effort_level, // in [0..6] range - uint8_t* const tmp_alpha, - FilterTrial* result) { - int ok = 0; - const uint8_t* alpha_src; - WebPFilterFunc filter_func; - uint8_t header; - const size_t data_size = width * height; - const uint8_t* output = NULL; - size_t output_size = 0; - VP8LBitWriter tmp_bw; - - assert((uint64_t)data_size == (uint64_t)width * height); // as per spec - assert(filter >= 0 && filter < WEBP_FILTER_LAST); - assert(method >= ALPHA_NO_COMPRESSION); - assert(method <= ALPHA_LOSSLESS_COMPRESSION); - assert(sizeof(header) == ALPHA_HEADER_LEN); - - filter_func = WebPFilters[filter]; - if (filter_func != NULL) { - filter_func(data, width, height, width, tmp_alpha); - alpha_src = tmp_alpha; - } else { - alpha_src = data; - } - - if (method != ALPHA_NO_COMPRESSION) { - ok = VP8LBitWriterInit(&tmp_bw, data_size >> 3); - ok = ok && EncodeLossless(alpha_src, width, height, effort_level, - &tmp_bw, &result->stats); - if (ok) { - output = VP8LBitWriterFinish(&tmp_bw); - output_size = VP8LBitWriterNumBytes(&tmp_bw); - if (output_size > data_size) { - // compressed size is larger than source! Revert to uncompressed mode. - method = ALPHA_NO_COMPRESSION; - VP8LBitWriterWipeOut(&tmp_bw); - } - } else { - VP8LBitWriterWipeOut(&tmp_bw); - return 0; - } - } - - if (method == ALPHA_NO_COMPRESSION) { - output = alpha_src; - output_size = data_size; - ok = 1; - } - - // Emit final result. - header = method | (filter << 2); - if (reduce_levels) header |= ALPHA_PREPROCESSED_LEVELS << 4; - - VP8BitWriterInit(&result->bw, ALPHA_HEADER_LEN + output_size); - ok = ok && VP8BitWriterAppend(&result->bw, &header, ALPHA_HEADER_LEN); - ok = ok && VP8BitWriterAppend(&result->bw, output, output_size); - - if (method != ALPHA_NO_COMPRESSION) { - VP8LBitWriterWipeOut(&tmp_bw); - } - ok = ok && !result->bw.error_; - result->score = VP8BitWriterSize(&result->bw); - return ok; -} - -// ----------------------------------------------------------------------------- - -static int GetNumColors(const uint8_t* data, int width, int height, - int stride) { - int j; - int colors = 0; - uint8_t color[256] = { 0 }; - - for (j = 0; j < height; ++j) { - int i; - const uint8_t* const p = data + j * stride; - for (i = 0; i < width; ++i) { - color[p[i]] = 1; - } - } - for (j = 0; j < 256; ++j) { - if (color[j] > 0) ++colors; - } - return colors; -} - -#define FILTER_TRY_NONE (1 << WEBP_FILTER_NONE) -#define FILTER_TRY_ALL ((1 << WEBP_FILTER_LAST) - 1) - -// Given the input 'filter' option, return an OR'd bit-set of filters to try. -static uint32_t GetFilterMap(const uint8_t* alpha, int width, int height, - int filter, int effort_level) { - uint32_t bit_map = 0U; - if (filter == WEBP_FILTER_FAST) { - // Quick estimate of the best candidate. - int try_filter_none = (effort_level > 3); - const int kMinColorsForFilterNone = 16; - const int kMaxColorsForFilterNone = 192; - const int num_colors = GetNumColors(alpha, width, height, width); - // For low number of colors, NONE yields better compression. - filter = (num_colors <= kMinColorsForFilterNone) - ? WEBP_FILTER_NONE - : WebPEstimateBestFilter(alpha, width, height, width); - bit_map |= 1 << filter; - // For large number of colors, try FILTER_NONE in addition to the best - // filter as well. - if (try_filter_none || num_colors > kMaxColorsForFilterNone) { - bit_map |= FILTER_TRY_NONE; - } - } else if (filter == WEBP_FILTER_NONE) { - bit_map = FILTER_TRY_NONE; - } else { // WEBP_FILTER_BEST -> try all - bit_map = FILTER_TRY_ALL; - } - return bit_map; -} - -static void InitFilterTrial(FilterTrial* const score) { - score->score = (size_t)~0U; - VP8BitWriterInit(&score->bw, 0); -} - -static int ApplyFiltersAndEncode(const uint8_t* alpha, int width, int height, - size_t data_size, int method, int filter, - int reduce_levels, int effort_level, - uint8_t** const output, - size_t* const output_size, - WebPAuxStats* const stats) { - int ok = 1; - FilterTrial best; - uint32_t try_map = - GetFilterMap(alpha, width, height, filter, effort_level); - InitFilterTrial(&best); - - if (try_map != FILTER_TRY_NONE) { - uint8_t* filtered_alpha = (uint8_t*)WebPSafeMalloc(1ULL, data_size); - if (filtered_alpha == NULL) return 0; - - for (filter = WEBP_FILTER_NONE; ok && try_map; ++filter, try_map >>= 1) { - if (try_map & 1) { - FilterTrial trial; - ok = EncodeAlphaInternal(alpha, width, height, method, filter, - reduce_levels, effort_level, filtered_alpha, - &trial); - if (ok && trial.score < best.score) { - VP8BitWriterWipeOut(&best.bw); - best = trial; - } else { - VP8BitWriterWipeOut(&trial.bw); - } - } - } - WebPSafeFree(filtered_alpha); - } else { - ok = EncodeAlphaInternal(alpha, width, height, method, WEBP_FILTER_NONE, - reduce_levels, effort_level, NULL, &best); - } - if (ok) { - if (stats != NULL) { - stats->lossless_features = best.stats.lossless_features; - stats->histogram_bits = best.stats.histogram_bits; - stats->transform_bits = best.stats.transform_bits; - stats->cache_bits = best.stats.cache_bits; - stats->palette_size = best.stats.palette_size; - stats->lossless_size = best.stats.lossless_size; - stats->lossless_hdr_size = best.stats.lossless_hdr_size; - stats->lossless_data_size = best.stats.lossless_data_size; - } - *output_size = VP8BitWriterSize(&best.bw); - *output = VP8BitWriterBuf(&best.bw); - } else { - VP8BitWriterWipeOut(&best.bw); - } - return ok; -} - -static int EncodeAlpha(VP8Encoder* const enc, - int quality, int method, int filter, - int effort_level, - uint8_t** const output, size_t* const output_size) { - const WebPPicture* const pic = enc->pic_; - const int width = pic->width; - const int height = pic->height; - - uint8_t* quant_alpha = NULL; - const size_t data_size = width * height; - uint64_t sse = 0; - int ok = 1; - const int reduce_levels = (quality < 100); - - // quick sanity checks - assert((uint64_t)data_size == (uint64_t)width * height); // as per spec - assert(enc != NULL && pic != NULL && pic->a != NULL); - assert(output != NULL && output_size != NULL); - assert(width > 0 && height > 0); - assert(pic->a_stride >= width); - assert(filter >= WEBP_FILTER_NONE && filter <= WEBP_FILTER_FAST); - - if (quality < 0 || quality > 100) { - return 0; - } - - if (method < ALPHA_NO_COMPRESSION || method > ALPHA_LOSSLESS_COMPRESSION) { - return 0; - } - - if (method == ALPHA_NO_COMPRESSION) { - // Don't filter, as filtering will make no impact on compressed size. - filter = WEBP_FILTER_NONE; - } - - quant_alpha = (uint8_t*)WebPSafeMalloc(1ULL, data_size); - if (quant_alpha == NULL) { - return 0; - } - - // Extract alpha data (width x height) from raw_data (stride x height). - WebPCopyPlane(pic->a, pic->a_stride, quant_alpha, width, width, height); - - if (reduce_levels) { // No Quantization required for 'quality = 100'. - // 16 alpha levels gives quite a low MSE w.r.t original alpha plane hence - // mapped to moderate quality 70. Hence Quality:[0, 70] -> Levels:[2, 16] - // and Quality:]70, 100] -> Levels:]16, 256]. - const int alpha_levels = (quality <= 70) ? (2 + quality / 5) - : (16 + (quality - 70) * 8); - ok = QuantizeLevels(quant_alpha, width, height, alpha_levels, &sse); - } - - if (ok) { - VP8FiltersInit(); - ok = ApplyFiltersAndEncode(quant_alpha, width, height, data_size, method, - filter, reduce_levels, effort_level, output, - output_size, pic->stats); - if (pic->stats != NULL) { // need stats? - pic->stats->coded_size += (int)(*output_size); - enc->sse_[3] = sse; - } - } - - WebPSafeFree(quant_alpha); - return ok; -} - -//------------------------------------------------------------------------------ -// Main calls - -static int CompressAlphaJob(VP8Encoder* const enc, void* dummy) { - const WebPConfig* config = enc->config_; - uint8_t* alpha_data = NULL; - size_t alpha_size = 0; - const int effort_level = config->method; // maps to [0..6] - const WEBP_FILTER_TYPE filter = - (config->alpha_filtering == 0) ? WEBP_FILTER_NONE : - (config->alpha_filtering == 1) ? WEBP_FILTER_FAST : - WEBP_FILTER_BEST; - if (!EncodeAlpha(enc, config->alpha_quality, config->alpha_compression, - filter, effort_level, &alpha_data, &alpha_size)) { - return 0; - } - if (alpha_size != (uint32_t)alpha_size) { // Sanity check. - WebPSafeFree(alpha_data); - return 0; - } - enc->alpha_data_size_ = (uint32_t)alpha_size; - enc->alpha_data_ = alpha_data; - (void)dummy; - return 1; -} - -void VP8EncInitAlpha(VP8Encoder* const enc) { - WebPInitAlphaProcessing(); - enc->has_alpha_ = WebPPictureHasTransparency(enc->pic_); - enc->alpha_data_ = NULL; - enc->alpha_data_size_ = 0; - if (enc->thread_level_ > 0) { - WebPWorker* const worker = &enc->alpha_worker_; - WebPGetWorkerInterface()->Init(worker); - worker->data1 = enc; - worker->data2 = NULL; - worker->hook = (WebPWorkerHook)CompressAlphaJob; - } -} - -int VP8EncStartAlpha(VP8Encoder* const enc) { - if (enc->has_alpha_) { - if (enc->thread_level_ > 0) { - WebPWorker* const worker = &enc->alpha_worker_; - // Makes sure worker is good to go. - if (!WebPGetWorkerInterface()->Reset(worker)) { - return 0; - } - WebPGetWorkerInterface()->Launch(worker); - return 1; - } else { - return CompressAlphaJob(enc, NULL); // just do the job right away - } - } - return 1; -} - -int VP8EncFinishAlpha(VP8Encoder* const enc) { - if (enc->has_alpha_) { - if (enc->thread_level_ > 0) { - WebPWorker* const worker = &enc->alpha_worker_; - if (!WebPGetWorkerInterface()->Sync(worker)) return 0; // error - } - } - return WebPReportProgress(enc->pic_, enc->percent_ + 20, &enc->percent_); -} - -int VP8EncDeleteAlpha(VP8Encoder* const enc) { - int ok = 1; - if (enc->thread_level_ > 0) { - WebPWorker* const worker = &enc->alpha_worker_; - // finish anything left in flight - ok = WebPGetWorkerInterface()->Sync(worker); - // still need to end the worker, even if !ok - WebPGetWorkerInterface()->End(worker); - } - WebPSafeFree(enc->alpha_data_); - enc->alpha_data_ = NULL; - enc->alpha_data_size_ = 0; - enc->has_alpha_ = 0; - return ok; -} diff --git a/thirdparty/libwebp/enc/alpha_enc.c b/thirdparty/libwebp/enc/alpha_enc.c new file mode 100644 index 0000000000..5a2c931f92 --- /dev/null +++ b/thirdparty/libwebp/enc/alpha_enc.c @@ -0,0 +1,433 @@ +// Copyright 2011 Google Inc. All Rights Reserved. +// +// Use of this source code is governed by a BSD-style license +// that can be found in the COPYING file in the root of the source +// tree. An additional intellectual property rights grant can be found +// in the file PATENTS. All contributing project authors may +// be found in the AUTHORS file in the root of the source tree. +// ----------------------------------------------------------------------------- +// +// Alpha-plane compression. +// +// Author: Skal (pascal.massimino@gmail.com) + +#include +#include + +#include "./vp8i_enc.h" +#include "../dsp/dsp.h" +#include "../utils/filters_utils.h" +#include "../utils/quant_levels_utils.h" +#include "../utils/utils.h" +#include "../webp/format_constants.h" + +// ----------------------------------------------------------------------------- +// Encodes the given alpha data via specified compression method 'method'. +// The pre-processing (quantization) is performed if 'quality' is less than 100. +// For such cases, the encoding is lossy. The valid range is [0, 100] for +// 'quality' and [0, 1] for 'method': +// 'method = 0' - No compression; +// 'method = 1' - Use lossless coder on the alpha plane only +// 'filter' values [0, 4] correspond to prediction modes none, horizontal, +// vertical & gradient filters. The prediction mode 4 will try all the +// prediction modes 0 to 3 and pick the best one. +// 'effort_level': specifies how much effort must be spent to try and reduce +// the compressed output size. In range 0 (quick) to 6 (slow). +// +// 'output' corresponds to the buffer containing compressed alpha data. +// This buffer is allocated by this method and caller should call +// WebPSafeFree(*output) when done. +// 'output_size' corresponds to size of this compressed alpha buffer. +// +// Returns 1 on successfully encoding the alpha and +// 0 if either: +// invalid quality or method, or +// memory allocation for the compressed data fails. + +#include "../enc/vp8li_enc.h" + +static int EncodeLossless(const uint8_t* const data, int width, int height, + int effort_level, // in [0..6] range + VP8LBitWriter* const bw, + WebPAuxStats* const stats) { + int ok = 0; + WebPConfig config; + WebPPicture picture; + + WebPPictureInit(&picture); + picture.width = width; + picture.height = height; + picture.use_argb = 1; + picture.stats = stats; + if (!WebPPictureAlloc(&picture)) return 0; + + // Transfer the alpha values to the green channel. + WebPDispatchAlphaToGreen(data, width, picture.width, picture.height, + picture.argb, picture.argb_stride); + + WebPConfigInit(&config); + config.lossless = 1; + // Enable exact, or it would alter RGB values of transparent alpha, which is + // normally OK but not here since we are not encoding the input image but an + // internal encoding-related image containing necessary exact information in + // RGB channels. + config.exact = 1; + config.method = effort_level; // impact is very small + // Set a low default quality for encoding alpha. Ensure that Alpha quality at + // lower methods (3 and below) is less than the threshold for triggering + // costly 'BackwardReferencesTraceBackwards'. + config.quality = 8.f * effort_level; + assert(config.quality >= 0 && config.quality <= 100.f); + + // TODO(urvang): Temporary fix to avoid generating images that trigger + // a decoder bug related to alpha with color cache. + // See: https://code.google.com/p/webp/issues/detail?id=239 + // Need to re-enable this later. + ok = (VP8LEncodeStream(&config, &picture, bw, 0 /*use_cache*/) == VP8_ENC_OK); + WebPPictureFree(&picture); + ok = ok && !bw->error_; + if (!ok) { + VP8LBitWriterWipeOut(bw); + return 0; + } + return 1; +} + +// ----------------------------------------------------------------------------- + +// Small struct to hold the result of a filter mode compression attempt. +typedef struct { + size_t score; + VP8BitWriter bw; + WebPAuxStats stats; +} FilterTrial; + +// This function always returns an initialized 'bw' object, even upon error. +static int EncodeAlphaInternal(const uint8_t* const data, int width, int height, + int method, int filter, int reduce_levels, + int effort_level, // in [0..6] range + uint8_t* const tmp_alpha, + FilterTrial* result) { + int ok = 0; + const uint8_t* alpha_src; + WebPFilterFunc filter_func; + uint8_t header; + const size_t data_size = width * height; + const uint8_t* output = NULL; + size_t output_size = 0; + VP8LBitWriter tmp_bw; + + assert((uint64_t)data_size == (uint64_t)width * height); // as per spec + assert(filter >= 0 && filter < WEBP_FILTER_LAST); + assert(method >= ALPHA_NO_COMPRESSION); + assert(method <= ALPHA_LOSSLESS_COMPRESSION); + assert(sizeof(header) == ALPHA_HEADER_LEN); + + filter_func = WebPFilters[filter]; + if (filter_func != NULL) { + filter_func(data, width, height, width, tmp_alpha); + alpha_src = tmp_alpha; + } else { + alpha_src = data; + } + + if (method != ALPHA_NO_COMPRESSION) { + ok = VP8LBitWriterInit(&tmp_bw, data_size >> 3); + ok = ok && EncodeLossless(alpha_src, width, height, effort_level, + &tmp_bw, &result->stats); + if (ok) { + output = VP8LBitWriterFinish(&tmp_bw); + output_size = VP8LBitWriterNumBytes(&tmp_bw); + if (output_size > data_size) { + // compressed size is larger than source! Revert to uncompressed mode. + method = ALPHA_NO_COMPRESSION; + VP8LBitWriterWipeOut(&tmp_bw); + } + } else { + VP8LBitWriterWipeOut(&tmp_bw); + return 0; + } + } + + if (method == ALPHA_NO_COMPRESSION) { + output = alpha_src; + output_size = data_size; + ok = 1; + } + + // Emit final result. + header = method | (filter << 2); + if (reduce_levels) header |= ALPHA_PREPROCESSED_LEVELS << 4; + + VP8BitWriterInit(&result->bw, ALPHA_HEADER_LEN + output_size); + ok = ok && VP8BitWriterAppend(&result->bw, &header, ALPHA_HEADER_LEN); + ok = ok && VP8BitWriterAppend(&result->bw, output, output_size); + + if (method != ALPHA_NO_COMPRESSION) { + VP8LBitWriterWipeOut(&tmp_bw); + } + ok = ok && !result->bw.error_; + result->score = VP8BitWriterSize(&result->bw); + return ok; +} + +// ----------------------------------------------------------------------------- + +static int GetNumColors(const uint8_t* data, int width, int height, + int stride) { + int j; + int colors = 0; + uint8_t color[256] = { 0 }; + + for (j = 0; j < height; ++j) { + int i; + const uint8_t* const p = data + j * stride; + for (i = 0; i < width; ++i) { + color[p[i]] = 1; + } + } + for (j = 0; j < 256; ++j) { + if (color[j] > 0) ++colors; + } + return colors; +} + +#define FILTER_TRY_NONE (1 << WEBP_FILTER_NONE) +#define FILTER_TRY_ALL ((1 << WEBP_FILTER_LAST) - 1) + +// Given the input 'filter' option, return an OR'd bit-set of filters to try. +static uint32_t GetFilterMap(const uint8_t* alpha, int width, int height, + int filter, int effort_level) { + uint32_t bit_map = 0U; + if (filter == WEBP_FILTER_FAST) { + // Quick estimate of the best candidate. + int try_filter_none = (effort_level > 3); + const int kMinColorsForFilterNone = 16; + const int kMaxColorsForFilterNone = 192; + const int num_colors = GetNumColors(alpha, width, height, width); + // For low number of colors, NONE yields better compression. + filter = (num_colors <= kMinColorsForFilterNone) + ? WEBP_FILTER_NONE + : WebPEstimateBestFilter(alpha, width, height, width); + bit_map |= 1 << filter; + // For large number of colors, try FILTER_NONE in addition to the best + // filter as well. + if (try_filter_none || num_colors > kMaxColorsForFilterNone) { + bit_map |= FILTER_TRY_NONE; + } + } else if (filter == WEBP_FILTER_NONE) { + bit_map = FILTER_TRY_NONE; + } else { // WEBP_FILTER_BEST -> try all + bit_map = FILTER_TRY_ALL; + } + return bit_map; +} + +static void InitFilterTrial(FilterTrial* const score) { + score->score = (size_t)~0U; + VP8BitWriterInit(&score->bw, 0); +} + +static int ApplyFiltersAndEncode(const uint8_t* alpha, int width, int height, + size_t data_size, int method, int filter, + int reduce_levels, int effort_level, + uint8_t** const output, + size_t* const output_size, + WebPAuxStats* const stats) { + int ok = 1; + FilterTrial best; + uint32_t try_map = + GetFilterMap(alpha, width, height, filter, effort_level); + InitFilterTrial(&best); + + if (try_map != FILTER_TRY_NONE) { + uint8_t* filtered_alpha = (uint8_t*)WebPSafeMalloc(1ULL, data_size); + if (filtered_alpha == NULL) return 0; + + for (filter = WEBP_FILTER_NONE; ok && try_map; ++filter, try_map >>= 1) { + if (try_map & 1) { + FilterTrial trial; + ok = EncodeAlphaInternal(alpha, width, height, method, filter, + reduce_levels, effort_level, filtered_alpha, + &trial); + if (ok && trial.score < best.score) { + VP8BitWriterWipeOut(&best.bw); + best = trial; + } else { + VP8BitWriterWipeOut(&trial.bw); + } + } + } + WebPSafeFree(filtered_alpha); + } else { + ok = EncodeAlphaInternal(alpha, width, height, method, WEBP_FILTER_NONE, + reduce_levels, effort_level, NULL, &best); + } + if (ok) { + if (stats != NULL) { + stats->lossless_features = best.stats.lossless_features; + stats->histogram_bits = best.stats.histogram_bits; + stats->transform_bits = best.stats.transform_bits; + stats->cache_bits = best.stats.cache_bits; + stats->palette_size = best.stats.palette_size; + stats->lossless_size = best.stats.lossless_size; + stats->lossless_hdr_size = best.stats.lossless_hdr_size; + stats->lossless_data_size = best.stats.lossless_data_size; + } + *output_size = VP8BitWriterSize(&best.bw); + *output = VP8BitWriterBuf(&best.bw); + } else { + VP8BitWriterWipeOut(&best.bw); + } + return ok; +} + +static int EncodeAlpha(VP8Encoder* const enc, + int quality, int method, int filter, + int effort_level, + uint8_t** const output, size_t* const output_size) { + const WebPPicture* const pic = enc->pic_; + const int width = pic->width; + const int height = pic->height; + + uint8_t* quant_alpha = NULL; + const size_t data_size = width * height; + uint64_t sse = 0; + int ok = 1; + const int reduce_levels = (quality < 100); + + // quick sanity checks + assert((uint64_t)data_size == (uint64_t)width * height); // as per spec + assert(enc != NULL && pic != NULL && pic->a != NULL); + assert(output != NULL && output_size != NULL); + assert(width > 0 && height > 0); + assert(pic->a_stride >= width); + assert(filter >= WEBP_FILTER_NONE && filter <= WEBP_FILTER_FAST); + + if (quality < 0 || quality > 100) { + return 0; + } + + if (method < ALPHA_NO_COMPRESSION || method > ALPHA_LOSSLESS_COMPRESSION) { + return 0; + } + + if (method == ALPHA_NO_COMPRESSION) { + // Don't filter, as filtering will make no impact on compressed size. + filter = WEBP_FILTER_NONE; + } + + quant_alpha = (uint8_t*)WebPSafeMalloc(1ULL, data_size); + if (quant_alpha == NULL) { + return 0; + } + + // Extract alpha data (width x height) from raw_data (stride x height). + WebPCopyPlane(pic->a, pic->a_stride, quant_alpha, width, width, height); + + if (reduce_levels) { // No Quantization required for 'quality = 100'. + // 16 alpha levels gives quite a low MSE w.r.t original alpha plane hence + // mapped to moderate quality 70. Hence Quality:[0, 70] -> Levels:[2, 16] + // and Quality:]70, 100] -> Levels:]16, 256]. + const int alpha_levels = (quality <= 70) ? (2 + quality / 5) + : (16 + (quality - 70) * 8); + ok = QuantizeLevels(quant_alpha, width, height, alpha_levels, &sse); + } + + if (ok) { + VP8FiltersInit(); + ok = ApplyFiltersAndEncode(quant_alpha, width, height, data_size, method, + filter, reduce_levels, effort_level, output, + output_size, pic->stats); + if (pic->stats != NULL) { // need stats? + pic->stats->coded_size += (int)(*output_size); + enc->sse_[3] = sse; + } + } + + WebPSafeFree(quant_alpha); + return ok; +} + +//------------------------------------------------------------------------------ +// Main calls + +static int CompressAlphaJob(VP8Encoder* const enc, void* dummy) { + const WebPConfig* config = enc->config_; + uint8_t* alpha_data = NULL; + size_t alpha_size = 0; + const int effort_level = config->method; // maps to [0..6] + const WEBP_FILTER_TYPE filter = + (config->alpha_filtering == 0) ? WEBP_FILTER_NONE : + (config->alpha_filtering == 1) ? WEBP_FILTER_FAST : + WEBP_FILTER_BEST; + if (!EncodeAlpha(enc, config->alpha_quality, config->alpha_compression, + filter, effort_level, &alpha_data, &alpha_size)) { + return 0; + } + if (alpha_size != (uint32_t)alpha_size) { // Sanity check. + WebPSafeFree(alpha_data); + return 0; + } + enc->alpha_data_size_ = (uint32_t)alpha_size; + enc->alpha_data_ = alpha_data; + (void)dummy; + return 1; +} + +void VP8EncInitAlpha(VP8Encoder* const enc) { + WebPInitAlphaProcessing(); + enc->has_alpha_ = WebPPictureHasTransparency(enc->pic_); + enc->alpha_data_ = NULL; + enc->alpha_data_size_ = 0; + if (enc->thread_level_ > 0) { + WebPWorker* const worker = &enc->alpha_worker_; + WebPGetWorkerInterface()->Init(worker); + worker->data1 = enc; + worker->data2 = NULL; + worker->hook = (WebPWorkerHook)CompressAlphaJob; + } +} + +int VP8EncStartAlpha(VP8Encoder* const enc) { + if (enc->has_alpha_) { + if (enc->thread_level_ > 0) { + WebPWorker* const worker = &enc->alpha_worker_; + // Makes sure worker is good to go. + if (!WebPGetWorkerInterface()->Reset(worker)) { + return 0; + } + WebPGetWorkerInterface()->Launch(worker); + return 1; + } else { + return CompressAlphaJob(enc, NULL); // just do the job right away + } + } + return 1; +} + +int VP8EncFinishAlpha(VP8Encoder* const enc) { + if (enc->has_alpha_) { + if (enc->thread_level_ > 0) { + WebPWorker* const worker = &enc->alpha_worker_; + if (!WebPGetWorkerInterface()->Sync(worker)) return 0; // error + } + } + return WebPReportProgress(enc->pic_, enc->percent_ + 20, &enc->percent_); +} + +int VP8EncDeleteAlpha(VP8Encoder* const enc) { + int ok = 1; + if (enc->thread_level_ > 0) { + WebPWorker* const worker = &enc->alpha_worker_; + // finish anything left in flight + ok = WebPGetWorkerInterface()->Sync(worker); + // still need to end the worker, even if !ok + WebPGetWorkerInterface()->End(worker); + } + WebPSafeFree(enc->alpha_data_); + enc->alpha_data_ = NULL; + enc->alpha_data_size_ = 0; + enc->has_alpha_ = 0; + return ok; +} diff --git a/thirdparty/libwebp/enc/analysis.c b/thirdparty/libwebp/enc/analysis.c deleted file mode 100644 index 136c331289..0000000000 --- a/thirdparty/libwebp/enc/analysis.c +++ /dev/null @@ -1,506 +0,0 @@ -// Copyright 2011 Google Inc. All Rights Reserved. -// -// Use of this source code is governed by a BSD-style license -// that can be found in the COPYING file in the root of the source -// tree. An additional intellectual property rights grant can be found -// in the file PATENTS. All contributing project authors may -// be found in the AUTHORS file in the root of the source tree. -// ----------------------------------------------------------------------------- -// -// Macroblock analysis -// -// Author: Skal (pascal.massimino@gmail.com) - -#include -#include -#include - -#include "./vp8enci.h" -#include "./cost.h" -#include "../utils/utils.h" - -#define MAX_ITERS_K_MEANS 6 - -//------------------------------------------------------------------------------ -// Smooth the segment map by replacing isolated block by the majority of its -// neighbours. - -static void SmoothSegmentMap(VP8Encoder* const enc) { - int n, x, y; - const int w = enc->mb_w_; - const int h = enc->mb_h_; - const int majority_cnt_3_x_3_grid = 5; - uint8_t* const tmp = (uint8_t*)WebPSafeMalloc(w * h, sizeof(*tmp)); - assert((uint64_t)(w * h) == (uint64_t)w * h); // no overflow, as per spec - - if (tmp == NULL) return; - for (y = 1; y < h - 1; ++y) { - for (x = 1; x < w - 1; ++x) { - int cnt[NUM_MB_SEGMENTS] = { 0 }; - const VP8MBInfo* const mb = &enc->mb_info_[x + w * y]; - int majority_seg = mb->segment_; - // Check the 8 neighbouring segment values. - cnt[mb[-w - 1].segment_]++; // top-left - cnt[mb[-w + 0].segment_]++; // top - cnt[mb[-w + 1].segment_]++; // top-right - cnt[mb[ - 1].segment_]++; // left - cnt[mb[ + 1].segment_]++; // right - cnt[mb[ w - 1].segment_]++; // bottom-left - cnt[mb[ w + 0].segment_]++; // bottom - cnt[mb[ w + 1].segment_]++; // bottom-right - for (n = 0; n < NUM_MB_SEGMENTS; ++n) { - if (cnt[n] >= majority_cnt_3_x_3_grid) { - majority_seg = n; - break; - } - } - tmp[x + y * w] = majority_seg; - } - } - for (y = 1; y < h - 1; ++y) { - for (x = 1; x < w - 1; ++x) { - VP8MBInfo* const mb = &enc->mb_info_[x + w * y]; - mb->segment_ = tmp[x + y * w]; - } - } - WebPSafeFree(tmp); -} - -//------------------------------------------------------------------------------ -// set segment susceptibility alpha_ / beta_ - -static WEBP_INLINE int clip(int v, int m, int M) { - return (v < m) ? m : (v > M) ? M : v; -} - -static void SetSegmentAlphas(VP8Encoder* const enc, - const int centers[NUM_MB_SEGMENTS], - int mid) { - const int nb = enc->segment_hdr_.num_segments_; - int min = centers[0], max = centers[0]; - int n; - - if (nb > 1) { - for (n = 0; n < nb; ++n) { - if (min > centers[n]) min = centers[n]; - if (max < centers[n]) max = centers[n]; - } - } - if (max == min) max = min + 1; - assert(mid <= max && mid >= min); - for (n = 0; n < nb; ++n) { - const int alpha = 255 * (centers[n] - mid) / (max - min); - const int beta = 255 * (centers[n] - min) / (max - min); - enc->dqm_[n].alpha_ = clip(alpha, -127, 127); - enc->dqm_[n].beta_ = clip(beta, 0, 255); - } -} - -//------------------------------------------------------------------------------ -// Compute susceptibility based on DCT-coeff histograms: -// the higher, the "easier" the macroblock is to compress. - -#define MAX_ALPHA 255 // 8b of precision for susceptibilities. -#define ALPHA_SCALE (2 * MAX_ALPHA) // scaling factor for alpha. -#define DEFAULT_ALPHA (-1) -#define IS_BETTER_ALPHA(alpha, best_alpha) ((alpha) > (best_alpha)) - -static int FinalAlphaValue(int alpha) { - alpha = MAX_ALPHA - alpha; - return clip(alpha, 0, MAX_ALPHA); -} - -static int GetAlpha(const VP8Histogram* const histo) { - // 'alpha' will later be clipped to [0..MAX_ALPHA] range, clamping outer - // values which happen to be mostly noise. This leaves the maximum precision - // for handling the useful small values which contribute most. - const int max_value = histo->max_value; - const int last_non_zero = histo->last_non_zero; - const int alpha = - (max_value > 1) ? ALPHA_SCALE * last_non_zero / max_value : 0; - return alpha; -} - -static void InitHistogram(VP8Histogram* const histo) { - histo->max_value = 0; - histo->last_non_zero = 1; -} - -static void MergeHistograms(const VP8Histogram* const in, - VP8Histogram* const out) { - if (in->max_value > out->max_value) { - out->max_value = in->max_value; - } - if (in->last_non_zero > out->last_non_zero) { - out->last_non_zero = in->last_non_zero; - } -} - -//------------------------------------------------------------------------------ -// Simplified k-Means, to assign Nb segments based on alpha-histogram - -static void AssignSegments(VP8Encoder* const enc, - const int alphas[MAX_ALPHA + 1]) { - // 'num_segments_' is previously validated and <= NUM_MB_SEGMENTS, but an - // explicit check is needed to avoid spurious warning about 'n + 1' exceeding - // array bounds of 'centers' with some compilers (noticed with gcc-4.9). - const int nb = (enc->segment_hdr_.num_segments_ < NUM_MB_SEGMENTS) ? - enc->segment_hdr_.num_segments_ : NUM_MB_SEGMENTS; - int centers[NUM_MB_SEGMENTS]; - int weighted_average = 0; - int map[MAX_ALPHA + 1]; - int a, n, k; - int min_a = 0, max_a = MAX_ALPHA, range_a; - // 'int' type is ok for histo, and won't overflow - int accum[NUM_MB_SEGMENTS], dist_accum[NUM_MB_SEGMENTS]; - - assert(nb >= 1); - assert(nb <= NUM_MB_SEGMENTS); - - // bracket the input - for (n = 0; n <= MAX_ALPHA && alphas[n] == 0; ++n) {} - min_a = n; - for (n = MAX_ALPHA; n > min_a && alphas[n] == 0; --n) {} - max_a = n; - range_a = max_a - min_a; - - // Spread initial centers evenly - for (k = 0, n = 1; k < nb; ++k, n += 2) { - assert(n < 2 * nb); - centers[k] = min_a + (n * range_a) / (2 * nb); - } - - for (k = 0; k < MAX_ITERS_K_MEANS; ++k) { // few iters are enough - int total_weight; - int displaced; - // Reset stats - for (n = 0; n < nb; ++n) { - accum[n] = 0; - dist_accum[n] = 0; - } - // Assign nearest center for each 'a' - n = 0; // track the nearest center for current 'a' - for (a = min_a; a <= max_a; ++a) { - if (alphas[a]) { - while (n + 1 < nb && abs(a - centers[n + 1]) < abs(a - centers[n])) { - n++; - } - map[a] = n; - // accumulate contribution into best centroid - dist_accum[n] += a * alphas[a]; - accum[n] += alphas[a]; - } - } - // All point are classified. Move the centroids to the - // center of their respective cloud. - displaced = 0; - weighted_average = 0; - total_weight = 0; - for (n = 0; n < nb; ++n) { - if (accum[n]) { - const int new_center = (dist_accum[n] + accum[n] / 2) / accum[n]; - displaced += abs(centers[n] - new_center); - centers[n] = new_center; - weighted_average += new_center * accum[n]; - total_weight += accum[n]; - } - } - weighted_average = (weighted_average + total_weight / 2) / total_weight; - if (displaced < 5) break; // no need to keep on looping... - } - - // Map each original value to the closest centroid - for (n = 0; n < enc->mb_w_ * enc->mb_h_; ++n) { - VP8MBInfo* const mb = &enc->mb_info_[n]; - const int alpha = mb->alpha_; - mb->segment_ = map[alpha]; - mb->alpha_ = centers[map[alpha]]; // for the record. - } - - if (nb > 1) { - const int smooth = (enc->config_->preprocessing & 1); - if (smooth) SmoothSegmentMap(enc); - } - - SetSegmentAlphas(enc, centers, weighted_average); // pick some alphas. -} - -//------------------------------------------------------------------------------ -// Macroblock analysis: collect histogram for each mode, deduce the maximal -// susceptibility and set best modes for this macroblock. -// Segment assignment is done later. - -// Number of modes to inspect for alpha_ evaluation. We don't need to test all -// the possible modes during the analysis phase: we risk falling into a local -// optimum, or be subject to boundary effect -#define MAX_INTRA16_MODE 2 -#define MAX_INTRA4_MODE 2 -#define MAX_UV_MODE 2 - -static int MBAnalyzeBestIntra16Mode(VP8EncIterator* const it) { - const int max_mode = MAX_INTRA16_MODE; - int mode; - int best_alpha = DEFAULT_ALPHA; - int best_mode = 0; - - VP8MakeLuma16Preds(it); - for (mode = 0; mode < max_mode; ++mode) { - VP8Histogram histo; - int alpha; - - InitHistogram(&histo); - VP8CollectHistogram(it->yuv_in_ + Y_OFF_ENC, - it->yuv_p_ + VP8I16ModeOffsets[mode], - 0, 16, &histo); - alpha = GetAlpha(&histo); - if (IS_BETTER_ALPHA(alpha, best_alpha)) { - best_alpha = alpha; - best_mode = mode; - } - } - VP8SetIntra16Mode(it, best_mode); - return best_alpha; -} - -static int MBAnalyzeBestIntra4Mode(VP8EncIterator* const it, - int best_alpha) { - uint8_t modes[16]; - const int max_mode = MAX_INTRA4_MODE; - int i4_alpha; - VP8Histogram total_histo; - int cur_histo = 0; - InitHistogram(&total_histo); - - VP8IteratorStartI4(it); - do { - int mode; - int best_mode_alpha = DEFAULT_ALPHA; - VP8Histogram histos[2]; - const uint8_t* const src = it->yuv_in_ + Y_OFF_ENC + VP8Scan[it->i4_]; - - VP8MakeIntra4Preds(it); - for (mode = 0; mode < max_mode; ++mode) { - int alpha; - - InitHistogram(&histos[cur_histo]); - VP8CollectHistogram(src, it->yuv_p_ + VP8I4ModeOffsets[mode], - 0, 1, &histos[cur_histo]); - alpha = GetAlpha(&histos[cur_histo]); - if (IS_BETTER_ALPHA(alpha, best_mode_alpha)) { - best_mode_alpha = alpha; - modes[it->i4_] = mode; - cur_histo ^= 1; // keep track of best histo so far. - } - } - // accumulate best histogram - MergeHistograms(&histos[cur_histo ^ 1], &total_histo); - // Note: we reuse the original samples for predictors - } while (VP8IteratorRotateI4(it, it->yuv_in_ + Y_OFF_ENC)); - - i4_alpha = GetAlpha(&total_histo); - if (IS_BETTER_ALPHA(i4_alpha, best_alpha)) { - VP8SetIntra4Mode(it, modes); - best_alpha = i4_alpha; - } - return best_alpha; -} - -static int MBAnalyzeBestUVMode(VP8EncIterator* const it) { - int best_alpha = DEFAULT_ALPHA; - int smallest_alpha = 0; - int best_mode = 0; - const int max_mode = MAX_UV_MODE; - int mode; - - VP8MakeChroma8Preds(it); - for (mode = 0; mode < max_mode; ++mode) { - VP8Histogram histo; - int alpha; - InitHistogram(&histo); - VP8CollectHistogram(it->yuv_in_ + U_OFF_ENC, - it->yuv_p_ + VP8UVModeOffsets[mode], - 16, 16 + 4 + 4, &histo); - alpha = GetAlpha(&histo); - if (IS_BETTER_ALPHA(alpha, best_alpha)) { - best_alpha = alpha; - } - // The best prediction mode tends to be the one with the smallest alpha. - if (mode == 0 || alpha < smallest_alpha) { - smallest_alpha = alpha; - best_mode = mode; - } - } - VP8SetIntraUVMode(it, best_mode); - return best_alpha; -} - -static void MBAnalyze(VP8EncIterator* const it, - int alphas[MAX_ALPHA + 1], - int* const alpha, int* const uv_alpha) { - const VP8Encoder* const enc = it->enc_; - int best_alpha, best_uv_alpha; - - VP8SetIntra16Mode(it, 0); // default: Intra16, DC_PRED - VP8SetSkip(it, 0); // not skipped - VP8SetSegment(it, 0); // default segment, spec-wise. - - best_alpha = MBAnalyzeBestIntra16Mode(it); - if (enc->method_ >= 5) { - // We go and make a fast decision for intra4/intra16. - // It's usually not a good and definitive pick, but helps seeding the stats - // about level bit-cost. - // TODO(skal): improve criterion. - best_alpha = MBAnalyzeBestIntra4Mode(it, best_alpha); - } - best_uv_alpha = MBAnalyzeBestUVMode(it); - - // Final susceptibility mix - best_alpha = (3 * best_alpha + best_uv_alpha + 2) >> 2; - best_alpha = FinalAlphaValue(best_alpha); - alphas[best_alpha]++; - it->mb_->alpha_ = best_alpha; // for later remapping. - - // Accumulate for later complexity analysis. - *alpha += best_alpha; // mixed susceptibility (not just luma) - *uv_alpha += best_uv_alpha; -} - -static void DefaultMBInfo(VP8MBInfo* const mb) { - mb->type_ = 1; // I16x16 - mb->uv_mode_ = 0; - mb->skip_ = 0; // not skipped - mb->segment_ = 0; // default segment - mb->alpha_ = 0; -} - -//------------------------------------------------------------------------------ -// Main analysis loop: -// Collect all susceptibilities for each macroblock and record their -// distribution in alphas[]. Segments is assigned a-posteriori, based on -// this histogram. -// We also pick an intra16 prediction mode, which shouldn't be considered -// final except for fast-encode settings. We can also pick some intra4 modes -// and decide intra4/intra16, but that's usually almost always a bad choice at -// this stage. - -static void ResetAllMBInfo(VP8Encoder* const enc) { - int n; - for (n = 0; n < enc->mb_w_ * enc->mb_h_; ++n) { - DefaultMBInfo(&enc->mb_info_[n]); - } - // Default susceptibilities. - enc->dqm_[0].alpha_ = 0; - enc->dqm_[0].beta_ = 0; - // Note: we can't compute this alpha_ / uv_alpha_ -> set to default value. - enc->alpha_ = 0; - enc->uv_alpha_ = 0; - WebPReportProgress(enc->pic_, enc->percent_ + 20, &enc->percent_); -} - -// struct used to collect job result -typedef struct { - WebPWorker worker; - int alphas[MAX_ALPHA + 1]; - int alpha, uv_alpha; - VP8EncIterator it; - int delta_progress; -} SegmentJob; - -// main work call -static int DoSegmentsJob(SegmentJob* const job, VP8EncIterator* const it) { - int ok = 1; - if (!VP8IteratorIsDone(it)) { - uint8_t tmp[32 + WEBP_ALIGN_CST]; - uint8_t* const scratch = (uint8_t*)WEBP_ALIGN(tmp); - do { - // Let's pretend we have perfect lossless reconstruction. - VP8IteratorImport(it, scratch); - MBAnalyze(it, job->alphas, &job->alpha, &job->uv_alpha); - ok = VP8IteratorProgress(it, job->delta_progress); - } while (ok && VP8IteratorNext(it)); - } - return ok; -} - -static void MergeJobs(const SegmentJob* const src, SegmentJob* const dst) { - int i; - for (i = 0; i <= MAX_ALPHA; ++i) dst->alphas[i] += src->alphas[i]; - dst->alpha += src->alpha; - dst->uv_alpha += src->uv_alpha; -} - -// initialize the job struct with some TODOs -static void InitSegmentJob(VP8Encoder* const enc, SegmentJob* const job, - int start_row, int end_row) { - WebPGetWorkerInterface()->Init(&job->worker); - job->worker.data1 = job; - job->worker.data2 = &job->it; - job->worker.hook = (WebPWorkerHook)DoSegmentsJob; - VP8IteratorInit(enc, &job->it); - VP8IteratorSetRow(&job->it, start_row); - VP8IteratorSetCountDown(&job->it, (end_row - start_row) * enc->mb_w_); - memset(job->alphas, 0, sizeof(job->alphas)); - job->alpha = 0; - job->uv_alpha = 0; - // only one of both jobs can record the progress, since we don't - // expect the user's hook to be multi-thread safe - job->delta_progress = (start_row == 0) ? 20 : 0; -} - -// main entry point -int VP8EncAnalyze(VP8Encoder* const enc) { - int ok = 1; - const int do_segments = - enc->config_->emulate_jpeg_size || // We need the complexity evaluation. - (enc->segment_hdr_.num_segments_ > 1) || - (enc->method_ == 0); // for method 0, we need preds_[] to be filled. - if (do_segments) { - const int last_row = enc->mb_h_; - // We give a little more than a half work to the main thread. - const int split_row = (9 * last_row + 15) >> 4; - const int total_mb = last_row * enc->mb_w_; -#ifdef WEBP_USE_THREAD - const int kMinSplitRow = 2; // minimal rows needed for mt to be worth it - const int do_mt = (enc->thread_level_ > 0) && (split_row >= kMinSplitRow); -#else - const int do_mt = 0; -#endif - const WebPWorkerInterface* const worker_interface = - WebPGetWorkerInterface(); - SegmentJob main_job; - if (do_mt) { - SegmentJob side_job; - // Note the use of '&' instead of '&&' because we must call the functions - // no matter what. - InitSegmentJob(enc, &main_job, 0, split_row); - InitSegmentJob(enc, &side_job, split_row, last_row); - // we don't need to call Reset() on main_job.worker, since we're calling - // WebPWorkerExecute() on it - ok &= worker_interface->Reset(&side_job.worker); - // launch the two jobs in parallel - if (ok) { - worker_interface->Launch(&side_job.worker); - worker_interface->Execute(&main_job.worker); - ok &= worker_interface->Sync(&side_job.worker); - ok &= worker_interface->Sync(&main_job.worker); - } - worker_interface->End(&side_job.worker); - if (ok) MergeJobs(&side_job, &main_job); // merge results together - } else { - // Even for single-thread case, we use the generic Worker tools. - InitSegmentJob(enc, &main_job, 0, last_row); - worker_interface->Execute(&main_job.worker); - ok &= worker_interface->Sync(&main_job.worker); - } - worker_interface->End(&main_job.worker); - if (ok) { - enc->alpha_ = main_job.alpha / total_mb; - enc->uv_alpha_ = main_job.uv_alpha / total_mb; - AssignSegments(enc, main_job.alphas); - } - } else { // Use only one default segment. - ResetAllMBInfo(enc); - } - return ok; -} - diff --git a/thirdparty/libwebp/enc/analysis_enc.c b/thirdparty/libwebp/enc/analysis_enc.c new file mode 100644 index 0000000000..dce159b316 --- /dev/null +++ b/thirdparty/libwebp/enc/analysis_enc.c @@ -0,0 +1,533 @@ +// Copyright 2011 Google Inc. All Rights Reserved. +// +// Use of this source code is governed by a BSD-style license +// that can be found in the COPYING file in the root of the source +// tree. An additional intellectual property rights grant can be found +// in the file PATENTS. All contributing project authors may +// be found in the AUTHORS file in the root of the source tree. +// ----------------------------------------------------------------------------- +// +// Macroblock analysis +// +// Author: Skal (pascal.massimino@gmail.com) + +#include +#include +#include + +#include "./vp8i_enc.h" +#include "./cost_enc.h" +#include "../utils/utils.h" + +#define MAX_ITERS_K_MEANS 6 + +//------------------------------------------------------------------------------ +// Smooth the segment map by replacing isolated block by the majority of its +// neighbours. + +static void SmoothSegmentMap(VP8Encoder* const enc) { + int n, x, y; + const int w = enc->mb_w_; + const int h = enc->mb_h_; + const int majority_cnt_3_x_3_grid = 5; + uint8_t* const tmp = (uint8_t*)WebPSafeMalloc(w * h, sizeof(*tmp)); + assert((uint64_t)(w * h) == (uint64_t)w * h); // no overflow, as per spec + + if (tmp == NULL) return; + for (y = 1; y < h - 1; ++y) { + for (x = 1; x < w - 1; ++x) { + int cnt[NUM_MB_SEGMENTS] = { 0 }; + const VP8MBInfo* const mb = &enc->mb_info_[x + w * y]; + int majority_seg = mb->segment_; + // Check the 8 neighbouring segment values. + cnt[mb[-w - 1].segment_]++; // top-left + cnt[mb[-w + 0].segment_]++; // top + cnt[mb[-w + 1].segment_]++; // top-right + cnt[mb[ - 1].segment_]++; // left + cnt[mb[ + 1].segment_]++; // right + cnt[mb[ w - 1].segment_]++; // bottom-left + cnt[mb[ w + 0].segment_]++; // bottom + cnt[mb[ w + 1].segment_]++; // bottom-right + for (n = 0; n < NUM_MB_SEGMENTS; ++n) { + if (cnt[n] >= majority_cnt_3_x_3_grid) { + majority_seg = n; + break; + } + } + tmp[x + y * w] = majority_seg; + } + } + for (y = 1; y < h - 1; ++y) { + for (x = 1; x < w - 1; ++x) { + VP8MBInfo* const mb = &enc->mb_info_[x + w * y]; + mb->segment_ = tmp[x + y * w]; + } + } + WebPSafeFree(tmp); +} + +//------------------------------------------------------------------------------ +// set segment susceptibility alpha_ / beta_ + +static WEBP_INLINE int clip(int v, int m, int M) { + return (v < m) ? m : (v > M) ? M : v; +} + +static void SetSegmentAlphas(VP8Encoder* const enc, + const int centers[NUM_MB_SEGMENTS], + int mid) { + const int nb = enc->segment_hdr_.num_segments_; + int min = centers[0], max = centers[0]; + int n; + + if (nb > 1) { + for (n = 0; n < nb; ++n) { + if (min > centers[n]) min = centers[n]; + if (max < centers[n]) max = centers[n]; + } + } + if (max == min) max = min + 1; + assert(mid <= max && mid >= min); + for (n = 0; n < nb; ++n) { + const int alpha = 255 * (centers[n] - mid) / (max - min); + const int beta = 255 * (centers[n] - min) / (max - min); + enc->dqm_[n].alpha_ = clip(alpha, -127, 127); + enc->dqm_[n].beta_ = clip(beta, 0, 255); + } +} + +//------------------------------------------------------------------------------ +// Compute susceptibility based on DCT-coeff histograms: +// the higher, the "easier" the macroblock is to compress. + +#define MAX_ALPHA 255 // 8b of precision for susceptibilities. +#define ALPHA_SCALE (2 * MAX_ALPHA) // scaling factor for alpha. +#define DEFAULT_ALPHA (-1) +#define IS_BETTER_ALPHA(alpha, best_alpha) ((alpha) > (best_alpha)) + +static int FinalAlphaValue(int alpha) { + alpha = MAX_ALPHA - alpha; + return clip(alpha, 0, MAX_ALPHA); +} + +static int GetAlpha(const VP8Histogram* const histo) { + // 'alpha' will later be clipped to [0..MAX_ALPHA] range, clamping outer + // values which happen to be mostly noise. This leaves the maximum precision + // for handling the useful small values which contribute most. + const int max_value = histo->max_value; + const int last_non_zero = histo->last_non_zero; + const int alpha = + (max_value > 1) ? ALPHA_SCALE * last_non_zero / max_value : 0; + return alpha; +} + +static void InitHistogram(VP8Histogram* const histo) { + histo->max_value = 0; + histo->last_non_zero = 1; +} + +static void MergeHistograms(const VP8Histogram* const in, + VP8Histogram* const out) { + if (in->max_value > out->max_value) { + out->max_value = in->max_value; + } + if (in->last_non_zero > out->last_non_zero) { + out->last_non_zero = in->last_non_zero; + } +} + +//------------------------------------------------------------------------------ +// Simplified k-Means, to assign Nb segments based on alpha-histogram + +static void AssignSegments(VP8Encoder* const enc, + const int alphas[MAX_ALPHA + 1]) { + // 'num_segments_' is previously validated and <= NUM_MB_SEGMENTS, but an + // explicit check is needed to avoid spurious warning about 'n + 1' exceeding + // array bounds of 'centers' with some compilers (noticed with gcc-4.9). + const int nb = (enc->segment_hdr_.num_segments_ < NUM_MB_SEGMENTS) ? + enc->segment_hdr_.num_segments_ : NUM_MB_SEGMENTS; + int centers[NUM_MB_SEGMENTS]; + int weighted_average = 0; + int map[MAX_ALPHA + 1]; + int a, n, k; + int min_a = 0, max_a = MAX_ALPHA, range_a; + // 'int' type is ok for histo, and won't overflow + int accum[NUM_MB_SEGMENTS], dist_accum[NUM_MB_SEGMENTS]; + + assert(nb >= 1); + assert(nb <= NUM_MB_SEGMENTS); + + // bracket the input + for (n = 0; n <= MAX_ALPHA && alphas[n] == 0; ++n) {} + min_a = n; + for (n = MAX_ALPHA; n > min_a && alphas[n] == 0; --n) {} + max_a = n; + range_a = max_a - min_a; + + // Spread initial centers evenly + for (k = 0, n = 1; k < nb; ++k, n += 2) { + assert(n < 2 * nb); + centers[k] = min_a + (n * range_a) / (2 * nb); + } + + for (k = 0; k < MAX_ITERS_K_MEANS; ++k) { // few iters are enough + int total_weight; + int displaced; + // Reset stats + for (n = 0; n < nb; ++n) { + accum[n] = 0; + dist_accum[n] = 0; + } + // Assign nearest center for each 'a' + n = 0; // track the nearest center for current 'a' + for (a = min_a; a <= max_a; ++a) { + if (alphas[a]) { + while (n + 1 < nb && abs(a - centers[n + 1]) < abs(a - centers[n])) { + n++; + } + map[a] = n; + // accumulate contribution into best centroid + dist_accum[n] += a * alphas[a]; + accum[n] += alphas[a]; + } + } + // All point are classified. Move the centroids to the + // center of their respective cloud. + displaced = 0; + weighted_average = 0; + total_weight = 0; + for (n = 0; n < nb; ++n) { + if (accum[n]) { + const int new_center = (dist_accum[n] + accum[n] / 2) / accum[n]; + displaced += abs(centers[n] - new_center); + centers[n] = new_center; + weighted_average += new_center * accum[n]; + total_weight += accum[n]; + } + } + weighted_average = (weighted_average + total_weight / 2) / total_weight; + if (displaced < 5) break; // no need to keep on looping... + } + + // Map each original value to the closest centroid + for (n = 0; n < enc->mb_w_ * enc->mb_h_; ++n) { + VP8MBInfo* const mb = &enc->mb_info_[n]; + const int alpha = mb->alpha_; + mb->segment_ = map[alpha]; + mb->alpha_ = centers[map[alpha]]; // for the record. + } + + if (nb > 1) { + const int smooth = (enc->config_->preprocessing & 1); + if (smooth) SmoothSegmentMap(enc); + } + + SetSegmentAlphas(enc, centers, weighted_average); // pick some alphas. +} + +//------------------------------------------------------------------------------ +// Macroblock analysis: collect histogram for each mode, deduce the maximal +// susceptibility and set best modes for this macroblock. +// Segment assignment is done later. + +// Number of modes to inspect for alpha_ evaluation. We don't need to test all +// the possible modes during the analysis phase: we risk falling into a local +// optimum, or be subject to boundary effect +#define MAX_INTRA16_MODE 2 +#define MAX_INTRA4_MODE 2 +#define MAX_UV_MODE 2 + +static int MBAnalyzeBestIntra16Mode(VP8EncIterator* const it) { + const int max_mode = MAX_INTRA16_MODE; + int mode; + int best_alpha = DEFAULT_ALPHA; + int best_mode = 0; + + VP8MakeLuma16Preds(it); + for (mode = 0; mode < max_mode; ++mode) { + VP8Histogram histo; + int alpha; + + InitHistogram(&histo); + VP8CollectHistogram(it->yuv_in_ + Y_OFF_ENC, + it->yuv_p_ + VP8I16ModeOffsets[mode], + 0, 16, &histo); + alpha = GetAlpha(&histo); + if (IS_BETTER_ALPHA(alpha, best_alpha)) { + best_alpha = alpha; + best_mode = mode; + } + } + VP8SetIntra16Mode(it, best_mode); + return best_alpha; +} + +static int FastMBAnalyze(VP8EncIterator* const it) { + // Empirical cut-off value, should be around 16 (~=block size). We use the + // [8-17] range and favor intra4 at high quality, intra16 for low quality. + const int q = (int)it->enc_->config_->quality; + const uint32_t kThreshold = 8 + (17 - 8) * q / 100; + int k; + uint32_t dc[16], m, m2; + for (k = 0; k < 16; k += 4) { + VP8Mean16x4(it->yuv_in_ + Y_OFF_ENC + k * BPS, &dc[k]); + } + for (m = 0, m2 = 0, k = 0; k < 16; ++k) { + m += dc[k]; + m2 += dc[k] * dc[k]; + } + if (kThreshold * m2 < m * m) { + VP8SetIntra16Mode(it, 0); // DC16 + } else { + const uint8_t modes[16] = { 0 }; // DC4 + VP8SetIntra4Mode(it, modes); + } + return 0; +} + +static int MBAnalyzeBestIntra4Mode(VP8EncIterator* const it, + int best_alpha) { + uint8_t modes[16]; + const int max_mode = MAX_INTRA4_MODE; + int i4_alpha; + VP8Histogram total_histo; + int cur_histo = 0; + InitHistogram(&total_histo); + + VP8IteratorStartI4(it); + do { + int mode; + int best_mode_alpha = DEFAULT_ALPHA; + VP8Histogram histos[2]; + const uint8_t* const src = it->yuv_in_ + Y_OFF_ENC + VP8Scan[it->i4_]; + + VP8MakeIntra4Preds(it); + for (mode = 0; mode < max_mode; ++mode) { + int alpha; + + InitHistogram(&histos[cur_histo]); + VP8CollectHistogram(src, it->yuv_p_ + VP8I4ModeOffsets[mode], + 0, 1, &histos[cur_histo]); + alpha = GetAlpha(&histos[cur_histo]); + if (IS_BETTER_ALPHA(alpha, best_mode_alpha)) { + best_mode_alpha = alpha; + modes[it->i4_] = mode; + cur_histo ^= 1; // keep track of best histo so far. + } + } + // accumulate best histogram + MergeHistograms(&histos[cur_histo ^ 1], &total_histo); + // Note: we reuse the original samples for predictors + } while (VP8IteratorRotateI4(it, it->yuv_in_ + Y_OFF_ENC)); + + i4_alpha = GetAlpha(&total_histo); + if (IS_BETTER_ALPHA(i4_alpha, best_alpha)) { + VP8SetIntra4Mode(it, modes); + best_alpha = i4_alpha; + } + return best_alpha; +} + +static int MBAnalyzeBestUVMode(VP8EncIterator* const it) { + int best_alpha = DEFAULT_ALPHA; + int smallest_alpha = 0; + int best_mode = 0; + const int max_mode = MAX_UV_MODE; + int mode; + + VP8MakeChroma8Preds(it); + for (mode = 0; mode < max_mode; ++mode) { + VP8Histogram histo; + int alpha; + InitHistogram(&histo); + VP8CollectHistogram(it->yuv_in_ + U_OFF_ENC, + it->yuv_p_ + VP8UVModeOffsets[mode], + 16, 16 + 4 + 4, &histo); + alpha = GetAlpha(&histo); + if (IS_BETTER_ALPHA(alpha, best_alpha)) { + best_alpha = alpha; + } + // The best prediction mode tends to be the one with the smallest alpha. + if (mode == 0 || alpha < smallest_alpha) { + smallest_alpha = alpha; + best_mode = mode; + } + } + VP8SetIntraUVMode(it, best_mode); + return best_alpha; +} + +static void MBAnalyze(VP8EncIterator* const it, + int alphas[MAX_ALPHA + 1], + int* const alpha, int* const uv_alpha) { + const VP8Encoder* const enc = it->enc_; + int best_alpha, best_uv_alpha; + + VP8SetIntra16Mode(it, 0); // default: Intra16, DC_PRED + VP8SetSkip(it, 0); // not skipped + VP8SetSegment(it, 0); // default segment, spec-wise. + + if (enc->method_ <= 1) { + best_alpha = FastMBAnalyze(it); + } else { + best_alpha = MBAnalyzeBestIntra16Mode(it); + if (enc->method_ >= 5) { + // We go and make a fast decision for intra4/intra16. + // It's usually not a good and definitive pick, but helps seeding the + // stats about level bit-cost. + // TODO(skal): improve criterion. + best_alpha = MBAnalyzeBestIntra4Mode(it, best_alpha); + } + } + best_uv_alpha = MBAnalyzeBestUVMode(it); + + // Final susceptibility mix + best_alpha = (3 * best_alpha + best_uv_alpha + 2) >> 2; + best_alpha = FinalAlphaValue(best_alpha); + alphas[best_alpha]++; + it->mb_->alpha_ = best_alpha; // for later remapping. + + // Accumulate for later complexity analysis. + *alpha += best_alpha; // mixed susceptibility (not just luma) + *uv_alpha += best_uv_alpha; +} + +static void DefaultMBInfo(VP8MBInfo* const mb) { + mb->type_ = 1; // I16x16 + mb->uv_mode_ = 0; + mb->skip_ = 0; // not skipped + mb->segment_ = 0; // default segment + mb->alpha_ = 0; +} + +//------------------------------------------------------------------------------ +// Main analysis loop: +// Collect all susceptibilities for each macroblock and record their +// distribution in alphas[]. Segments is assigned a-posteriori, based on +// this histogram. +// We also pick an intra16 prediction mode, which shouldn't be considered +// final except for fast-encode settings. We can also pick some intra4 modes +// and decide intra4/intra16, but that's usually almost always a bad choice at +// this stage. + +static void ResetAllMBInfo(VP8Encoder* const enc) { + int n; + for (n = 0; n < enc->mb_w_ * enc->mb_h_; ++n) { + DefaultMBInfo(&enc->mb_info_[n]); + } + // Default susceptibilities. + enc->dqm_[0].alpha_ = 0; + enc->dqm_[0].beta_ = 0; + // Note: we can't compute this alpha_ / uv_alpha_ -> set to default value. + enc->alpha_ = 0; + enc->uv_alpha_ = 0; + WebPReportProgress(enc->pic_, enc->percent_ + 20, &enc->percent_); +} + +// struct used to collect job result +typedef struct { + WebPWorker worker; + int alphas[MAX_ALPHA + 1]; + int alpha, uv_alpha; + VP8EncIterator it; + int delta_progress; +} SegmentJob; + +// main work call +static int DoSegmentsJob(SegmentJob* const job, VP8EncIterator* const it) { + int ok = 1; + if (!VP8IteratorIsDone(it)) { + uint8_t tmp[32 + WEBP_ALIGN_CST]; + uint8_t* const scratch = (uint8_t*)WEBP_ALIGN(tmp); + do { + // Let's pretend we have perfect lossless reconstruction. + VP8IteratorImport(it, scratch); + MBAnalyze(it, job->alphas, &job->alpha, &job->uv_alpha); + ok = VP8IteratorProgress(it, job->delta_progress); + } while (ok && VP8IteratorNext(it)); + } + return ok; +} + +static void MergeJobs(const SegmentJob* const src, SegmentJob* const dst) { + int i; + for (i = 0; i <= MAX_ALPHA; ++i) dst->alphas[i] += src->alphas[i]; + dst->alpha += src->alpha; + dst->uv_alpha += src->uv_alpha; +} + +// initialize the job struct with some TODOs +static void InitSegmentJob(VP8Encoder* const enc, SegmentJob* const job, + int start_row, int end_row) { + WebPGetWorkerInterface()->Init(&job->worker); + job->worker.data1 = job; + job->worker.data2 = &job->it; + job->worker.hook = (WebPWorkerHook)DoSegmentsJob; + VP8IteratorInit(enc, &job->it); + VP8IteratorSetRow(&job->it, start_row); + VP8IteratorSetCountDown(&job->it, (end_row - start_row) * enc->mb_w_); + memset(job->alphas, 0, sizeof(job->alphas)); + job->alpha = 0; + job->uv_alpha = 0; + // only one of both jobs can record the progress, since we don't + // expect the user's hook to be multi-thread safe + job->delta_progress = (start_row == 0) ? 20 : 0; +} + +// main entry point +int VP8EncAnalyze(VP8Encoder* const enc) { + int ok = 1; + const int do_segments = + enc->config_->emulate_jpeg_size || // We need the complexity evaluation. + (enc->segment_hdr_.num_segments_ > 1) || + (enc->method_ <= 1); // for method 0 - 1, we need preds_[] to be filled. + if (do_segments) { + const int last_row = enc->mb_h_; + // We give a little more than a half work to the main thread. + const int split_row = (9 * last_row + 15) >> 4; + const int total_mb = last_row * enc->mb_w_; +#ifdef WEBP_USE_THREAD + const int kMinSplitRow = 2; // minimal rows needed for mt to be worth it + const int do_mt = (enc->thread_level_ > 0) && (split_row >= kMinSplitRow); +#else + const int do_mt = 0; +#endif + const WebPWorkerInterface* const worker_interface = + WebPGetWorkerInterface(); + SegmentJob main_job; + if (do_mt) { + SegmentJob side_job; + // Note the use of '&' instead of '&&' because we must call the functions + // no matter what. + InitSegmentJob(enc, &main_job, 0, split_row); + InitSegmentJob(enc, &side_job, split_row, last_row); + // we don't need to call Reset() on main_job.worker, since we're calling + // WebPWorkerExecute() on it + ok &= worker_interface->Reset(&side_job.worker); + // launch the two jobs in parallel + if (ok) { + worker_interface->Launch(&side_job.worker); + worker_interface->Execute(&main_job.worker); + ok &= worker_interface->Sync(&side_job.worker); + ok &= worker_interface->Sync(&main_job.worker); + } + worker_interface->End(&side_job.worker); + if (ok) MergeJobs(&side_job, &main_job); // merge results together + } else { + // Even for single-thread case, we use the generic Worker tools. + InitSegmentJob(enc, &main_job, 0, last_row); + worker_interface->Execute(&main_job.worker); + ok &= worker_interface->Sync(&main_job.worker); + } + worker_interface->End(&main_job.worker); + if (ok) { + enc->alpha_ = main_job.alpha / total_mb; + enc->uv_alpha_ = main_job.uv_alpha / total_mb; + AssignSegments(enc, main_job.alphas); + } + } else { // Use only one default segment. + ResetAllMBInfo(enc); + } + return ok; +} + diff --git a/thirdparty/libwebp/enc/backward_references.c b/thirdparty/libwebp/enc/backward_references.c deleted file mode 100644 index 136a24a8c3..0000000000 --- a/thirdparty/libwebp/enc/backward_references.c +++ /dev/null @@ -1,1715 +0,0 @@ -// Copyright 2012 Google Inc. All Rights Reserved. -// -// Use of this source code is governed by a BSD-style license -// that can be found in the COPYING file in the root of the source -// tree. An additional intellectual property rights grant can be found -// in the file PATENTS. All contributing project authors may -// be found in the AUTHORS file in the root of the source tree. -// ----------------------------------------------------------------------------- -// -// Author: Jyrki Alakuijala (jyrki@google.com) -// - -#include -#include - -#include "./backward_references.h" -#include "./histogram.h" -#include "../dsp/lossless.h" -#include "../dsp/dsp.h" -#include "../utils/color_cache.h" -#include "../utils/utils.h" - -#define VALUES_IN_BYTE 256 - -#define MIN_BLOCK_SIZE 256 // minimum block size for backward references - -#define MAX_ENTROPY (1e30f) - -// 1M window (4M bytes) minus 120 special codes for short distances. -#define WINDOW_SIZE_BITS 20 -#define WINDOW_SIZE ((1 << WINDOW_SIZE_BITS) - 120) - -// Bounds for the match length. -#define MIN_LENGTH 2 -// If you change this, you need MAX_LENGTH_BITS + WINDOW_SIZE_BITS <= 32 as it -// is used in VP8LHashChain. -#define MAX_LENGTH_BITS 12 -// We want the max value to be attainable and stored in MAX_LENGTH_BITS bits. -#define MAX_LENGTH ((1 << MAX_LENGTH_BITS) - 1) -#if MAX_LENGTH_BITS + WINDOW_SIZE_BITS > 32 -#error "MAX_LENGTH_BITS + WINDOW_SIZE_BITS > 32" -#endif - -// ----------------------------------------------------------------------------- - -static const uint8_t plane_to_code_lut[128] = { - 96, 73, 55, 39, 23, 13, 5, 1, 255, 255, 255, 255, 255, 255, 255, 255, - 101, 78, 58, 42, 26, 16, 8, 2, 0, 3, 9, 17, 27, 43, 59, 79, - 102, 86, 62, 46, 32, 20, 10, 6, 4, 7, 11, 21, 33, 47, 63, 87, - 105, 90, 70, 52, 37, 28, 18, 14, 12, 15, 19, 29, 38, 53, 71, 91, - 110, 99, 82, 66, 48, 35, 30, 24, 22, 25, 31, 36, 49, 67, 83, 100, - 115, 108, 94, 76, 64, 50, 44, 40, 34, 41, 45, 51, 65, 77, 95, 109, - 118, 113, 103, 92, 80, 68, 60, 56, 54, 57, 61, 69, 81, 93, 104, 114, - 119, 116, 111, 106, 97, 88, 84, 74, 72, 75, 85, 89, 98, 107, 112, 117 -}; - -static int DistanceToPlaneCode(int xsize, int dist) { - const int yoffset = dist / xsize; - const int xoffset = dist - yoffset * xsize; - if (xoffset <= 8 && yoffset < 8) { - return plane_to_code_lut[yoffset * 16 + 8 - xoffset] + 1; - } else if (xoffset > xsize - 8 && yoffset < 7) { - return plane_to_code_lut[(yoffset + 1) * 16 + 8 + (xsize - xoffset)] + 1; - } - return dist + 120; -} - -// Returns the exact index where array1 and array2 are different. For an index -// inferior or equal to best_len_match, the return value just has to be strictly -// inferior to best_len_match. The current behavior is to return 0 if this index -// is best_len_match, and the index itself otherwise. -// If no two elements are the same, it returns max_limit. -static WEBP_INLINE int FindMatchLength(const uint32_t* const array1, - const uint32_t* const array2, - int best_len_match, int max_limit) { - // Before 'expensive' linear match, check if the two arrays match at the - // current best length index. - if (array1[best_len_match] != array2[best_len_match]) return 0; - - return VP8LVectorMismatch(array1, array2, max_limit); -} - -// ----------------------------------------------------------------------------- -// VP8LBackwardRefs - -struct PixOrCopyBlock { - PixOrCopyBlock* next_; // next block (or NULL) - PixOrCopy* start_; // data start - int size_; // currently used size -}; - -static void ClearBackwardRefs(VP8LBackwardRefs* const refs) { - assert(refs != NULL); - if (refs->tail_ != NULL) { - *refs->tail_ = refs->free_blocks_; // recycle all blocks at once - } - refs->free_blocks_ = refs->refs_; - refs->tail_ = &refs->refs_; - refs->last_block_ = NULL; - refs->refs_ = NULL; -} - -void VP8LBackwardRefsClear(VP8LBackwardRefs* const refs) { - assert(refs != NULL); - ClearBackwardRefs(refs); - while (refs->free_blocks_ != NULL) { - PixOrCopyBlock* const next = refs->free_blocks_->next_; - WebPSafeFree(refs->free_blocks_); - refs->free_blocks_ = next; - } -} - -void VP8LBackwardRefsInit(VP8LBackwardRefs* const refs, int block_size) { - assert(refs != NULL); - memset(refs, 0, sizeof(*refs)); - refs->tail_ = &refs->refs_; - refs->block_size_ = - (block_size < MIN_BLOCK_SIZE) ? MIN_BLOCK_SIZE : block_size; -} - -VP8LRefsCursor VP8LRefsCursorInit(const VP8LBackwardRefs* const refs) { - VP8LRefsCursor c; - c.cur_block_ = refs->refs_; - if (refs->refs_ != NULL) { - c.cur_pos = c.cur_block_->start_; - c.last_pos_ = c.cur_pos + c.cur_block_->size_; - } else { - c.cur_pos = NULL; - c.last_pos_ = NULL; - } - return c; -} - -void VP8LRefsCursorNextBlock(VP8LRefsCursor* const c) { - PixOrCopyBlock* const b = c->cur_block_->next_; - c->cur_pos = (b == NULL) ? NULL : b->start_; - c->last_pos_ = (b == NULL) ? NULL : b->start_ + b->size_; - c->cur_block_ = b; -} - -// Create a new block, either from the free list or allocated -static PixOrCopyBlock* BackwardRefsNewBlock(VP8LBackwardRefs* const refs) { - PixOrCopyBlock* b = refs->free_blocks_; - if (b == NULL) { // allocate new memory chunk - const size_t total_size = - sizeof(*b) + refs->block_size_ * sizeof(*b->start_); - b = (PixOrCopyBlock*)WebPSafeMalloc(1ULL, total_size); - if (b == NULL) { - refs->error_ |= 1; - return NULL; - } - b->start_ = (PixOrCopy*)((uint8_t*)b + sizeof(*b)); // not always aligned - } else { // recycle from free-list - refs->free_blocks_ = b->next_; - } - *refs->tail_ = b; - refs->tail_ = &b->next_; - refs->last_block_ = b; - b->next_ = NULL; - b->size_ = 0; - return b; -} - -static WEBP_INLINE void BackwardRefsCursorAdd(VP8LBackwardRefs* const refs, - const PixOrCopy v) { - PixOrCopyBlock* b = refs->last_block_; - if (b == NULL || b->size_ == refs->block_size_) { - b = BackwardRefsNewBlock(refs); - if (b == NULL) return; // refs->error_ is set - } - b->start_[b->size_++] = v; -} - -int VP8LBackwardRefsCopy(const VP8LBackwardRefs* const src, - VP8LBackwardRefs* const dst) { - const PixOrCopyBlock* b = src->refs_; - ClearBackwardRefs(dst); - assert(src->block_size_ == dst->block_size_); - while (b != NULL) { - PixOrCopyBlock* const new_b = BackwardRefsNewBlock(dst); - if (new_b == NULL) return 0; // dst->error_ is set - memcpy(new_b->start_, b->start_, b->size_ * sizeof(*b->start_)); - new_b->size_ = b->size_; - b = b->next_; - } - return 1; -} - -// ----------------------------------------------------------------------------- -// Hash chains - -int VP8LHashChainInit(VP8LHashChain* const p, int size) { - assert(p->size_ == 0); - assert(p->offset_length_ == NULL); - assert(size > 0); - p->offset_length_ = - (uint32_t*)WebPSafeMalloc(size, sizeof(*p->offset_length_)); - if (p->offset_length_ == NULL) return 0; - p->size_ = size; - - return 1; -} - -void VP8LHashChainClear(VP8LHashChain* const p) { - assert(p != NULL); - WebPSafeFree(p->offset_length_); - - p->size_ = 0; - p->offset_length_ = NULL; -} - -// ----------------------------------------------------------------------------- - -#define HASH_MULTIPLIER_HI (0xc6a4a793U) -#define HASH_MULTIPLIER_LO (0x5bd1e996U) - -static WEBP_INLINE uint32_t GetPixPairHash64(const uint32_t* const argb) { - uint32_t key; - key = argb[1] * HASH_MULTIPLIER_HI; - key += argb[0] * HASH_MULTIPLIER_LO; - key = key >> (32 - HASH_BITS); - return key; -} - -// Returns the maximum number of hash chain lookups to do for a -// given compression quality. Return value in range [8, 86]. -static int GetMaxItersForQuality(int quality) { - return 8 + (quality * quality) / 128; -} - -static int GetWindowSizeForHashChain(int quality, int xsize) { - const int max_window_size = (quality > 75) ? WINDOW_SIZE - : (quality > 50) ? (xsize << 8) - : (quality > 25) ? (xsize << 6) - : (xsize << 4); - assert(xsize > 0); - return (max_window_size > WINDOW_SIZE) ? WINDOW_SIZE : max_window_size; -} - -static WEBP_INLINE int MaxFindCopyLength(int len) { - return (len < MAX_LENGTH) ? len : MAX_LENGTH; -} - -int VP8LHashChainFill(VP8LHashChain* const p, int quality, - const uint32_t* const argb, int xsize, int ysize) { - const int size = xsize * ysize; - const int iter_max = GetMaxItersForQuality(quality); - const int iter_min = iter_max - quality / 10; - const uint32_t window_size = GetWindowSizeForHashChain(quality, xsize); - int pos; - uint32_t base_position; - int32_t* hash_to_first_index; - // Temporarily use the p->offset_length_ as a hash chain. - int32_t* chain = (int32_t*)p->offset_length_; - assert(p->size_ != 0); - assert(p->offset_length_ != NULL); - - hash_to_first_index = - (int32_t*)WebPSafeMalloc(HASH_SIZE, sizeof(*hash_to_first_index)); - if (hash_to_first_index == NULL) return 0; - - // Set the int32_t array to -1. - memset(hash_to_first_index, 0xff, HASH_SIZE * sizeof(*hash_to_first_index)); - // Fill the chain linking pixels with the same hash. - for (pos = 0; pos < size - 1; ++pos) { - const uint32_t hash_code = GetPixPairHash64(argb + pos); - chain[pos] = hash_to_first_index[hash_code]; - hash_to_first_index[hash_code] = pos; - } - WebPSafeFree(hash_to_first_index); - - // Find the best match interval at each pixel, defined by an offset to the - // pixel and a length. The right-most pixel cannot match anything to the right - // (hence a best length of 0) and the left-most pixel nothing to the left - // (hence an offset of 0). - p->offset_length_[0] = p->offset_length_[size - 1] = 0; - for (base_position = size - 2 < 0 ? 0 : size - 2; base_position > 0;) { - const int max_len = MaxFindCopyLength(size - 1 - base_position); - const uint32_t* const argb_start = argb + base_position; - int iter = iter_max; - int best_length = 0; - uint32_t best_distance = 0; - const int min_pos = - (base_position > window_size) ? base_position - window_size : 0; - const int length_max = (max_len < 256) ? max_len : 256; - uint32_t max_base_position; - - for (pos = chain[base_position]; pos >= min_pos; pos = chain[pos]) { - int curr_length; - if (--iter < 0) { - break; - } - assert(base_position > (uint32_t)pos); - - curr_length = - FindMatchLength(argb + pos, argb_start, best_length, max_len); - if (best_length < curr_length) { - best_length = curr_length; - best_distance = base_position - pos; - // Stop if we have reached the maximum length. Otherwise, make sure - // we have executed a minimum number of iterations depending on the - // quality. - if ((best_length == MAX_LENGTH) || - (curr_length >= length_max && iter < iter_min)) { - break; - } - } - } - // We have the best match but in case the two intervals continue matching - // to the left, we have the best matches for the left-extended pixels. - max_base_position = base_position; - while (1) { - assert(best_length <= MAX_LENGTH); - assert(best_distance <= WINDOW_SIZE); - p->offset_length_[base_position] = - (best_distance << MAX_LENGTH_BITS) | (uint32_t)best_length; - --base_position; - // Stop if we don't have a match or if we are out of bounds. - if (best_distance == 0 || base_position == 0) break; - // Stop if we cannot extend the matching intervals to the left. - if (base_position < best_distance || - argb[base_position - best_distance] != argb[base_position]) { - break; - } - // Stop if we are matching at its limit because there could be a closer - // matching interval with the same maximum length. Then again, if the - // matching interval is as close as possible (best_distance == 1), we will - // never find anything better so let's continue. - if (best_length == MAX_LENGTH && best_distance != 1 && - base_position + MAX_LENGTH < max_base_position) { - break; - } - if (best_length < MAX_LENGTH) { - ++best_length; - max_base_position = base_position; - } - } - } - return 1; -} - -static WEBP_INLINE int HashChainFindOffset(const VP8LHashChain* const p, - const int base_position) { - return p->offset_length_[base_position] >> MAX_LENGTH_BITS; -} - -static WEBP_INLINE int HashChainFindLength(const VP8LHashChain* const p, - const int base_position) { - return p->offset_length_[base_position] & ((1U << MAX_LENGTH_BITS) - 1); -} - -static WEBP_INLINE void HashChainFindCopy(const VP8LHashChain* const p, - int base_position, - int* const offset_ptr, - int* const length_ptr) { - *offset_ptr = HashChainFindOffset(p, base_position); - *length_ptr = HashChainFindLength(p, base_position); -} - -static WEBP_INLINE void AddSingleLiteral(uint32_t pixel, int use_color_cache, - VP8LColorCache* const hashers, - VP8LBackwardRefs* const refs) { - PixOrCopy v; - if (use_color_cache) { - const uint32_t key = VP8LColorCacheGetIndex(hashers, pixel); - if (VP8LColorCacheLookup(hashers, key) == pixel) { - v = PixOrCopyCreateCacheIdx(key); - } else { - v = PixOrCopyCreateLiteral(pixel); - VP8LColorCacheSet(hashers, key, pixel); - } - } else { - v = PixOrCopyCreateLiteral(pixel); - } - BackwardRefsCursorAdd(refs, v); -} - -static int BackwardReferencesRle(int xsize, int ysize, - const uint32_t* const argb, - int cache_bits, VP8LBackwardRefs* const refs) { - const int pix_count = xsize * ysize; - int i, k; - const int use_color_cache = (cache_bits > 0); - VP8LColorCache hashers; - - if (use_color_cache && !VP8LColorCacheInit(&hashers, cache_bits)) { - return 0; - } - ClearBackwardRefs(refs); - // Add first pixel as literal. - AddSingleLiteral(argb[0], use_color_cache, &hashers, refs); - i = 1; - while (i < pix_count) { - const int max_len = MaxFindCopyLength(pix_count - i); - const int kMinLength = 4; - const int rle_len = FindMatchLength(argb + i, argb + i - 1, 0, max_len); - const int prev_row_len = (i < xsize) ? 0 : - FindMatchLength(argb + i, argb + i - xsize, 0, max_len); - if (rle_len >= prev_row_len && rle_len >= kMinLength) { - BackwardRefsCursorAdd(refs, PixOrCopyCreateCopy(1, rle_len)); - // We don't need to update the color cache here since it is always the - // same pixel being copied, and that does not change the color cache - // state. - i += rle_len; - } else if (prev_row_len >= kMinLength) { - BackwardRefsCursorAdd(refs, PixOrCopyCreateCopy(xsize, prev_row_len)); - if (use_color_cache) { - for (k = 0; k < prev_row_len; ++k) { - VP8LColorCacheInsert(&hashers, argb[i + k]); - } - } - i += prev_row_len; - } else { - AddSingleLiteral(argb[i], use_color_cache, &hashers, refs); - i++; - } - } - if (use_color_cache) VP8LColorCacheClear(&hashers); - return !refs->error_; -} - -static int BackwardReferencesLz77(int xsize, int ysize, - const uint32_t* const argb, int cache_bits, - const VP8LHashChain* const hash_chain, - VP8LBackwardRefs* const refs) { - int i; - int i_last_check = -1; - int ok = 0; - int cc_init = 0; - const int use_color_cache = (cache_bits > 0); - const int pix_count = xsize * ysize; - VP8LColorCache hashers; - - if (use_color_cache) { - cc_init = VP8LColorCacheInit(&hashers, cache_bits); - if (!cc_init) goto Error; - } - ClearBackwardRefs(refs); - for (i = 0; i < pix_count;) { - // Alternative#1: Code the pixels starting at 'i' using backward reference. - int offset = 0; - int len = 0; - int j; - HashChainFindCopy(hash_chain, i, &offset, &len); - if (len > MIN_LENGTH + 1) { - const int len_ini = len; - int max_reach = 0; - assert(i + len < pix_count); - // Only start from what we have not checked already. - i_last_check = (i > i_last_check) ? i : i_last_check; - // We know the best match for the current pixel but we try to find the - // best matches for the current pixel AND the next one combined. - // The naive method would use the intervals: - // [i,i+len) + [i+len, length of best match at i+len) - // while we check if we can use: - // [i,j) (where j<=i+len) + [j, length of best match at j) - for (j = i_last_check + 1; j <= i + len_ini; ++j) { - const int len_j = HashChainFindLength(hash_chain, j); - const int reach = - j + (len_j > MIN_LENGTH + 1 ? len_j : 1); // 1 for single literal. - if (reach > max_reach) { - len = j - i; - max_reach = reach; - } - } - } else { - len = 1; - } - // Go with literal or backward reference. - assert(len > 0); - if (len == 1) { - AddSingleLiteral(argb[i], use_color_cache, &hashers, refs); - } else { - BackwardRefsCursorAdd(refs, PixOrCopyCreateCopy(offset, len)); - if (use_color_cache) { - for (j = i; j < i + len; ++j) VP8LColorCacheInsert(&hashers, argb[j]); - } - } - i += len; - } - - ok = !refs->error_; - Error: - if (cc_init) VP8LColorCacheClear(&hashers); - return ok; -} - -// ----------------------------------------------------------------------------- - -typedef struct { - double alpha_[VALUES_IN_BYTE]; - double red_[VALUES_IN_BYTE]; - double blue_[VALUES_IN_BYTE]; - double distance_[NUM_DISTANCE_CODES]; - double* literal_; -} CostModel; - -static int BackwardReferencesTraceBackwards( - int xsize, int ysize, const uint32_t* const argb, int quality, - int cache_bits, const VP8LHashChain* const hash_chain, - VP8LBackwardRefs* const refs); - -static void ConvertPopulationCountTableToBitEstimates( - int num_symbols, const uint32_t population_counts[], double output[]) { - uint32_t sum = 0; - int nonzeros = 0; - int i; - for (i = 0; i < num_symbols; ++i) { - sum += population_counts[i]; - if (population_counts[i] > 0) { - ++nonzeros; - } - } - if (nonzeros <= 1) { - memset(output, 0, num_symbols * sizeof(*output)); - } else { - const double logsum = VP8LFastLog2(sum); - for (i = 0; i < num_symbols; ++i) { - output[i] = logsum - VP8LFastLog2(population_counts[i]); - } - } -} - -static int CostModelBuild(CostModel* const m, int cache_bits, - VP8LBackwardRefs* const refs) { - int ok = 0; - VP8LHistogram* const histo = VP8LAllocateHistogram(cache_bits); - if (histo == NULL) goto Error; - - VP8LHistogramCreate(histo, refs, cache_bits); - - ConvertPopulationCountTableToBitEstimates( - VP8LHistogramNumCodes(histo->palette_code_bits_), - histo->literal_, m->literal_); - ConvertPopulationCountTableToBitEstimates( - VALUES_IN_BYTE, histo->red_, m->red_); - ConvertPopulationCountTableToBitEstimates( - VALUES_IN_BYTE, histo->blue_, m->blue_); - ConvertPopulationCountTableToBitEstimates( - VALUES_IN_BYTE, histo->alpha_, m->alpha_); - ConvertPopulationCountTableToBitEstimates( - NUM_DISTANCE_CODES, histo->distance_, m->distance_); - ok = 1; - - Error: - VP8LFreeHistogram(histo); - return ok; -} - -static WEBP_INLINE double GetLiteralCost(const CostModel* const m, uint32_t v) { - return m->alpha_[v >> 24] + - m->red_[(v >> 16) & 0xff] + - m->literal_[(v >> 8) & 0xff] + - m->blue_[v & 0xff]; -} - -static WEBP_INLINE double GetCacheCost(const CostModel* const m, uint32_t idx) { - const int literal_idx = VALUES_IN_BYTE + NUM_LENGTH_CODES + idx; - return m->literal_[literal_idx]; -} - -static WEBP_INLINE double GetLengthCost(const CostModel* const m, - uint32_t length) { - int code, extra_bits; - VP8LPrefixEncodeBits(length, &code, &extra_bits); - return m->literal_[VALUES_IN_BYTE + code] + extra_bits; -} - -static WEBP_INLINE double GetDistanceCost(const CostModel* const m, - uint32_t distance) { - int code, extra_bits; - VP8LPrefixEncodeBits(distance, &code, &extra_bits); - return m->distance_[code] + extra_bits; -} - -static void AddSingleLiteralWithCostModel(const uint32_t* const argb, - VP8LColorCache* const hashers, - const CostModel* const cost_model, - int idx, int use_color_cache, - double prev_cost, float* const cost, - uint16_t* const dist_array) { - double cost_val = prev_cost; - const uint32_t color = argb[0]; - if (use_color_cache && VP8LColorCacheContains(hashers, color)) { - const double mul0 = 0.68; - const int ix = VP8LColorCacheGetIndex(hashers, color); - cost_val += GetCacheCost(cost_model, ix) * mul0; - } else { - const double mul1 = 0.82; - if (use_color_cache) VP8LColorCacheInsert(hashers, color); - cost_val += GetLiteralCost(cost_model, color) * mul1; - } - if (cost[idx] > cost_val) { - cost[idx] = (float)cost_val; - dist_array[idx] = 1; // only one is inserted. - } -} - -// ----------------------------------------------------------------------------- -// CostManager and interval handling - -// Empirical value to avoid high memory consumption but good for performance. -#define COST_CACHE_INTERVAL_SIZE_MAX 100 - -// To perform backward reference every pixel at index index_ is considered and -// the cost for the MAX_LENGTH following pixels computed. Those following pixels -// at index index_ + k (k from 0 to MAX_LENGTH) have a cost of: -// distance_cost_ at index_ + GetLengthCost(cost_model, k) -// (named cost) (named cached cost) -// and the minimum value is kept. GetLengthCost(cost_model, k) is cached in an -// array of size MAX_LENGTH. -// Instead of performing MAX_LENGTH comparisons per pixel, we keep track of the -// minimal values using intervals, for which lower_ and upper_ bounds are kept. -// An interval is defined by the index_ of the pixel that generated it and -// is only useful in a range of indices from start_ to end_ (exclusive), i.e. -// it contains the minimum value for pixels between start_ and end_. -// Intervals are stored in a linked list and ordered by start_. When a new -// interval has a better minimum, old intervals are split or removed. -typedef struct CostInterval CostInterval; -struct CostInterval { - double lower_; - double upper_; - int start_; - int end_; - double distance_cost_; - int index_; - CostInterval* previous_; - CostInterval* next_; -}; - -// The GetLengthCost(cost_model, k) part of the costs is also bounded for -// efficiency in a set of intervals of a different type. -// If those intervals are small enough, they are not used for comparison and -// written into the costs right away. -typedef struct { - double lower_; // Lower bound of the interval. - double upper_; // Upper bound of the interval. - int start_; - int end_; // Exclusive. - int do_write_; // If !=0, the interval is saved to cost instead of being kept - // for comparison. -} CostCacheInterval; - -// This structure is in charge of managing intervals and costs. -// It caches the different CostCacheInterval, caches the different -// GetLengthCost(cost_model, k) in cost_cache_ and the CostInterval's (whose -// count_ is limited by COST_CACHE_INTERVAL_SIZE_MAX). -#define COST_MANAGER_MAX_FREE_LIST 10 -typedef struct { - CostInterval* head_; - int count_; // The number of stored intervals. - CostCacheInterval* cache_intervals_; - size_t cache_intervals_size_; - double cost_cache_[MAX_LENGTH]; // Contains the GetLengthCost(cost_model, k). - double min_cost_cache_; // The minimum value in cost_cache_[1:]. - double max_cost_cache_; // The maximum value in cost_cache_[1:]. - float* costs_; - uint16_t* dist_array_; - // Most of the time, we only need few intervals -> use a free-list, to avoid - // fragmentation with small allocs in most common cases. - CostInterval intervals_[COST_MANAGER_MAX_FREE_LIST]; - CostInterval* free_intervals_; - // These are regularly malloc'd remains. This list can't grow larger than than - // size COST_CACHE_INTERVAL_SIZE_MAX - COST_MANAGER_MAX_FREE_LIST, note. - CostInterval* recycled_intervals_; - // Buffer used in BackwardReferencesHashChainDistanceOnly to store the ends - // of the intervals that can have impacted the cost at a pixel. - int* interval_ends_; - int interval_ends_size_; -} CostManager; - -static int IsCostCacheIntervalWritable(int start, int end) { - // 100 is the length for which we consider an interval for comparison, and not - // for writing. - // The first intervals are very small and go in increasing size. This constant - // helps merging them into one big interval (up to index 150/200 usually from - // which intervals start getting much bigger). - // This value is empirical. - return (end - start + 1 < 100); -} - -static void CostIntervalAddToFreeList(CostManager* const manager, - CostInterval* const interval) { - interval->next_ = manager->free_intervals_; - manager->free_intervals_ = interval; -} - -static int CostIntervalIsInFreeList(const CostManager* const manager, - const CostInterval* const interval) { - return (interval >= &manager->intervals_[0] && - interval <= &manager->intervals_[COST_MANAGER_MAX_FREE_LIST - 1]); -} - -static void CostManagerInitFreeList(CostManager* const manager) { - int i; - manager->free_intervals_ = NULL; - for (i = 0; i < COST_MANAGER_MAX_FREE_LIST; ++i) { - CostIntervalAddToFreeList(manager, &manager->intervals_[i]); - } -} - -static void DeleteIntervalList(CostManager* const manager, - const CostInterval* interval) { - while (interval != NULL) { - const CostInterval* const next = interval->next_; - if (!CostIntervalIsInFreeList(manager, interval)) { - WebPSafeFree((void*)interval); - } // else: do nothing - interval = next; - } -} - -static void CostManagerClear(CostManager* const manager) { - if (manager == NULL) return; - - WebPSafeFree(manager->costs_); - WebPSafeFree(manager->cache_intervals_); - WebPSafeFree(manager->interval_ends_); - - // Clear the interval lists. - DeleteIntervalList(manager, manager->head_); - manager->head_ = NULL; - DeleteIntervalList(manager, manager->recycled_intervals_); - manager->recycled_intervals_ = NULL; - - // Reset pointers, count_ and cache_intervals_size_. - memset(manager, 0, sizeof(*manager)); - CostManagerInitFreeList(manager); -} - -static int CostManagerInit(CostManager* const manager, - uint16_t* const dist_array, int pix_count, - const CostModel* const cost_model) { - int i; - const int cost_cache_size = (pix_count > MAX_LENGTH) ? MAX_LENGTH : pix_count; - // This constant is tied to the cost_model we use. - // Empirically, differences between intervals is usually of more than 1. - const double min_cost_diff = 0.1; - - manager->costs_ = NULL; - manager->cache_intervals_ = NULL; - manager->interval_ends_ = NULL; - manager->head_ = NULL; - manager->recycled_intervals_ = NULL; - manager->count_ = 0; - manager->dist_array_ = dist_array; - CostManagerInitFreeList(manager); - - // Fill in the cost_cache_. - manager->cache_intervals_size_ = 1; - manager->cost_cache_[0] = 0; - for (i = 1; i < cost_cache_size; ++i) { - manager->cost_cache_[i] = GetLengthCost(cost_model, i); - // Get an approximation of the number of bound intervals. - if (fabs(manager->cost_cache_[i] - manager->cost_cache_[i - 1]) > - min_cost_diff) { - ++manager->cache_intervals_size_; - } - // Compute the minimum of cost_cache_. - if (i == 1) { - manager->min_cost_cache_ = manager->cost_cache_[1]; - manager->max_cost_cache_ = manager->cost_cache_[1]; - } else if (manager->cost_cache_[i] < manager->min_cost_cache_) { - manager->min_cost_cache_ = manager->cost_cache_[i]; - } else if (manager->cost_cache_[i] > manager->max_cost_cache_) { - manager->max_cost_cache_ = manager->cost_cache_[i]; - } - } - - // With the current cost models, we have 15 intervals, so we are safe by - // setting a maximum of COST_CACHE_INTERVAL_SIZE_MAX. - if (manager->cache_intervals_size_ > COST_CACHE_INTERVAL_SIZE_MAX) { - manager->cache_intervals_size_ = COST_CACHE_INTERVAL_SIZE_MAX; - } - manager->cache_intervals_ = (CostCacheInterval*)WebPSafeMalloc( - manager->cache_intervals_size_, sizeof(*manager->cache_intervals_)); - if (manager->cache_intervals_ == NULL) { - CostManagerClear(manager); - return 0; - } - - // Fill in the cache_intervals_. - { - double cost_prev = -1e38f; // unprobably low initial value - CostCacheInterval* prev = NULL; - CostCacheInterval* cur = manager->cache_intervals_; - const CostCacheInterval* const end = - manager->cache_intervals_ + manager->cache_intervals_size_; - - // Consecutive values in cost_cache_ are compared and if a big enough - // difference is found, a new interval is created and bounded. - for (i = 0; i < cost_cache_size; ++i) { - const double cost_val = manager->cost_cache_[i]; - if (i == 0 || - (fabs(cost_val - cost_prev) > min_cost_diff && cur + 1 < end)) { - if (i > 1) { - const int is_writable = - IsCostCacheIntervalWritable(cur->start_, cur->end_); - // Merge with the previous interval if both are writable. - if (is_writable && cur != manager->cache_intervals_ && - prev->do_write_) { - // Update the previous interval. - prev->end_ = cur->end_; - if (cur->lower_ < prev->lower_) { - prev->lower_ = cur->lower_; - } else if (cur->upper_ > prev->upper_) { - prev->upper_ = cur->upper_; - } - } else { - cur->do_write_ = is_writable; - prev = cur; - ++cur; - } - } - // Initialize an interval. - cur->start_ = i; - cur->do_write_ = 0; - cur->lower_ = cost_val; - cur->upper_ = cost_val; - } else { - // Update the current interval bounds. - if (cost_val < cur->lower_) { - cur->lower_ = cost_val; - } else if (cost_val > cur->upper_) { - cur->upper_ = cost_val; - } - } - cur->end_ = i + 1; - cost_prev = cost_val; - } - manager->cache_intervals_size_ = cur + 1 - manager->cache_intervals_; - } - - manager->costs_ = (float*)WebPSafeMalloc(pix_count, sizeof(*manager->costs_)); - if (manager->costs_ == NULL) { - CostManagerClear(manager); - return 0; - } - // Set the initial costs_ high for every pixel as we will keep the minimum. - for (i = 0; i < pix_count; ++i) manager->costs_[i] = 1e38f; - - // The cost at pixel is influenced by the cost intervals from previous pixels. - // Let us take the specific case where the offset is the same (which actually - // happens a lot in case of uniform regions). - // pixel i contributes to j>i a cost of: offset cost + cost_cache_[j-i] - // pixel i+1 contributes to j>i a cost of: 2*offset cost + cost_cache_[j-i-1] - // pixel i+2 contributes to j>i a cost of: 3*offset cost + cost_cache_[j-i-2] - // and so on. - // A pixel i influences the following length(j) < MAX_LENGTH pixels. What is - // the value of j such that pixel i + j cannot influence any of those pixels? - // This value is such that: - // max of cost_cache_ < j*offset cost + min of cost_cache_ - // (pixel i + j 's cost cannot beat the worst cost given by pixel i). - // This value will be used to optimize the cost computation in - // BackwardReferencesHashChainDistanceOnly. - { - // The offset cost is computed in GetDistanceCost and has a minimum value of - // the minimum in cost_model->distance_. The case where the offset cost is 0 - // will be dealt with differently later so we are only interested in the - // minimum non-zero offset cost. - double offset_cost_min = 0.; - int size; - for (i = 0; i < NUM_DISTANCE_CODES; ++i) { - if (cost_model->distance_[i] != 0) { - if (offset_cost_min == 0.) { - offset_cost_min = cost_model->distance_[i]; - } else if (cost_model->distance_[i] < offset_cost_min) { - offset_cost_min = cost_model->distance_[i]; - } - } - } - // In case all the cost_model->distance_ is 0, the next non-zero cost we - // can have is from the extra bit in GetDistanceCost, hence 1. - if (offset_cost_min < 1.) offset_cost_min = 1.; - - size = 1 + (int)ceil((manager->max_cost_cache_ - manager->min_cost_cache_) / - offset_cost_min); - // Empirically, we usually end up with a value below 100. - if (size > MAX_LENGTH) size = MAX_LENGTH; - - manager->interval_ends_ = - (int*)WebPSafeMalloc(size, sizeof(*manager->interval_ends_)); - if (manager->interval_ends_ == NULL) { - CostManagerClear(manager); - return 0; - } - manager->interval_ends_size_ = size; - } - - return 1; -} - -// Given the distance_cost for pixel 'index', update the cost at pixel 'i' if it -// is smaller than the previously computed value. -static WEBP_INLINE void UpdateCost(CostManager* const manager, int i, int index, - double distance_cost) { - int k = i - index; - double cost_tmp; - assert(k >= 0 && k < MAX_LENGTH); - cost_tmp = distance_cost + manager->cost_cache_[k]; - - if (manager->costs_[i] > cost_tmp) { - manager->costs_[i] = (float)cost_tmp; - manager->dist_array_[i] = k + 1; - } -} - -// Given the distance_cost for pixel 'index', update the cost for all the pixels -// between 'start' and 'end' excluded. -static WEBP_INLINE void UpdateCostPerInterval(CostManager* const manager, - int start, int end, int index, - double distance_cost) { - int i; - for (i = start; i < end; ++i) UpdateCost(manager, i, index, distance_cost); -} - -// Given two intervals, make 'prev' be the previous one of 'next' in 'manager'. -static WEBP_INLINE void ConnectIntervals(CostManager* const manager, - CostInterval* const prev, - CostInterval* const next) { - if (prev != NULL) { - prev->next_ = next; - } else { - manager->head_ = next; - } - - if (next != NULL) next->previous_ = prev; -} - -// Pop an interval in the manager. -static WEBP_INLINE void PopInterval(CostManager* const manager, - CostInterval* const interval) { - CostInterval* const next = interval->next_; - - if (interval == NULL) return; - - ConnectIntervals(manager, interval->previous_, next); - if (CostIntervalIsInFreeList(manager, interval)) { - CostIntervalAddToFreeList(manager, interval); - } else { // recycle regularly malloc'd intervals too - interval->next_ = manager->recycled_intervals_; - manager->recycled_intervals_ = interval; - } - --manager->count_; - assert(manager->count_ >= 0); -} - -// Update the cost at index i by going over all the stored intervals that -// overlap with i. -static WEBP_INLINE void UpdateCostPerIndex(CostManager* const manager, int i) { - CostInterval* current = manager->head_; - - while (current != NULL && current->start_ <= i) { - if (current->end_ <= i) { - // We have an outdated interval, remove it. - CostInterval* next = current->next_; - PopInterval(manager, current); - current = next; - } else { - UpdateCost(manager, i, current->index_, current->distance_cost_); - current = current->next_; - } - } -} - -// Given a current orphan interval and its previous interval, before -// it was orphaned (which can be NULL), set it at the right place in the list -// of intervals using the start_ ordering and the previous interval as a hint. -static WEBP_INLINE void PositionOrphanInterval(CostManager* const manager, - CostInterval* const current, - CostInterval* previous) { - assert(current != NULL); - - if (previous == NULL) previous = manager->head_; - while (previous != NULL && current->start_ < previous->start_) { - previous = previous->previous_; - } - while (previous != NULL && previous->next_ != NULL && - previous->next_->start_ < current->start_) { - previous = previous->next_; - } - - if (previous != NULL) { - ConnectIntervals(manager, current, previous->next_); - } else { - ConnectIntervals(manager, current, manager->head_); - } - ConnectIntervals(manager, previous, current); -} - -// Insert an interval in the list contained in the manager by starting at -// interval_in as a hint. The intervals are sorted by start_ value. -static WEBP_INLINE void InsertInterval(CostManager* const manager, - CostInterval* const interval_in, - double distance_cost, double lower, - double upper, int index, int start, - int end) { - CostInterval* interval_new; - - if (IsCostCacheIntervalWritable(start, end) || - manager->count_ >= COST_CACHE_INTERVAL_SIZE_MAX) { - // Write down the interval if it is too small. - UpdateCostPerInterval(manager, start, end, index, distance_cost); - return; - } - if (manager->free_intervals_ != NULL) { - interval_new = manager->free_intervals_; - manager->free_intervals_ = interval_new->next_; - } else if (manager->recycled_intervals_ != NULL) { - interval_new = manager->recycled_intervals_; - manager->recycled_intervals_ = interval_new->next_; - } else { // malloc for good - interval_new = (CostInterval*)WebPSafeMalloc(1, sizeof(*interval_new)); - if (interval_new == NULL) { - // Write down the interval if we cannot create it. - UpdateCostPerInterval(manager, start, end, index, distance_cost); - return; - } - } - - interval_new->distance_cost_ = distance_cost; - interval_new->lower_ = lower; - interval_new->upper_ = upper; - interval_new->index_ = index; - interval_new->start_ = start; - interval_new->end_ = end; - PositionOrphanInterval(manager, interval_new, interval_in); - - ++manager->count_; -} - -// When an interval has its start_ or end_ modified, it needs to be -// repositioned in the linked list. -static WEBP_INLINE void RepositionInterval(CostManager* const manager, - CostInterval* const interval) { - if (IsCostCacheIntervalWritable(interval->start_, interval->end_)) { - // Maybe interval has been resized and is small enough to be removed. - UpdateCostPerInterval(manager, interval->start_, interval->end_, - interval->index_, interval->distance_cost_); - PopInterval(manager, interval); - return; - } - - // Early exit if interval is at the right spot. - if ((interval->previous_ == NULL || - interval->previous_->start_ <= interval->start_) && - (interval->next_ == NULL || - interval->start_ <= interval->next_->start_)) { - return; - } - - ConnectIntervals(manager, interval->previous_, interval->next_); - PositionOrphanInterval(manager, interval, interval->previous_); -} - -// Given a new cost interval defined by its start at index, its last value and -// distance_cost, add its contributions to the previous intervals and costs. -// If handling the interval or one of its subintervals becomes to heavy, its -// contribution is added to the costs right away. -static WEBP_INLINE void PushInterval(CostManager* const manager, - double distance_cost, int index, - int last) { - size_t i; - CostInterval* interval = manager->head_; - CostInterval* interval_next; - const CostCacheInterval* const cost_cache_intervals = - manager->cache_intervals_; - - for (i = 0; i < manager->cache_intervals_size_ && - cost_cache_intervals[i].start_ < last; - ++i) { - // Define the intersection of the ith interval with the new one. - int start = index + cost_cache_intervals[i].start_; - const int end = index + (cost_cache_intervals[i].end_ > last - ? last - : cost_cache_intervals[i].end_); - const double lower_in = cost_cache_intervals[i].lower_; - const double upper_in = cost_cache_intervals[i].upper_; - const double lower_full_in = distance_cost + lower_in; - const double upper_full_in = distance_cost + upper_in; - - if (cost_cache_intervals[i].do_write_) { - UpdateCostPerInterval(manager, start, end, index, distance_cost); - continue; - } - - for (; interval != NULL && interval->start_ < end && start < end; - interval = interval_next) { - const double lower_full_interval = - interval->distance_cost_ + interval->lower_; - const double upper_full_interval = - interval->distance_cost_ + interval->upper_; - - interval_next = interval->next_; - - // Make sure we have some overlap - if (start >= interval->end_) continue; - - if (lower_full_in >= upper_full_interval) { - // When intervals are represented, the lower, the better. - // [**********************************************************] - // start end - // [----------------------------------] - // interval->start_ interval->end_ - // If we are worse than what we already have, add whatever we have so - // far up to interval. - const int start_new = interval->end_; - InsertInterval(manager, interval, distance_cost, lower_in, upper_in, - index, start, interval->start_); - start = start_new; - continue; - } - - // We know the two intervals intersect. - if (upper_full_in >= lower_full_interval) { - // There is no clear cut on which is best, so let's keep both. - // [*********[*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*]***********] - // start interval->start_ interval->end_ end - // OR - // [*********[*-*-*-*-*-*-*-*-*-*-*-]----------------------] - // start interval->start_ end interval->end_ - const int end_new = (interval->end_ <= end) ? interval->end_ : end; - InsertInterval(manager, interval, distance_cost, lower_in, upper_in, - index, start, end_new); - start = end_new; - } else if (start <= interval->start_ && interval->end_ <= end) { - // [----------------------------------] - // interval->start_ interval->end_ - // [**************************************************************] - // start end - // We can safely remove the old interval as it is fully included. - PopInterval(manager, interval); - } else { - if (interval->start_ <= start && end <= interval->end_) { - // [--------------------------------------------------------------] - // interval->start_ interval->end_ - // [*****************************] - // start end - // We have to split the old interval as it fully contains the new one. - const int end_original = interval->end_; - interval->end_ = start; - InsertInterval(manager, interval, interval->distance_cost_, - interval->lower_, interval->upper_, interval->index_, - end, end_original); - } else if (interval->start_ < start) { - // [------------------------------------] - // interval->start_ interval->end_ - // [*****************************] - // start end - interval->end_ = start; - } else { - // [------------------------------------] - // interval->start_ interval->end_ - // [*****************************] - // start end - interval->start_ = end; - } - - // The interval has been modified, we need to reposition it or write it. - RepositionInterval(manager, interval); - } - } - // Insert the remaining interval from start to end. - InsertInterval(manager, interval, distance_cost, lower_in, upper_in, index, - start, end); - } -} - -static int BackwardReferencesHashChainDistanceOnly( - int xsize, int ysize, const uint32_t* const argb, int quality, - int cache_bits, const VP8LHashChain* const hash_chain, - VP8LBackwardRefs* const refs, uint16_t* const dist_array) { - int i; - int ok = 0; - int cc_init = 0; - const int pix_count = xsize * ysize; - const int use_color_cache = (cache_bits > 0); - const size_t literal_array_size = sizeof(double) * - (NUM_LITERAL_CODES + NUM_LENGTH_CODES + - ((cache_bits > 0) ? (1 << cache_bits) : 0)); - const size_t cost_model_size = sizeof(CostModel) + literal_array_size; - CostModel* const cost_model = - (CostModel*)WebPSafeCalloc(1ULL, cost_model_size); - VP8LColorCache hashers; - const int skip_length = 32 + quality; - const int skip_min_distance_code = 2; - CostManager* cost_manager = - (CostManager*)WebPSafeMalloc(1ULL, sizeof(*cost_manager)); - - if (cost_model == NULL || cost_manager == NULL) goto Error; - - cost_model->literal_ = (double*)(cost_model + 1); - if (use_color_cache) { - cc_init = VP8LColorCacheInit(&hashers, cache_bits); - if (!cc_init) goto Error; - } - - if (!CostModelBuild(cost_model, cache_bits, refs)) { - goto Error; - } - - if (!CostManagerInit(cost_manager, dist_array, pix_count, cost_model)) { - goto Error; - } - - // We loop one pixel at a time, but store all currently best points to - // non-processed locations from this point. - dist_array[0] = 0; - // Add first pixel as literal. - AddSingleLiteralWithCostModel(argb + 0, &hashers, cost_model, 0, - use_color_cache, 0.0, cost_manager->costs_, - dist_array); - - for (i = 1; i < pix_count - 1; ++i) { - int offset = 0, len = 0; - double prev_cost = cost_manager->costs_[i - 1]; - HashChainFindCopy(hash_chain, i, &offset, &len); - if (len >= MIN_LENGTH) { - const int code = DistanceToPlaneCode(xsize, offset); - const double offset_cost = GetDistanceCost(cost_model, code); - const int first_i = i; - int j_max = 0, interval_ends_index = 0; - const int is_offset_zero = (offset_cost == 0.); - - if (!is_offset_zero) { - j_max = (int)ceil( - (cost_manager->max_cost_cache_ - cost_manager->min_cost_cache_) / - offset_cost); - if (j_max < 1) { - j_max = 1; - } else if (j_max > cost_manager->interval_ends_size_ - 1) { - // This could only happen in the case of MAX_LENGTH. - j_max = cost_manager->interval_ends_size_ - 1; - } - } // else j_max is unused anyway. - - // Instead of considering all contributions from a pixel i by calling: - // PushInterval(cost_manager, prev_cost + offset_cost, i, len); - // we optimize these contributions in case offset_cost stays the same for - // consecutive pixels. This describes a set of pixels similar to a - // previous set (e.g. constant color regions). - for (; i < pix_count - 1; ++i) { - int offset_next, len_next; - prev_cost = cost_manager->costs_[i - 1]; - - if (is_offset_zero) { - // No optimization can be made so we just push all of the - // contributions from i. - PushInterval(cost_manager, prev_cost, i, len); - } else { - // j_max is chosen as the smallest j such that: - // max of cost_cache_ < j*offset cost + min of cost_cache_ - // Therefore, the pixel influenced by i-j_max, cannot be influenced - // by i. Only the costs after the end of what i contributed need to be - // updated. cost_manager->interval_ends_ is a circular buffer that - // stores those ends. - const double distance_cost = prev_cost + offset_cost; - int j = cost_manager->interval_ends_[interval_ends_index]; - if (i - first_i <= j_max || - !IsCostCacheIntervalWritable(j, i + len)) { - PushInterval(cost_manager, distance_cost, i, len); - } else { - for (; j < i + len; ++j) { - UpdateCost(cost_manager, j, i, distance_cost); - } - } - // Store the new end in the circular buffer. - assert(interval_ends_index < cost_manager->interval_ends_size_); - cost_manager->interval_ends_[interval_ends_index] = i + len; - if (++interval_ends_index > j_max) interval_ends_index = 0; - } - - // Check whether i is the last pixel to consider, as it is handled - // differently. - if (i + 1 >= pix_count - 1) break; - HashChainFindCopy(hash_chain, i + 1, &offset_next, &len_next); - if (offset_next != offset) break; - len = len_next; - UpdateCostPerIndex(cost_manager, i); - AddSingleLiteralWithCostModel(argb + i, &hashers, cost_model, i, - use_color_cache, prev_cost, - cost_manager->costs_, dist_array); - } - // Submit the last pixel. - UpdateCostPerIndex(cost_manager, i + 1); - - // This if is for speedup only. It roughly doubles the speed, and - // makes compression worse by .1 %. - if (len >= skip_length && code <= skip_min_distance_code) { - // Long copy for short distances, let's skip the middle - // lookups for better copies. - // 1) insert the hashes. - if (use_color_cache) { - int k; - for (k = 0; k < len; ++k) { - VP8LColorCacheInsert(&hashers, argb[i + k]); - } - } - // 2) jump. - { - const int i_next = i + len - 1; // for loop does ++i, thus -1 here. - for (; i <= i_next; ++i) UpdateCostPerIndex(cost_manager, i + 1); - i = i_next; - } - goto next_symbol; - } - if (len > MIN_LENGTH) { - int code_min_length; - double cost_total; - offset = HashChainFindOffset(hash_chain, i); - code_min_length = DistanceToPlaneCode(xsize, offset); - cost_total = prev_cost + - GetDistanceCost(cost_model, code_min_length) + - GetLengthCost(cost_model, 1); - if (cost_manager->costs_[i + 1] > cost_total) { - cost_manager->costs_[i + 1] = (float)cost_total; - dist_array[i + 1] = 2; - } - } - } else { // len < MIN_LENGTH - UpdateCostPerIndex(cost_manager, i + 1); - } - - AddSingleLiteralWithCostModel(argb + i, &hashers, cost_model, i, - use_color_cache, prev_cost, - cost_manager->costs_, dist_array); - - next_symbol: ; - } - // Handle the last pixel. - if (i == (pix_count - 1)) { - AddSingleLiteralWithCostModel( - argb + i, &hashers, cost_model, i, use_color_cache, - cost_manager->costs_[pix_count - 2], cost_manager->costs_, dist_array); - } - - ok = !refs->error_; - Error: - if (cc_init) VP8LColorCacheClear(&hashers); - CostManagerClear(cost_manager); - WebPSafeFree(cost_model); - WebPSafeFree(cost_manager); - return ok; -} - -// We pack the path at the end of *dist_array and return -// a pointer to this part of the array. Example: -// dist_array = [1x2xx3x2] => packed [1x2x1232], chosen_path = [1232] -static void TraceBackwards(uint16_t* const dist_array, - int dist_array_size, - uint16_t** const chosen_path, - int* const chosen_path_size) { - uint16_t* path = dist_array + dist_array_size; - uint16_t* cur = dist_array + dist_array_size - 1; - while (cur >= dist_array) { - const int k = *cur; - --path; - *path = k; - cur -= k; - } - *chosen_path = path; - *chosen_path_size = (int)(dist_array + dist_array_size - path); -} - -static int BackwardReferencesHashChainFollowChosenPath( - const uint32_t* const argb, int cache_bits, - const uint16_t* const chosen_path, int chosen_path_size, - const VP8LHashChain* const hash_chain, VP8LBackwardRefs* const refs) { - const int use_color_cache = (cache_bits > 0); - int ix; - int i = 0; - int ok = 0; - int cc_init = 0; - VP8LColorCache hashers; - - if (use_color_cache) { - cc_init = VP8LColorCacheInit(&hashers, cache_bits); - if (!cc_init) goto Error; - } - - ClearBackwardRefs(refs); - for (ix = 0; ix < chosen_path_size; ++ix) { - const int len = chosen_path[ix]; - if (len != 1) { - int k; - const int offset = HashChainFindOffset(hash_chain, i); - BackwardRefsCursorAdd(refs, PixOrCopyCreateCopy(offset, len)); - if (use_color_cache) { - for (k = 0; k < len; ++k) { - VP8LColorCacheInsert(&hashers, argb[i + k]); - } - } - i += len; - } else { - PixOrCopy v; - if (use_color_cache && VP8LColorCacheContains(&hashers, argb[i])) { - // push pixel as a color cache index - const int idx = VP8LColorCacheGetIndex(&hashers, argb[i]); - v = PixOrCopyCreateCacheIdx(idx); - } else { - if (use_color_cache) VP8LColorCacheInsert(&hashers, argb[i]); - v = PixOrCopyCreateLiteral(argb[i]); - } - BackwardRefsCursorAdd(refs, v); - ++i; - } - } - ok = !refs->error_; - Error: - if (cc_init) VP8LColorCacheClear(&hashers); - return ok; -} - -// Returns 1 on success. -static int BackwardReferencesTraceBackwards( - int xsize, int ysize, const uint32_t* const argb, int quality, - int cache_bits, const VP8LHashChain* const hash_chain, - VP8LBackwardRefs* const refs) { - int ok = 0; - const int dist_array_size = xsize * ysize; - uint16_t* chosen_path = NULL; - int chosen_path_size = 0; - uint16_t* dist_array = - (uint16_t*)WebPSafeMalloc(dist_array_size, sizeof(*dist_array)); - - if (dist_array == NULL) goto Error; - - if (!BackwardReferencesHashChainDistanceOnly( - xsize, ysize, argb, quality, cache_bits, hash_chain, - refs, dist_array)) { - goto Error; - } - TraceBackwards(dist_array, dist_array_size, &chosen_path, &chosen_path_size); - if (!BackwardReferencesHashChainFollowChosenPath( - argb, cache_bits, chosen_path, chosen_path_size, hash_chain, refs)) { - goto Error; - } - ok = 1; - Error: - WebPSafeFree(dist_array); - return ok; -} - -static void BackwardReferences2DLocality(int xsize, - const VP8LBackwardRefs* const refs) { - VP8LRefsCursor c = VP8LRefsCursorInit(refs); - while (VP8LRefsCursorOk(&c)) { - if (PixOrCopyIsCopy(c.cur_pos)) { - const int dist = c.cur_pos->argb_or_distance; - const int transformed_dist = DistanceToPlaneCode(xsize, dist); - c.cur_pos->argb_or_distance = transformed_dist; - } - VP8LRefsCursorNext(&c); - } -} - -// Returns entropy for the given cache bits. -static double ComputeCacheEntropy(const uint32_t* argb, - const VP8LBackwardRefs* const refs, - int cache_bits) { - const int use_color_cache = (cache_bits > 0); - int cc_init = 0; - double entropy = MAX_ENTROPY; - const double kSmallPenaltyForLargeCache = 4.0; - VP8LColorCache hashers; - VP8LRefsCursor c = VP8LRefsCursorInit(refs); - VP8LHistogram* histo = VP8LAllocateHistogram(cache_bits); - if (histo == NULL) goto Error; - - if (use_color_cache) { - cc_init = VP8LColorCacheInit(&hashers, cache_bits); - if (!cc_init) goto Error; - } - if (!use_color_cache) { - while (VP8LRefsCursorOk(&c)) { - VP8LHistogramAddSinglePixOrCopy(histo, c.cur_pos); - VP8LRefsCursorNext(&c); - } - } else { - while (VP8LRefsCursorOk(&c)) { - const PixOrCopy* const v = c.cur_pos; - if (PixOrCopyIsLiteral(v)) { - const uint32_t pix = *argb++; - const uint32_t key = VP8LColorCacheGetIndex(&hashers, pix); - if (VP8LColorCacheLookup(&hashers, key) == pix) { - ++histo->literal_[NUM_LITERAL_CODES + NUM_LENGTH_CODES + key]; - } else { - VP8LColorCacheSet(&hashers, key, pix); - ++histo->blue_[pix & 0xff]; - ++histo->literal_[(pix >> 8) & 0xff]; - ++histo->red_[(pix >> 16) & 0xff]; - ++histo->alpha_[pix >> 24]; - } - } else { - int len = PixOrCopyLength(v); - int code, extra_bits; - VP8LPrefixEncodeBits(len, &code, &extra_bits); - ++histo->literal_[NUM_LITERAL_CODES + code]; - VP8LPrefixEncodeBits(PixOrCopyDistance(v), &code, &extra_bits); - ++histo->distance_[code]; - do { - VP8LColorCacheInsert(&hashers, *argb++); - } while(--len != 0); - } - VP8LRefsCursorNext(&c); - } - } - entropy = VP8LHistogramEstimateBits(histo) + - kSmallPenaltyForLargeCache * cache_bits; - Error: - if (cc_init) VP8LColorCacheClear(&hashers); - VP8LFreeHistogram(histo); - return entropy; -} - -// Evaluate optimal cache bits for the local color cache. -// The input *best_cache_bits sets the maximum cache bits to use (passing 0 -// implies disabling the local color cache). The local color cache is also -// disabled for the lower (<= 25) quality. -// Returns 0 in case of memory error. -static int CalculateBestCacheSize(const uint32_t* const argb, - int xsize, int ysize, int quality, - const VP8LHashChain* const hash_chain, - VP8LBackwardRefs* const refs, - int* const lz77_computed, - int* const best_cache_bits) { - int eval_low = 1; - int eval_high = 1; - double entropy_low = MAX_ENTROPY; - double entropy_high = MAX_ENTROPY; - const double cost_mul = 5e-4; - int cache_bits_low = 0; - int cache_bits_high = (quality <= 25) ? 0 : *best_cache_bits; - - assert(cache_bits_high <= MAX_COLOR_CACHE_BITS); - - *lz77_computed = 0; - if (cache_bits_high == 0) { - *best_cache_bits = 0; - // Local color cache is disabled. - return 1; - } - if (!BackwardReferencesLz77(xsize, ysize, argb, cache_bits_low, hash_chain, - refs)) { - return 0; - } - // Do a binary search to find the optimal entropy for cache_bits. - while (eval_low || eval_high) { - if (eval_low) { - entropy_low = ComputeCacheEntropy(argb, refs, cache_bits_low); - entropy_low += entropy_low * cache_bits_low * cost_mul; - eval_low = 0; - } - if (eval_high) { - entropy_high = ComputeCacheEntropy(argb, refs, cache_bits_high); - entropy_high += entropy_high * cache_bits_high * cost_mul; - eval_high = 0; - } - if (entropy_high < entropy_low) { - const int prev_cache_bits_low = cache_bits_low; - *best_cache_bits = cache_bits_high; - cache_bits_low = (cache_bits_low + cache_bits_high) / 2; - if (cache_bits_low != prev_cache_bits_low) eval_low = 1; - } else { - *best_cache_bits = cache_bits_low; - cache_bits_high = (cache_bits_low + cache_bits_high) / 2; - if (cache_bits_high != cache_bits_low) eval_high = 1; - } - } - *lz77_computed = 1; - return 1; -} - -// Update (in-place) backward references for specified cache_bits. -static int BackwardRefsWithLocalCache(const uint32_t* const argb, - int cache_bits, - VP8LBackwardRefs* const refs) { - int pixel_index = 0; - VP8LColorCache hashers; - VP8LRefsCursor c = VP8LRefsCursorInit(refs); - if (!VP8LColorCacheInit(&hashers, cache_bits)) return 0; - - while (VP8LRefsCursorOk(&c)) { - PixOrCopy* const v = c.cur_pos; - if (PixOrCopyIsLiteral(v)) { - const uint32_t argb_literal = v->argb_or_distance; - if (VP8LColorCacheContains(&hashers, argb_literal)) { - const int ix = VP8LColorCacheGetIndex(&hashers, argb_literal); - *v = PixOrCopyCreateCacheIdx(ix); - } else { - VP8LColorCacheInsert(&hashers, argb_literal); - } - ++pixel_index; - } else { - // refs was created without local cache, so it can not have cache indexes. - int k; - assert(PixOrCopyIsCopy(v)); - for (k = 0; k < v->len; ++k) { - VP8LColorCacheInsert(&hashers, argb[pixel_index++]); - } - } - VP8LRefsCursorNext(&c); - } - VP8LColorCacheClear(&hashers); - return 1; -} - -static VP8LBackwardRefs* GetBackwardReferencesLowEffort( - int width, int height, const uint32_t* const argb, - int* const cache_bits, const VP8LHashChain* const hash_chain, - VP8LBackwardRefs refs_array[2]) { - VP8LBackwardRefs* refs_lz77 = &refs_array[0]; - *cache_bits = 0; - if (!BackwardReferencesLz77(width, height, argb, 0, hash_chain, refs_lz77)) { - return NULL; - } - BackwardReferences2DLocality(width, refs_lz77); - return refs_lz77; -} - -static VP8LBackwardRefs* GetBackwardReferences( - int width, int height, const uint32_t* const argb, int quality, - int* const cache_bits, const VP8LHashChain* const hash_chain, - VP8LBackwardRefs refs_array[2]) { - int lz77_is_useful; - int lz77_computed; - double bit_cost_lz77, bit_cost_rle; - VP8LBackwardRefs* best = NULL; - VP8LBackwardRefs* refs_lz77 = &refs_array[0]; - VP8LBackwardRefs* refs_rle = &refs_array[1]; - VP8LHistogram* histo = NULL; - - if (!CalculateBestCacheSize(argb, width, height, quality, hash_chain, - refs_lz77, &lz77_computed, cache_bits)) { - goto Error; - } - - if (lz77_computed) { - // Transform refs_lz77 for the optimized cache_bits. - if (*cache_bits > 0) { - if (!BackwardRefsWithLocalCache(argb, *cache_bits, refs_lz77)) { - goto Error; - } - } - } else { - if (!BackwardReferencesLz77(width, height, argb, *cache_bits, hash_chain, - refs_lz77)) { - goto Error; - } - } - - if (!BackwardReferencesRle(width, height, argb, *cache_bits, refs_rle)) { - goto Error; - } - - histo = VP8LAllocateHistogram(*cache_bits); - if (histo == NULL) goto Error; - - { - // Evaluate LZ77 coding. - VP8LHistogramCreate(histo, refs_lz77, *cache_bits); - bit_cost_lz77 = VP8LHistogramEstimateBits(histo); - // Evaluate RLE coding. - VP8LHistogramCreate(histo, refs_rle, *cache_bits); - bit_cost_rle = VP8LHistogramEstimateBits(histo); - // Decide if LZ77 is useful. - lz77_is_useful = (bit_cost_lz77 < bit_cost_rle); - } - - // Choose appropriate backward reference. - if (lz77_is_useful) { - // TraceBackwards is costly. Don't execute it at lower quality. - const int try_lz77_trace_backwards = (quality >= 25); - best = refs_lz77; // default guess: lz77 is better - if (try_lz77_trace_backwards) { - VP8LBackwardRefs* const refs_trace = refs_rle; - if (!VP8LBackwardRefsCopy(refs_lz77, refs_trace)) { - best = NULL; - goto Error; - } - if (BackwardReferencesTraceBackwards(width, height, argb, quality, - *cache_bits, hash_chain, - refs_trace)) { - double bit_cost_trace; - // Evaluate LZ77 coding. - VP8LHistogramCreate(histo, refs_trace, *cache_bits); - bit_cost_trace = VP8LHistogramEstimateBits(histo); - if (bit_cost_trace < bit_cost_lz77) { - best = refs_trace; - } - } - } - } else { - best = refs_rle; - } - - BackwardReferences2DLocality(width, best); - - Error: - VP8LFreeHistogram(histo); - return best; -} - -VP8LBackwardRefs* VP8LGetBackwardReferences( - int width, int height, const uint32_t* const argb, int quality, - int low_effort, int* const cache_bits, - const VP8LHashChain* const hash_chain, VP8LBackwardRefs refs_array[2]) { - if (low_effort) { - return GetBackwardReferencesLowEffort(width, height, argb, cache_bits, - hash_chain, refs_array); - } else { - return GetBackwardReferences(width, height, argb, quality, cache_bits, - hash_chain, refs_array); - } -} diff --git a/thirdparty/libwebp/enc/backward_references.h b/thirdparty/libwebp/enc/backward_references.h deleted file mode 100644 index 0cadb11e11..0000000000 --- a/thirdparty/libwebp/enc/backward_references.h +++ /dev/null @@ -1,206 +0,0 @@ -// Copyright 2012 Google Inc. All Rights Reserved. -// -// Use of this source code is governed by a BSD-style license -// that can be found in the COPYING file in the root of the source -// tree. An additional intellectual property rights grant can be found -// in the file PATENTS. All contributing project authors may -// be found in the AUTHORS file in the root of the source tree. -// ----------------------------------------------------------------------------- -// -// Author: Jyrki Alakuijala (jyrki@google.com) -// - -#ifndef WEBP_ENC_BACKWARD_REFERENCES_H_ -#define WEBP_ENC_BACKWARD_REFERENCES_H_ - -#include -#include -#include "../webp/types.h" -#include "../webp/format_constants.h" - -#ifdef __cplusplus -extern "C" { -#endif - -// The maximum allowed limit is 11. -#define MAX_COLOR_CACHE_BITS 10 - -// ----------------------------------------------------------------------------- -// PixOrCopy - -enum Mode { - kLiteral, - kCacheIdx, - kCopy, - kNone -}; - -typedef struct { - // mode as uint8_t to make the memory layout to be exactly 8 bytes. - uint8_t mode; - uint16_t len; - uint32_t argb_or_distance; -} PixOrCopy; - -static WEBP_INLINE PixOrCopy PixOrCopyCreateCopy(uint32_t distance, - uint16_t len) { - PixOrCopy retval; - retval.mode = kCopy; - retval.argb_or_distance = distance; - retval.len = len; - return retval; -} - -static WEBP_INLINE PixOrCopy PixOrCopyCreateCacheIdx(int idx) { - PixOrCopy retval; - assert(idx >= 0); - assert(idx < (1 << MAX_COLOR_CACHE_BITS)); - retval.mode = kCacheIdx; - retval.argb_or_distance = idx; - retval.len = 1; - return retval; -} - -static WEBP_INLINE PixOrCopy PixOrCopyCreateLiteral(uint32_t argb) { - PixOrCopy retval; - retval.mode = kLiteral; - retval.argb_or_distance = argb; - retval.len = 1; - return retval; -} - -static WEBP_INLINE int PixOrCopyIsLiteral(const PixOrCopy* const p) { - return (p->mode == kLiteral); -} - -static WEBP_INLINE int PixOrCopyIsCacheIdx(const PixOrCopy* const p) { - return (p->mode == kCacheIdx); -} - -static WEBP_INLINE int PixOrCopyIsCopy(const PixOrCopy* const p) { - return (p->mode == kCopy); -} - -static WEBP_INLINE uint32_t PixOrCopyLiteral(const PixOrCopy* const p, - int component) { - assert(p->mode == kLiteral); - return (p->argb_or_distance >> (component * 8)) & 0xff; -} - -static WEBP_INLINE uint32_t PixOrCopyLength(const PixOrCopy* const p) { - return p->len; -} - -static WEBP_INLINE uint32_t PixOrCopyArgb(const PixOrCopy* const p) { - assert(p->mode == kLiteral); - return p->argb_or_distance; -} - -static WEBP_INLINE uint32_t PixOrCopyCacheIdx(const PixOrCopy* const p) { - assert(p->mode == kCacheIdx); - assert(p->argb_or_distance < (1U << MAX_COLOR_CACHE_BITS)); - return p->argb_or_distance; -} - -static WEBP_INLINE uint32_t PixOrCopyDistance(const PixOrCopy* const p) { - assert(p->mode == kCopy); - return p->argb_or_distance; -} - -// ----------------------------------------------------------------------------- -// VP8LHashChain - -#define HASH_BITS 18 -#define HASH_SIZE (1 << HASH_BITS) - -typedef struct VP8LHashChain VP8LHashChain; -struct VP8LHashChain { - // The 20 most significant bits contain the offset at which the best match - // is found. These 20 bits are the limit defined by GetWindowSizeForHashChain - // (through WINDOW_SIZE = 1<<20). - // The lower 12 bits contain the length of the match. The 12 bit limit is - // defined in MaxFindCopyLength with MAX_LENGTH=4096. - uint32_t* offset_length_; - // This is the maximum size of the hash_chain that can be constructed. - // Typically this is the pixel count (width x height) for a given image. - int size_; -}; - -// Must be called first, to set size. -int VP8LHashChainInit(VP8LHashChain* const p, int size); -// Pre-compute the best matches for argb. -int VP8LHashChainFill(VP8LHashChain* const p, int quality, - const uint32_t* const argb, int xsize, int ysize); -void VP8LHashChainClear(VP8LHashChain* const p); // release memory - -// ----------------------------------------------------------------------------- -// VP8LBackwardRefs (block-based backward-references storage) - -// maximum number of reference blocks the image will be segmented into -#define MAX_REFS_BLOCK_PER_IMAGE 16 - -typedef struct PixOrCopyBlock PixOrCopyBlock; // forward declaration -typedef struct VP8LBackwardRefs VP8LBackwardRefs; - -// Container for blocks chain -struct VP8LBackwardRefs { - int block_size_; // common block-size - int error_; // set to true if some memory error occurred - PixOrCopyBlock* refs_; // list of currently used blocks - PixOrCopyBlock** tail_; // for list recycling - PixOrCopyBlock* free_blocks_; // free-list - PixOrCopyBlock* last_block_; // used for adding new refs (internal) -}; - -// Initialize the object. 'block_size' is the common block size to store -// references (typically, width * height / MAX_REFS_BLOCK_PER_IMAGE). -void VP8LBackwardRefsInit(VP8LBackwardRefs* const refs, int block_size); -// Release memory for backward references. -void VP8LBackwardRefsClear(VP8LBackwardRefs* const refs); -// Copies the 'src' backward refs to the 'dst'. Returns 0 in case of error. -int VP8LBackwardRefsCopy(const VP8LBackwardRefs* const src, - VP8LBackwardRefs* const dst); - -// Cursor for iterating on references content -typedef struct { - // public: - PixOrCopy* cur_pos; // current position - // private: - PixOrCopyBlock* cur_block_; // current block in the refs list - const PixOrCopy* last_pos_; // sentinel for switching to next block -} VP8LRefsCursor; - -// Returns a cursor positioned at the beginning of the references list. -VP8LRefsCursor VP8LRefsCursorInit(const VP8LBackwardRefs* const refs); -// Returns true if cursor is pointing at a valid position. -static WEBP_INLINE int VP8LRefsCursorOk(const VP8LRefsCursor* const c) { - return (c->cur_pos != NULL); -} -// Move to next block of references. Internal, not to be called directly. -void VP8LRefsCursorNextBlock(VP8LRefsCursor* const c); -// Move to next position, or NULL. Should not be called if !VP8LRefsCursorOk(). -static WEBP_INLINE void VP8LRefsCursorNext(VP8LRefsCursor* const c) { - assert(c != NULL); - assert(VP8LRefsCursorOk(c)); - if (++c->cur_pos == c->last_pos_) VP8LRefsCursorNextBlock(c); -} - -// ----------------------------------------------------------------------------- -// Main entry points - -// Evaluates best possible backward references for specified quality. -// The input cache_bits to 'VP8LGetBackwardReferences' sets the maximum cache -// bits to use (passing 0 implies disabling the local color cache). -// The optimal cache bits is evaluated and set for the *cache_bits parameter. -// The return value is the pointer to the best of the two backward refs viz, -// refs[0] or refs[1]. -VP8LBackwardRefs* VP8LGetBackwardReferences( - int width, int height, const uint32_t* const argb, int quality, - int low_effort, int* const cache_bits, - const VP8LHashChain* const hash_chain, VP8LBackwardRefs refs[2]); - -#ifdef __cplusplus -} -#endif - -#endif // WEBP_ENC_BACKWARD_REFERENCES_H_ diff --git a/thirdparty/libwebp/enc/backward_references_enc.c b/thirdparty/libwebp/enc/backward_references_enc.c new file mode 100644 index 0000000000..7c0559ff1e --- /dev/null +++ b/thirdparty/libwebp/enc/backward_references_enc.c @@ -0,0 +1,1800 @@ +// Copyright 2012 Google Inc. All Rights Reserved. +// +// Use of this source code is governed by a BSD-style license +// that can be found in the COPYING file in the root of the source +// tree. An additional intellectual property rights grant can be found +// in the file PATENTS. All contributing project authors may +// be found in the AUTHORS file in the root of the source tree. +// ----------------------------------------------------------------------------- +// +// Author: Jyrki Alakuijala (jyrki@google.com) +// + +#include +#include + +#include "./backward_references_enc.h" +#include "./histogram_enc.h" +#include "../dsp/lossless.h" +#include "../dsp/lossless_common.h" +#include "../dsp/dsp.h" +#include "../utils/color_cache_utils.h" +#include "../utils/utils.h" + +#define VALUES_IN_BYTE 256 + +#define MIN_BLOCK_SIZE 256 // minimum block size for backward references + +#define MAX_ENTROPY (1e30f) + +// 1M window (4M bytes) minus 120 special codes for short distances. +#define WINDOW_SIZE_BITS 20 +#define WINDOW_SIZE ((1 << WINDOW_SIZE_BITS) - 120) + +// Minimum number of pixels for which it is cheaper to encode a +// distance + length instead of each pixel as a literal. +#define MIN_LENGTH 4 +// If you change this, you need MAX_LENGTH_BITS + WINDOW_SIZE_BITS <= 32 as it +// is used in VP8LHashChain. +#define MAX_LENGTH_BITS 12 +// We want the max value to be attainable and stored in MAX_LENGTH_BITS bits. +#define MAX_LENGTH ((1 << MAX_LENGTH_BITS) - 1) +#if MAX_LENGTH_BITS + WINDOW_SIZE_BITS > 32 +#error "MAX_LENGTH_BITS + WINDOW_SIZE_BITS > 32" +#endif + +// ----------------------------------------------------------------------------- + +static const uint8_t plane_to_code_lut[128] = { + 96, 73, 55, 39, 23, 13, 5, 1, 255, 255, 255, 255, 255, 255, 255, 255, + 101, 78, 58, 42, 26, 16, 8, 2, 0, 3, 9, 17, 27, 43, 59, 79, + 102, 86, 62, 46, 32, 20, 10, 6, 4, 7, 11, 21, 33, 47, 63, 87, + 105, 90, 70, 52, 37, 28, 18, 14, 12, 15, 19, 29, 38, 53, 71, 91, + 110, 99, 82, 66, 48, 35, 30, 24, 22, 25, 31, 36, 49, 67, 83, 100, + 115, 108, 94, 76, 64, 50, 44, 40, 34, 41, 45, 51, 65, 77, 95, 109, + 118, 113, 103, 92, 80, 68, 60, 56, 54, 57, 61, 69, 81, 93, 104, 114, + 119, 116, 111, 106, 97, 88, 84, 74, 72, 75, 85, 89, 98, 107, 112, 117 +}; + +static int DistanceToPlaneCode(int xsize, int dist) { + const int yoffset = dist / xsize; + const int xoffset = dist - yoffset * xsize; + if (xoffset <= 8 && yoffset < 8) { + return plane_to_code_lut[yoffset * 16 + 8 - xoffset] + 1; + } else if (xoffset > xsize - 8 && yoffset < 7) { + return plane_to_code_lut[(yoffset + 1) * 16 + 8 + (xsize - xoffset)] + 1; + } + return dist + 120; +} + +// Returns the exact index where array1 and array2 are different. For an index +// inferior or equal to best_len_match, the return value just has to be strictly +// inferior to best_len_match. The current behavior is to return 0 if this index +// is best_len_match, and the index itself otherwise. +// If no two elements are the same, it returns max_limit. +static WEBP_INLINE int FindMatchLength(const uint32_t* const array1, + const uint32_t* const array2, + int best_len_match, int max_limit) { + // Before 'expensive' linear match, check if the two arrays match at the + // current best length index. + if (array1[best_len_match] != array2[best_len_match]) return 0; + + return VP8LVectorMismatch(array1, array2, max_limit); +} + +// ----------------------------------------------------------------------------- +// VP8LBackwardRefs + +struct PixOrCopyBlock { + PixOrCopyBlock* next_; // next block (or NULL) + PixOrCopy* start_; // data start + int size_; // currently used size +}; + +static void ClearBackwardRefs(VP8LBackwardRefs* const refs) { + assert(refs != NULL); + if (refs->tail_ != NULL) { + *refs->tail_ = refs->free_blocks_; // recycle all blocks at once + } + refs->free_blocks_ = refs->refs_; + refs->tail_ = &refs->refs_; + refs->last_block_ = NULL; + refs->refs_ = NULL; +} + +void VP8LBackwardRefsClear(VP8LBackwardRefs* const refs) { + assert(refs != NULL); + ClearBackwardRefs(refs); + while (refs->free_blocks_ != NULL) { + PixOrCopyBlock* const next = refs->free_blocks_->next_; + WebPSafeFree(refs->free_blocks_); + refs->free_blocks_ = next; + } +} + +void VP8LBackwardRefsInit(VP8LBackwardRefs* const refs, int block_size) { + assert(refs != NULL); + memset(refs, 0, sizeof(*refs)); + refs->tail_ = &refs->refs_; + refs->block_size_ = + (block_size < MIN_BLOCK_SIZE) ? MIN_BLOCK_SIZE : block_size; +} + +VP8LRefsCursor VP8LRefsCursorInit(const VP8LBackwardRefs* const refs) { + VP8LRefsCursor c; + c.cur_block_ = refs->refs_; + if (refs->refs_ != NULL) { + c.cur_pos = c.cur_block_->start_; + c.last_pos_ = c.cur_pos + c.cur_block_->size_; + } else { + c.cur_pos = NULL; + c.last_pos_ = NULL; + } + return c; +} + +void VP8LRefsCursorNextBlock(VP8LRefsCursor* const c) { + PixOrCopyBlock* const b = c->cur_block_->next_; + c->cur_pos = (b == NULL) ? NULL : b->start_; + c->last_pos_ = (b == NULL) ? NULL : b->start_ + b->size_; + c->cur_block_ = b; +} + +// Create a new block, either from the free list or allocated +static PixOrCopyBlock* BackwardRefsNewBlock(VP8LBackwardRefs* const refs) { + PixOrCopyBlock* b = refs->free_blocks_; + if (b == NULL) { // allocate new memory chunk + const size_t total_size = + sizeof(*b) + refs->block_size_ * sizeof(*b->start_); + b = (PixOrCopyBlock*)WebPSafeMalloc(1ULL, total_size); + if (b == NULL) { + refs->error_ |= 1; + return NULL; + } + b->start_ = (PixOrCopy*)((uint8_t*)b + sizeof(*b)); // not always aligned + } else { // recycle from free-list + refs->free_blocks_ = b->next_; + } + *refs->tail_ = b; + refs->tail_ = &b->next_; + refs->last_block_ = b; + b->next_ = NULL; + b->size_ = 0; + return b; +} + +static WEBP_INLINE void BackwardRefsCursorAdd(VP8LBackwardRefs* const refs, + const PixOrCopy v) { + PixOrCopyBlock* b = refs->last_block_; + if (b == NULL || b->size_ == refs->block_size_) { + b = BackwardRefsNewBlock(refs); + if (b == NULL) return; // refs->error_ is set + } + b->start_[b->size_++] = v; +} + +int VP8LBackwardRefsCopy(const VP8LBackwardRefs* const src, + VP8LBackwardRefs* const dst) { + const PixOrCopyBlock* b = src->refs_; + ClearBackwardRefs(dst); + assert(src->block_size_ == dst->block_size_); + while (b != NULL) { + PixOrCopyBlock* const new_b = BackwardRefsNewBlock(dst); + if (new_b == NULL) return 0; // dst->error_ is set + memcpy(new_b->start_, b->start_, b->size_ * sizeof(*b->start_)); + new_b->size_ = b->size_; + b = b->next_; + } + return 1; +} + +// ----------------------------------------------------------------------------- +// Hash chains + +int VP8LHashChainInit(VP8LHashChain* const p, int size) { + assert(p->size_ == 0); + assert(p->offset_length_ == NULL); + assert(size > 0); + p->offset_length_ = + (uint32_t*)WebPSafeMalloc(size, sizeof(*p->offset_length_)); + if (p->offset_length_ == NULL) return 0; + p->size_ = size; + + return 1; +} + +void VP8LHashChainClear(VP8LHashChain* const p) { + assert(p != NULL); + WebPSafeFree(p->offset_length_); + + p->size_ = 0; + p->offset_length_ = NULL; +} + +// ----------------------------------------------------------------------------- + +#define HASH_MULTIPLIER_HI (0xc6a4a793ULL) +#define HASH_MULTIPLIER_LO (0x5bd1e996ULL) + +static WEBP_INLINE uint32_t GetPixPairHash64(const uint32_t* const argb) { + uint32_t key; + key = (argb[1] * HASH_MULTIPLIER_HI) & 0xffffffffu; + key += (argb[0] * HASH_MULTIPLIER_LO) & 0xffffffffu; + key = key >> (32 - HASH_BITS); + return key; +} + +// Returns the maximum number of hash chain lookups to do for a +// given compression quality. Return value in range [8, 86]. +static int GetMaxItersForQuality(int quality) { + return 8 + (quality * quality) / 128; +} + +static int GetWindowSizeForHashChain(int quality, int xsize) { + const int max_window_size = (quality > 75) ? WINDOW_SIZE + : (quality > 50) ? (xsize << 8) + : (quality > 25) ? (xsize << 6) + : (xsize << 4); + assert(xsize > 0); + return (max_window_size > WINDOW_SIZE) ? WINDOW_SIZE : max_window_size; +} + +static WEBP_INLINE int MaxFindCopyLength(int len) { + return (len < MAX_LENGTH) ? len : MAX_LENGTH; +} + +int VP8LHashChainFill(VP8LHashChain* const p, int quality, + const uint32_t* const argb, int xsize, int ysize, + int low_effort) { + const int size = xsize * ysize; + const int iter_max = GetMaxItersForQuality(quality); + const uint32_t window_size = GetWindowSizeForHashChain(quality, xsize); + int pos; + int argb_comp; + uint32_t base_position; + int32_t* hash_to_first_index; + // Temporarily use the p->offset_length_ as a hash chain. + int32_t* chain = (int32_t*)p->offset_length_; + assert(size > 0); + assert(p->size_ != 0); + assert(p->offset_length_ != NULL); + + if (size <= 2) { + p->offset_length_[0] = p->offset_length_[size - 1] = 0; + return 1; + } + + hash_to_first_index = + (int32_t*)WebPSafeMalloc(HASH_SIZE, sizeof(*hash_to_first_index)); + if (hash_to_first_index == NULL) return 0; + + // Set the int32_t array to -1. + memset(hash_to_first_index, 0xff, HASH_SIZE * sizeof(*hash_to_first_index)); + // Fill the chain linking pixels with the same hash. + argb_comp = (argb[0] == argb[1]); + for (pos = 0; pos < size - 2;) { + uint32_t hash_code; + const int argb_comp_next = (argb[pos + 1] == argb[pos + 2]); + if (argb_comp && argb_comp_next) { + // Consecutive pixels with the same color will share the same hash. + // We therefore use a different hash: the color and its repetition + // length. + uint32_t tmp[2]; + uint32_t len = 1; + tmp[0] = argb[pos]; + // Figure out how far the pixels are the same. + // The last pixel has a different 64 bit hash, as its next pixel does + // not have the same color, so we just need to get to the last pixel equal + // to its follower. + while (pos + (int)len + 2 < size && argb[pos + len + 2] == argb[pos]) { + ++len; + } + if (len > MAX_LENGTH) { + // Skip the pixels that match for distance=1 and length>MAX_LENGTH + // because they are linked to their predecessor and we automatically + // check that in the main for loop below. Skipping means setting no + // predecessor in the chain, hence -1. + memset(chain + pos, 0xff, (len - MAX_LENGTH) * sizeof(*chain)); + pos += len - MAX_LENGTH; + len = MAX_LENGTH; + } + // Process the rest of the hash chain. + while (len) { + tmp[1] = len--; + hash_code = GetPixPairHash64(tmp); + chain[pos] = hash_to_first_index[hash_code]; + hash_to_first_index[hash_code] = pos++; + } + argb_comp = 0; + } else { + // Just move one pixel forward. + hash_code = GetPixPairHash64(argb + pos); + chain[pos] = hash_to_first_index[hash_code]; + hash_to_first_index[hash_code] = pos++; + argb_comp = argb_comp_next; + } + } + // Process the penultimate pixel. + chain[pos] = hash_to_first_index[GetPixPairHash64(argb + pos)]; + + WebPSafeFree(hash_to_first_index); + + // Find the best match interval at each pixel, defined by an offset to the + // pixel and a length. The right-most pixel cannot match anything to the right + // (hence a best length of 0) and the left-most pixel nothing to the left + // (hence an offset of 0). + assert(size > 2); + p->offset_length_[0] = p->offset_length_[size - 1] = 0; + for (base_position = size - 2; base_position > 0;) { + const int max_len = MaxFindCopyLength(size - 1 - base_position); + const uint32_t* const argb_start = argb + base_position; + int iter = iter_max; + int best_length = 0; + uint32_t best_distance = 0; + uint32_t best_argb; + const int min_pos = + (base_position > window_size) ? base_position - window_size : 0; + const int length_max = (max_len < 256) ? max_len : 256; + uint32_t max_base_position; + + pos = chain[base_position]; + if (!low_effort) { + int curr_length; + // Heuristic: use the comparison with the above line as an initialization. + if (base_position >= (uint32_t)xsize) { + curr_length = FindMatchLength(argb_start - xsize, argb_start, + best_length, max_len); + if (curr_length > best_length) { + best_length = curr_length; + best_distance = xsize; + } + --iter; + } + // Heuristic: compare to the previous pixel. + curr_length = + FindMatchLength(argb_start - 1, argb_start, best_length, max_len); + if (curr_length > best_length) { + best_length = curr_length; + best_distance = 1; + } + --iter; + // Skip the for loop if we already have the maximum. + if (best_length == MAX_LENGTH) pos = min_pos - 1; + } + best_argb = argb_start[best_length]; + + for (; pos >= min_pos && --iter; pos = chain[pos]) { + int curr_length; + assert(base_position > (uint32_t)pos); + + if (argb[pos + best_length] != best_argb) continue; + + curr_length = VP8LVectorMismatch(argb + pos, argb_start, max_len); + if (best_length < curr_length) { + best_length = curr_length; + best_distance = base_position - pos; + best_argb = argb_start[best_length]; + // Stop if we have reached a good enough length. + if (best_length >= length_max) break; + } + } + // We have the best match but in case the two intervals continue matching + // to the left, we have the best matches for the left-extended pixels. + max_base_position = base_position; + while (1) { + assert(best_length <= MAX_LENGTH); + assert(best_distance <= WINDOW_SIZE); + p->offset_length_[base_position] = + (best_distance << MAX_LENGTH_BITS) | (uint32_t)best_length; + --base_position; + // Stop if we don't have a match or if we are out of bounds. + if (best_distance == 0 || base_position == 0) break; + // Stop if we cannot extend the matching intervals to the left. + if (base_position < best_distance || + argb[base_position - best_distance] != argb[base_position]) { + break; + } + // Stop if we are matching at its limit because there could be a closer + // matching interval with the same maximum length. Then again, if the + // matching interval is as close as possible (best_distance == 1), we will + // never find anything better so let's continue. + if (best_length == MAX_LENGTH && best_distance != 1 && + base_position + MAX_LENGTH < max_base_position) { + break; + } + if (best_length < MAX_LENGTH) { + ++best_length; + max_base_position = base_position; + } + } + } + return 1; +} + +static WEBP_INLINE int HashChainFindOffset(const VP8LHashChain* const p, + const int base_position) { + return p->offset_length_[base_position] >> MAX_LENGTH_BITS; +} + +static WEBP_INLINE int HashChainFindLength(const VP8LHashChain* const p, + const int base_position) { + return p->offset_length_[base_position] & ((1U << MAX_LENGTH_BITS) - 1); +} + +static WEBP_INLINE void HashChainFindCopy(const VP8LHashChain* const p, + int base_position, + int* const offset_ptr, + int* const length_ptr) { + *offset_ptr = HashChainFindOffset(p, base_position); + *length_ptr = HashChainFindLength(p, base_position); +} + +static WEBP_INLINE void AddSingleLiteral(uint32_t pixel, int use_color_cache, + VP8LColorCache* const hashers, + VP8LBackwardRefs* const refs) { + PixOrCopy v; + if (use_color_cache) { + const uint32_t key = VP8LColorCacheGetIndex(hashers, pixel); + if (VP8LColorCacheLookup(hashers, key) == pixel) { + v = PixOrCopyCreateCacheIdx(key); + } else { + v = PixOrCopyCreateLiteral(pixel); + VP8LColorCacheSet(hashers, key, pixel); + } + } else { + v = PixOrCopyCreateLiteral(pixel); + } + BackwardRefsCursorAdd(refs, v); +} + +static int BackwardReferencesRle(int xsize, int ysize, + const uint32_t* const argb, + int cache_bits, VP8LBackwardRefs* const refs) { + const int pix_count = xsize * ysize; + int i, k; + const int use_color_cache = (cache_bits > 0); + VP8LColorCache hashers; + + if (use_color_cache && !VP8LColorCacheInit(&hashers, cache_bits)) { + return 0; + } + ClearBackwardRefs(refs); + // Add first pixel as literal. + AddSingleLiteral(argb[0], use_color_cache, &hashers, refs); + i = 1; + while (i < pix_count) { + const int max_len = MaxFindCopyLength(pix_count - i); + const int rle_len = FindMatchLength(argb + i, argb + i - 1, 0, max_len); + const int prev_row_len = (i < xsize) ? 0 : + FindMatchLength(argb + i, argb + i - xsize, 0, max_len); + if (rle_len >= prev_row_len && rle_len >= MIN_LENGTH) { + BackwardRefsCursorAdd(refs, PixOrCopyCreateCopy(1, rle_len)); + // We don't need to update the color cache here since it is always the + // same pixel being copied, and that does not change the color cache + // state. + i += rle_len; + } else if (prev_row_len >= MIN_LENGTH) { + BackwardRefsCursorAdd(refs, PixOrCopyCreateCopy(xsize, prev_row_len)); + if (use_color_cache) { + for (k = 0; k < prev_row_len; ++k) { + VP8LColorCacheInsert(&hashers, argb[i + k]); + } + } + i += prev_row_len; + } else { + AddSingleLiteral(argb[i], use_color_cache, &hashers, refs); + i++; + } + } + if (use_color_cache) VP8LColorCacheClear(&hashers); + return !refs->error_; +} + +static int BackwardReferencesLz77(int xsize, int ysize, + const uint32_t* const argb, int cache_bits, + const VP8LHashChain* const hash_chain, + VP8LBackwardRefs* const refs) { + int i; + int i_last_check = -1; + int ok = 0; + int cc_init = 0; + const int use_color_cache = (cache_bits > 0); + const int pix_count = xsize * ysize; + VP8LColorCache hashers; + + if (use_color_cache) { + cc_init = VP8LColorCacheInit(&hashers, cache_bits); + if (!cc_init) goto Error; + } + ClearBackwardRefs(refs); + for (i = 0; i < pix_count;) { + // Alternative#1: Code the pixels starting at 'i' using backward reference. + int offset = 0; + int len = 0; + int j; + HashChainFindCopy(hash_chain, i, &offset, &len); + if (len >= MIN_LENGTH) { + const int len_ini = len; + int max_reach = 0; + assert(i + len < pix_count); + // Only start from what we have not checked already. + i_last_check = (i > i_last_check) ? i : i_last_check; + // We know the best match for the current pixel but we try to find the + // best matches for the current pixel AND the next one combined. + // The naive method would use the intervals: + // [i,i+len) + [i+len, length of best match at i+len) + // while we check if we can use: + // [i,j) (where j<=i+len) + [j, length of best match at j) + for (j = i_last_check + 1; j <= i + len_ini; ++j) { + const int len_j = HashChainFindLength(hash_chain, j); + const int reach = + j + (len_j >= MIN_LENGTH ? len_j : 1); // 1 for single literal. + if (reach > max_reach) { + len = j - i; + max_reach = reach; + } + } + } else { + len = 1; + } + // Go with literal or backward reference. + assert(len > 0); + if (len == 1) { + AddSingleLiteral(argb[i], use_color_cache, &hashers, refs); + } else { + BackwardRefsCursorAdd(refs, PixOrCopyCreateCopy(offset, len)); + if (use_color_cache) { + for (j = i; j < i + len; ++j) VP8LColorCacheInsert(&hashers, argb[j]); + } + } + i += len; + } + + ok = !refs->error_; + Error: + if (cc_init) VP8LColorCacheClear(&hashers); + return ok; +} + +// ----------------------------------------------------------------------------- + +typedef struct { + double alpha_[VALUES_IN_BYTE]; + double red_[VALUES_IN_BYTE]; + double blue_[VALUES_IN_BYTE]; + double distance_[NUM_DISTANCE_CODES]; + double* literal_; +} CostModel; + +static int BackwardReferencesTraceBackwards( + int xsize, int ysize, const uint32_t* const argb, int quality, + int cache_bits, const VP8LHashChain* const hash_chain, + VP8LBackwardRefs* const refs); + +static void ConvertPopulationCountTableToBitEstimates( + int num_symbols, const uint32_t population_counts[], double output[]) { + uint32_t sum = 0; + int nonzeros = 0; + int i; + for (i = 0; i < num_symbols; ++i) { + sum += population_counts[i]; + if (population_counts[i] > 0) { + ++nonzeros; + } + } + if (nonzeros <= 1) { + memset(output, 0, num_symbols * sizeof(*output)); + } else { + const double logsum = VP8LFastLog2(sum); + for (i = 0; i < num_symbols; ++i) { + output[i] = logsum - VP8LFastLog2(population_counts[i]); + } + } +} + +static int CostModelBuild(CostModel* const m, int cache_bits, + VP8LBackwardRefs* const refs) { + int ok = 0; + VP8LHistogram* const histo = VP8LAllocateHistogram(cache_bits); + if (histo == NULL) goto Error; + + VP8LHistogramCreate(histo, refs, cache_bits); + + ConvertPopulationCountTableToBitEstimates( + VP8LHistogramNumCodes(histo->palette_code_bits_), + histo->literal_, m->literal_); + ConvertPopulationCountTableToBitEstimates( + VALUES_IN_BYTE, histo->red_, m->red_); + ConvertPopulationCountTableToBitEstimates( + VALUES_IN_BYTE, histo->blue_, m->blue_); + ConvertPopulationCountTableToBitEstimates( + VALUES_IN_BYTE, histo->alpha_, m->alpha_); + ConvertPopulationCountTableToBitEstimates( + NUM_DISTANCE_CODES, histo->distance_, m->distance_); + ok = 1; + + Error: + VP8LFreeHistogram(histo); + return ok; +} + +static WEBP_INLINE double GetLiteralCost(const CostModel* const m, uint32_t v) { + return m->alpha_[v >> 24] + + m->red_[(v >> 16) & 0xff] + + m->literal_[(v >> 8) & 0xff] + + m->blue_[v & 0xff]; +} + +static WEBP_INLINE double GetCacheCost(const CostModel* const m, uint32_t idx) { + const int literal_idx = VALUES_IN_BYTE + NUM_LENGTH_CODES + idx; + return m->literal_[literal_idx]; +} + +static WEBP_INLINE double GetLengthCost(const CostModel* const m, + uint32_t length) { + int code, extra_bits; + VP8LPrefixEncodeBits(length, &code, &extra_bits); + return m->literal_[VALUES_IN_BYTE + code] + extra_bits; +} + +static WEBP_INLINE double GetDistanceCost(const CostModel* const m, + uint32_t distance) { + int code, extra_bits; + VP8LPrefixEncodeBits(distance, &code, &extra_bits); + return m->distance_[code] + extra_bits; +} + +static void AddSingleLiteralWithCostModel(const uint32_t* const argb, + VP8LColorCache* const hashers, + const CostModel* const cost_model, + int idx, int use_color_cache, + double prev_cost, float* const cost, + uint16_t* const dist_array) { + double cost_val = prev_cost; + const uint32_t color = argb[0]; + const int ix = use_color_cache ? VP8LColorCacheContains(hashers, color) : -1; + if (ix >= 0) { + // use_color_cache is true and hashers contains color + const double mul0 = 0.68; + cost_val += GetCacheCost(cost_model, ix) * mul0; + } else { + const double mul1 = 0.82; + if (use_color_cache) VP8LColorCacheInsert(hashers, color); + cost_val += GetLiteralCost(cost_model, color) * mul1; + } + if (cost[idx] > cost_val) { + cost[idx] = (float)cost_val; + dist_array[idx] = 1; // only one is inserted. + } +} + +// ----------------------------------------------------------------------------- +// CostManager and interval handling + +// Empirical value to avoid high memory consumption but good for performance. +#define COST_CACHE_INTERVAL_SIZE_MAX 100 + +// To perform backward reference every pixel at index index_ is considered and +// the cost for the MAX_LENGTH following pixels computed. Those following pixels +// at index index_ + k (k from 0 to MAX_LENGTH) have a cost of: +// distance_cost_ at index_ + GetLengthCost(cost_model, k) +// (named cost) (named cached cost) +// and the minimum value is kept. GetLengthCost(cost_model, k) is cached in an +// array of size MAX_LENGTH. +// Instead of performing MAX_LENGTH comparisons per pixel, we keep track of the +// minimal values using intervals, for which lower_ and upper_ bounds are kept. +// An interval is defined by the index_ of the pixel that generated it and +// is only useful in a range of indices from start_ to end_ (exclusive), i.e. +// it contains the minimum value for pixels between start_ and end_. +// Intervals are stored in a linked list and ordered by start_. When a new +// interval has a better minimum, old intervals are split or removed. +typedef struct CostInterval CostInterval; +struct CostInterval { + double lower_; + double upper_; + int start_; + int end_; + double distance_cost_; + int index_; + CostInterval* previous_; + CostInterval* next_; +}; + +// The GetLengthCost(cost_model, k) part of the costs is also bounded for +// efficiency in a set of intervals of a different type. +// If those intervals are small enough, they are not used for comparison and +// written into the costs right away. +typedef struct { + double lower_; // Lower bound of the interval. + double upper_; // Upper bound of the interval. + int start_; + int end_; // Exclusive. + int do_write_; // If !=0, the interval is saved to cost instead of being kept + // for comparison. +} CostCacheInterval; + +// This structure is in charge of managing intervals and costs. +// It caches the different CostCacheInterval, caches the different +// GetLengthCost(cost_model, k) in cost_cache_ and the CostInterval's (whose +// count_ is limited by COST_CACHE_INTERVAL_SIZE_MAX). +#define COST_MANAGER_MAX_FREE_LIST 10 +typedef struct { + CostInterval* head_; + int count_; // The number of stored intervals. + CostCacheInterval* cache_intervals_; + size_t cache_intervals_size_; + double cost_cache_[MAX_LENGTH]; // Contains the GetLengthCost(cost_model, k). + double min_cost_cache_; // The minimum value in cost_cache_[1:]. + double max_cost_cache_; // The maximum value in cost_cache_[1:]. + float* costs_; + uint16_t* dist_array_; + // Most of the time, we only need few intervals -> use a free-list, to avoid + // fragmentation with small allocs in most common cases. + CostInterval intervals_[COST_MANAGER_MAX_FREE_LIST]; + CostInterval* free_intervals_; + // These are regularly malloc'd remains. This list can't grow larger than than + // size COST_CACHE_INTERVAL_SIZE_MAX - COST_MANAGER_MAX_FREE_LIST, note. + CostInterval* recycled_intervals_; + // Buffer used in BackwardReferencesHashChainDistanceOnly to store the ends + // of the intervals that can have impacted the cost at a pixel. + int* interval_ends_; + int interval_ends_size_; +} CostManager; + +static int IsCostCacheIntervalWritable(int start, int end) { + // 100 is the length for which we consider an interval for comparison, and not + // for writing. + // The first intervals are very small and go in increasing size. This constant + // helps merging them into one big interval (up to index 150/200 usually from + // which intervals start getting much bigger). + // This value is empirical. + return (end - start + 1 < 100); +} + +static void CostIntervalAddToFreeList(CostManager* const manager, + CostInterval* const interval) { + interval->next_ = manager->free_intervals_; + manager->free_intervals_ = interval; +} + +static int CostIntervalIsInFreeList(const CostManager* const manager, + const CostInterval* const interval) { + return (interval >= &manager->intervals_[0] && + interval <= &manager->intervals_[COST_MANAGER_MAX_FREE_LIST - 1]); +} + +static void CostManagerInitFreeList(CostManager* const manager) { + int i; + manager->free_intervals_ = NULL; + for (i = 0; i < COST_MANAGER_MAX_FREE_LIST; ++i) { + CostIntervalAddToFreeList(manager, &manager->intervals_[i]); + } +} + +static void DeleteIntervalList(CostManager* const manager, + const CostInterval* interval) { + while (interval != NULL) { + const CostInterval* const next = interval->next_; + if (!CostIntervalIsInFreeList(manager, interval)) { + WebPSafeFree((void*)interval); + } // else: do nothing + interval = next; + } +} + +static void CostManagerClear(CostManager* const manager) { + if (manager == NULL) return; + + WebPSafeFree(manager->costs_); + WebPSafeFree(manager->cache_intervals_); + WebPSafeFree(manager->interval_ends_); + + // Clear the interval lists. + DeleteIntervalList(manager, manager->head_); + manager->head_ = NULL; + DeleteIntervalList(manager, manager->recycled_intervals_); + manager->recycled_intervals_ = NULL; + + // Reset pointers, count_ and cache_intervals_size_. + memset(manager, 0, sizeof(*manager)); + CostManagerInitFreeList(manager); +} + +static int CostManagerInit(CostManager* const manager, + uint16_t* const dist_array, int pix_count, + const CostModel* const cost_model) { + int i; + const int cost_cache_size = (pix_count > MAX_LENGTH) ? MAX_LENGTH : pix_count; + // This constant is tied to the cost_model we use. + // Empirically, differences between intervals is usually of more than 1. + const double min_cost_diff = 0.1; + + manager->costs_ = NULL; + manager->cache_intervals_ = NULL; + manager->interval_ends_ = NULL; + manager->head_ = NULL; + manager->recycled_intervals_ = NULL; + manager->count_ = 0; + manager->dist_array_ = dist_array; + CostManagerInitFreeList(manager); + + // Fill in the cost_cache_. + manager->cache_intervals_size_ = 1; + manager->cost_cache_[0] = 0; + for (i = 1; i < cost_cache_size; ++i) { + manager->cost_cache_[i] = GetLengthCost(cost_model, i); + // Get an approximation of the number of bound intervals. + if (fabs(manager->cost_cache_[i] - manager->cost_cache_[i - 1]) > + min_cost_diff) { + ++manager->cache_intervals_size_; + } + // Compute the minimum of cost_cache_. + if (i == 1) { + manager->min_cost_cache_ = manager->cost_cache_[1]; + manager->max_cost_cache_ = manager->cost_cache_[1]; + } else if (manager->cost_cache_[i] < manager->min_cost_cache_) { + manager->min_cost_cache_ = manager->cost_cache_[i]; + } else if (manager->cost_cache_[i] > manager->max_cost_cache_) { + manager->max_cost_cache_ = manager->cost_cache_[i]; + } + } + + // With the current cost models, we have 15 intervals, so we are safe by + // setting a maximum of COST_CACHE_INTERVAL_SIZE_MAX. + if (manager->cache_intervals_size_ > COST_CACHE_INTERVAL_SIZE_MAX) { + manager->cache_intervals_size_ = COST_CACHE_INTERVAL_SIZE_MAX; + } + manager->cache_intervals_ = (CostCacheInterval*)WebPSafeMalloc( + manager->cache_intervals_size_, sizeof(*manager->cache_intervals_)); + if (manager->cache_intervals_ == NULL) { + CostManagerClear(manager); + return 0; + } + + // Fill in the cache_intervals_. + { + double cost_prev = -1e38f; // unprobably low initial value + CostCacheInterval* prev = NULL; + CostCacheInterval* cur = manager->cache_intervals_; + const CostCacheInterval* const end = + manager->cache_intervals_ + manager->cache_intervals_size_; + + // Consecutive values in cost_cache_ are compared and if a big enough + // difference is found, a new interval is created and bounded. + for (i = 0; i < cost_cache_size; ++i) { + const double cost_val = manager->cost_cache_[i]; + if (i == 0 || + (fabs(cost_val - cost_prev) > min_cost_diff && cur + 1 < end)) { + if (i > 1) { + const int is_writable = + IsCostCacheIntervalWritable(cur->start_, cur->end_); + // Merge with the previous interval if both are writable. + if (is_writable && cur != manager->cache_intervals_ && + prev->do_write_) { + // Update the previous interval. + prev->end_ = cur->end_; + if (cur->lower_ < prev->lower_) { + prev->lower_ = cur->lower_; + } else if (cur->upper_ > prev->upper_) { + prev->upper_ = cur->upper_; + } + } else { + cur->do_write_ = is_writable; + prev = cur; + ++cur; + } + } + // Initialize an interval. + cur->start_ = i; + cur->do_write_ = 0; + cur->lower_ = cost_val; + cur->upper_ = cost_val; + } else { + // Update the current interval bounds. + if (cost_val < cur->lower_) { + cur->lower_ = cost_val; + } else if (cost_val > cur->upper_) { + cur->upper_ = cost_val; + } + } + cur->end_ = i + 1; + cost_prev = cost_val; + } + manager->cache_intervals_size_ = cur + 1 - manager->cache_intervals_; + } + + manager->costs_ = (float*)WebPSafeMalloc(pix_count, sizeof(*manager->costs_)); + if (manager->costs_ == NULL) { + CostManagerClear(manager); + return 0; + } + // Set the initial costs_ high for every pixel as we will keep the minimum. + for (i = 0; i < pix_count; ++i) manager->costs_[i] = 1e38f; + + // The cost at pixel is influenced by the cost intervals from previous pixels. + // Let us take the specific case where the offset is the same (which actually + // happens a lot in case of uniform regions). + // pixel i contributes to j>i a cost of: offset cost + cost_cache_[j-i] + // pixel i+1 contributes to j>i a cost of: 2*offset cost + cost_cache_[j-i-1] + // pixel i+2 contributes to j>i a cost of: 3*offset cost + cost_cache_[j-i-2] + // and so on. + // A pixel i influences the following length(j) < MAX_LENGTH pixels. What is + // the value of j such that pixel i + j cannot influence any of those pixels? + // This value is such that: + // max of cost_cache_ < j*offset cost + min of cost_cache_ + // (pixel i + j 's cost cannot beat the worst cost given by pixel i). + // This value will be used to optimize the cost computation in + // BackwardReferencesHashChainDistanceOnly. + { + // The offset cost is computed in GetDistanceCost and has a minimum value of + // the minimum in cost_model->distance_. The case where the offset cost is 0 + // will be dealt with differently later so we are only interested in the + // minimum non-zero offset cost. + double offset_cost_min = 0.; + int size; + for (i = 0; i < NUM_DISTANCE_CODES; ++i) { + if (cost_model->distance_[i] != 0) { + if (offset_cost_min == 0.) { + offset_cost_min = cost_model->distance_[i]; + } else if (cost_model->distance_[i] < offset_cost_min) { + offset_cost_min = cost_model->distance_[i]; + } + } + } + // In case all the cost_model->distance_ is 0, the next non-zero cost we + // can have is from the extra bit in GetDistanceCost, hence 1. + if (offset_cost_min < 1.) offset_cost_min = 1.; + + size = 1 + (int)ceil((manager->max_cost_cache_ - manager->min_cost_cache_) / + offset_cost_min); + // Empirically, we usually end up with a value below 100. + if (size > MAX_LENGTH) size = MAX_LENGTH; + + manager->interval_ends_ = + (int*)WebPSafeMalloc(size, sizeof(*manager->interval_ends_)); + if (manager->interval_ends_ == NULL) { + CostManagerClear(manager); + return 0; + } + manager->interval_ends_size_ = size; + } + + return 1; +} + +// Given the distance_cost for pixel 'index', update the cost at pixel 'i' if it +// is smaller than the previously computed value. +static WEBP_INLINE void UpdateCost(CostManager* const manager, int i, int index, + double distance_cost) { + int k = i - index; + double cost_tmp; + assert(k >= 0 && k < MAX_LENGTH); + cost_tmp = distance_cost + manager->cost_cache_[k]; + + if (manager->costs_[i] > cost_tmp) { + manager->costs_[i] = (float)cost_tmp; + manager->dist_array_[i] = k + 1; + } +} + +// Given the distance_cost for pixel 'index', update the cost for all the pixels +// between 'start' and 'end' excluded. +static WEBP_INLINE void UpdateCostPerInterval(CostManager* const manager, + int start, int end, int index, + double distance_cost) { + int i; + for (i = start; i < end; ++i) UpdateCost(manager, i, index, distance_cost); +} + +// Given two intervals, make 'prev' be the previous one of 'next' in 'manager'. +static WEBP_INLINE void ConnectIntervals(CostManager* const manager, + CostInterval* const prev, + CostInterval* const next) { + if (prev != NULL) { + prev->next_ = next; + } else { + manager->head_ = next; + } + + if (next != NULL) next->previous_ = prev; +} + +// Pop an interval in the manager. +static WEBP_INLINE void PopInterval(CostManager* const manager, + CostInterval* const interval) { + CostInterval* const next = interval->next_; + + if (interval == NULL) return; + + ConnectIntervals(manager, interval->previous_, next); + if (CostIntervalIsInFreeList(manager, interval)) { + CostIntervalAddToFreeList(manager, interval); + } else { // recycle regularly malloc'd intervals too + interval->next_ = manager->recycled_intervals_; + manager->recycled_intervals_ = interval; + } + --manager->count_; + assert(manager->count_ >= 0); +} + +// Update the cost at index i by going over all the stored intervals that +// overlap with i. +static WEBP_INLINE void UpdateCostPerIndex(CostManager* const manager, int i) { + CostInterval* current = manager->head_; + + while (current != NULL && current->start_ <= i) { + if (current->end_ <= i) { + // We have an outdated interval, remove it. + CostInterval* next = current->next_; + PopInterval(manager, current); + current = next; + } else { + UpdateCost(manager, i, current->index_, current->distance_cost_); + current = current->next_; + } + } +} + +// Given a current orphan interval and its previous interval, before +// it was orphaned (which can be NULL), set it at the right place in the list +// of intervals using the start_ ordering and the previous interval as a hint. +static WEBP_INLINE void PositionOrphanInterval(CostManager* const manager, + CostInterval* const current, + CostInterval* previous) { + assert(current != NULL); + + if (previous == NULL) previous = manager->head_; + while (previous != NULL && current->start_ < previous->start_) { + previous = previous->previous_; + } + while (previous != NULL && previous->next_ != NULL && + previous->next_->start_ < current->start_) { + previous = previous->next_; + } + + if (previous != NULL) { + ConnectIntervals(manager, current, previous->next_); + } else { + ConnectIntervals(manager, current, manager->head_); + } + ConnectIntervals(manager, previous, current); +} + +// Insert an interval in the list contained in the manager by starting at +// interval_in as a hint. The intervals are sorted by start_ value. +static WEBP_INLINE void InsertInterval(CostManager* const manager, + CostInterval* const interval_in, + double distance_cost, double lower, + double upper, int index, int start, + int end) { + CostInterval* interval_new; + + if (IsCostCacheIntervalWritable(start, end) || + manager->count_ >= COST_CACHE_INTERVAL_SIZE_MAX) { + // Write down the interval if it is too small. + UpdateCostPerInterval(manager, start, end, index, distance_cost); + return; + } + if (manager->free_intervals_ != NULL) { + interval_new = manager->free_intervals_; + manager->free_intervals_ = interval_new->next_; + } else if (manager->recycled_intervals_ != NULL) { + interval_new = manager->recycled_intervals_; + manager->recycled_intervals_ = interval_new->next_; + } else { // malloc for good + interval_new = (CostInterval*)WebPSafeMalloc(1, sizeof(*interval_new)); + if (interval_new == NULL) { + // Write down the interval if we cannot create it. + UpdateCostPerInterval(manager, start, end, index, distance_cost); + return; + } + } + + interval_new->distance_cost_ = distance_cost; + interval_new->lower_ = lower; + interval_new->upper_ = upper; + interval_new->index_ = index; + interval_new->start_ = start; + interval_new->end_ = end; + PositionOrphanInterval(manager, interval_new, interval_in); + + ++manager->count_; +} + +// When an interval has its start_ or end_ modified, it needs to be +// repositioned in the linked list. +static WEBP_INLINE void RepositionInterval(CostManager* const manager, + CostInterval* const interval) { + if (IsCostCacheIntervalWritable(interval->start_, interval->end_)) { + // Maybe interval has been resized and is small enough to be removed. + UpdateCostPerInterval(manager, interval->start_, interval->end_, + interval->index_, interval->distance_cost_); + PopInterval(manager, interval); + return; + } + + // Early exit if interval is at the right spot. + if ((interval->previous_ == NULL || + interval->previous_->start_ <= interval->start_) && + (interval->next_ == NULL || + interval->start_ <= interval->next_->start_)) { + return; + } + + ConnectIntervals(manager, interval->previous_, interval->next_); + PositionOrphanInterval(manager, interval, interval->previous_); +} + +// Given a new cost interval defined by its start at index, its last value and +// distance_cost, add its contributions to the previous intervals and costs. +// If handling the interval or one of its subintervals becomes to heavy, its +// contribution is added to the costs right away. +static WEBP_INLINE void PushInterval(CostManager* const manager, + double distance_cost, int index, + int last) { + size_t i; + CostInterval* interval = manager->head_; + CostInterval* interval_next; + const CostCacheInterval* const cost_cache_intervals = + manager->cache_intervals_; + + for (i = 0; i < manager->cache_intervals_size_ && + cost_cache_intervals[i].start_ < last; + ++i) { + // Define the intersection of the ith interval with the new one. + int start = index + cost_cache_intervals[i].start_; + const int end = index + (cost_cache_intervals[i].end_ > last + ? last + : cost_cache_intervals[i].end_); + const double lower_in = cost_cache_intervals[i].lower_; + const double upper_in = cost_cache_intervals[i].upper_; + const double lower_full_in = distance_cost + lower_in; + const double upper_full_in = distance_cost + upper_in; + + if (cost_cache_intervals[i].do_write_) { + UpdateCostPerInterval(manager, start, end, index, distance_cost); + continue; + } + + for (; interval != NULL && interval->start_ < end && start < end; + interval = interval_next) { + const double lower_full_interval = + interval->distance_cost_ + interval->lower_; + const double upper_full_interval = + interval->distance_cost_ + interval->upper_; + + interval_next = interval->next_; + + // Make sure we have some overlap + if (start >= interval->end_) continue; + + if (lower_full_in >= upper_full_interval) { + // When intervals are represented, the lower, the better. + // [**********************************************************] + // start end + // [----------------------------------] + // interval->start_ interval->end_ + // If we are worse than what we already have, add whatever we have so + // far up to interval. + const int start_new = interval->end_; + InsertInterval(manager, interval, distance_cost, lower_in, upper_in, + index, start, interval->start_); + start = start_new; + continue; + } + + // We know the two intervals intersect. + if (upper_full_in >= lower_full_interval) { + // There is no clear cut on which is best, so let's keep both. + // [*********[*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*]***********] + // start interval->start_ interval->end_ end + // OR + // [*********[*-*-*-*-*-*-*-*-*-*-*-]----------------------] + // start interval->start_ end interval->end_ + const int end_new = (interval->end_ <= end) ? interval->end_ : end; + InsertInterval(manager, interval, distance_cost, lower_in, upper_in, + index, start, end_new); + start = end_new; + } else if (start <= interval->start_ && interval->end_ <= end) { + // [----------------------------------] + // interval->start_ interval->end_ + // [**************************************************************] + // start end + // We can safely remove the old interval as it is fully included. + PopInterval(manager, interval); + } else { + if (interval->start_ <= start && end <= interval->end_) { + // [--------------------------------------------------------------] + // interval->start_ interval->end_ + // [*****************************] + // start end + // We have to split the old interval as it fully contains the new one. + const int end_original = interval->end_; + interval->end_ = start; + InsertInterval(manager, interval, interval->distance_cost_, + interval->lower_, interval->upper_, interval->index_, + end, end_original); + } else if (interval->start_ < start) { + // [------------------------------------] + // interval->start_ interval->end_ + // [*****************************] + // start end + interval->end_ = start; + } else { + // [------------------------------------] + // interval->start_ interval->end_ + // [*****************************] + // start end + interval->start_ = end; + } + + // The interval has been modified, we need to reposition it or write it. + RepositionInterval(manager, interval); + } + } + // Insert the remaining interval from start to end. + InsertInterval(manager, interval, distance_cost, lower_in, upper_in, index, + start, end); + } +} + +static int BackwardReferencesHashChainDistanceOnly( + int xsize, int ysize, const uint32_t* const argb, int quality, + int cache_bits, const VP8LHashChain* const hash_chain, + VP8LBackwardRefs* const refs, uint16_t* const dist_array) { + int i; + int ok = 0; + int cc_init = 0; + const int pix_count = xsize * ysize; + const int use_color_cache = (cache_bits > 0); + const size_t literal_array_size = sizeof(double) * + (NUM_LITERAL_CODES + NUM_LENGTH_CODES + + ((cache_bits > 0) ? (1 << cache_bits) : 0)); + const size_t cost_model_size = sizeof(CostModel) + literal_array_size; + CostModel* const cost_model = + (CostModel*)WebPSafeCalloc(1ULL, cost_model_size); + VP8LColorCache hashers; + const int skip_length = 32 + quality; + const int skip_min_distance_code = 2; + CostManager* cost_manager = + (CostManager*)WebPSafeMalloc(1ULL, sizeof(*cost_manager)); + + if (cost_model == NULL || cost_manager == NULL) goto Error; + + cost_model->literal_ = (double*)(cost_model + 1); + if (use_color_cache) { + cc_init = VP8LColorCacheInit(&hashers, cache_bits); + if (!cc_init) goto Error; + } + + if (!CostModelBuild(cost_model, cache_bits, refs)) { + goto Error; + } + + if (!CostManagerInit(cost_manager, dist_array, pix_count, cost_model)) { + goto Error; + } + + // We loop one pixel at a time, but store all currently best points to + // non-processed locations from this point. + dist_array[0] = 0; + // Add first pixel as literal. + AddSingleLiteralWithCostModel(argb + 0, &hashers, cost_model, 0, + use_color_cache, 0.0, cost_manager->costs_, + dist_array); + + for (i = 1; i < pix_count - 1; ++i) { + int offset = 0, len = 0; + double prev_cost = cost_manager->costs_[i - 1]; + HashChainFindCopy(hash_chain, i, &offset, &len); + if (len >= 2) { + // If we are dealing with a non-literal. + const int code = DistanceToPlaneCode(xsize, offset); + const double offset_cost = GetDistanceCost(cost_model, code); + const int first_i = i; + int j_max = 0, interval_ends_index = 0; + const int is_offset_zero = (offset_cost == 0.); + + if (!is_offset_zero) { + j_max = (int)ceil( + (cost_manager->max_cost_cache_ - cost_manager->min_cost_cache_) / + offset_cost); + if (j_max < 1) { + j_max = 1; + } else if (j_max > cost_manager->interval_ends_size_ - 1) { + // This could only happen in the case of MAX_LENGTH. + j_max = cost_manager->interval_ends_size_ - 1; + } + } // else j_max is unused anyway. + + // Instead of considering all contributions from a pixel i by calling: + // PushInterval(cost_manager, prev_cost + offset_cost, i, len); + // we optimize these contributions in case offset_cost stays the same for + // consecutive pixels. This describes a set of pixels similar to a + // previous set (e.g. constant color regions). + for (; i < pix_count - 1; ++i) { + int offset_next, len_next; + prev_cost = cost_manager->costs_[i - 1]; + + if (is_offset_zero) { + // No optimization can be made so we just push all of the + // contributions from i. + PushInterval(cost_manager, prev_cost, i, len); + } else { + // j_max is chosen as the smallest j such that: + // max of cost_cache_ < j*offset cost + min of cost_cache_ + // Therefore, the pixel influenced by i-j_max, cannot be influenced + // by i. Only the costs after the end of what i contributed need to be + // updated. cost_manager->interval_ends_ is a circular buffer that + // stores those ends. + const double distance_cost = prev_cost + offset_cost; + int j = cost_manager->interval_ends_[interval_ends_index]; + if (i - first_i <= j_max || + !IsCostCacheIntervalWritable(j, i + len)) { + PushInterval(cost_manager, distance_cost, i, len); + } else { + for (; j < i + len; ++j) { + UpdateCost(cost_manager, j, i, distance_cost); + } + } + // Store the new end in the circular buffer. + assert(interval_ends_index < cost_manager->interval_ends_size_); + cost_manager->interval_ends_[interval_ends_index] = i + len; + if (++interval_ends_index > j_max) interval_ends_index = 0; + } + + // Check whether i is the last pixel to consider, as it is handled + // differently. + if (i + 1 >= pix_count - 1) break; + HashChainFindCopy(hash_chain, i + 1, &offset_next, &len_next); + if (offset_next != offset) break; + len = len_next; + UpdateCostPerIndex(cost_manager, i); + AddSingleLiteralWithCostModel(argb + i, &hashers, cost_model, i, + use_color_cache, prev_cost, + cost_manager->costs_, dist_array); + } + // Submit the last pixel. + UpdateCostPerIndex(cost_manager, i + 1); + + // This if is for speedup only. It roughly doubles the speed, and + // makes compression worse by .1 %. + if (len >= skip_length && code <= skip_min_distance_code) { + // Long copy for short distances, let's skip the middle + // lookups for better copies. + // 1) insert the hashes. + if (use_color_cache) { + int k; + for (k = 0; k < len; ++k) { + VP8LColorCacheInsert(&hashers, argb[i + k]); + } + } + // 2) jump. + { + const int i_next = i + len - 1; // for loop does ++i, thus -1 here. + for (; i <= i_next; ++i) UpdateCostPerIndex(cost_manager, i + 1); + i = i_next; + } + goto next_symbol; + } + if (len > 2) { + // Also try the smallest interval possible (size 2). + double cost_total = + prev_cost + offset_cost + GetLengthCost(cost_model, 1); + if (cost_manager->costs_[i + 1] > cost_total) { + cost_manager->costs_[i + 1] = (float)cost_total; + dist_array[i + 1] = 2; + } + } + } else { + // The pixel is added as a single literal so just update the costs. + UpdateCostPerIndex(cost_manager, i + 1); + } + + AddSingleLiteralWithCostModel(argb + i, &hashers, cost_model, i, + use_color_cache, prev_cost, + cost_manager->costs_, dist_array); + + next_symbol: ; + } + // Handle the last pixel. + if (i == (pix_count - 1)) { + AddSingleLiteralWithCostModel( + argb + i, &hashers, cost_model, i, use_color_cache, + cost_manager->costs_[pix_count - 2], cost_manager->costs_, dist_array); + } + + ok = !refs->error_; + Error: + if (cc_init) VP8LColorCacheClear(&hashers); + CostManagerClear(cost_manager); + WebPSafeFree(cost_model); + WebPSafeFree(cost_manager); + return ok; +} + +// We pack the path at the end of *dist_array and return +// a pointer to this part of the array. Example: +// dist_array = [1x2xx3x2] => packed [1x2x1232], chosen_path = [1232] +static void TraceBackwards(uint16_t* const dist_array, + int dist_array_size, + uint16_t** const chosen_path, + int* const chosen_path_size) { + uint16_t* path = dist_array + dist_array_size; + uint16_t* cur = dist_array + dist_array_size - 1; + while (cur >= dist_array) { + const int k = *cur; + --path; + *path = k; + cur -= k; + } + *chosen_path = path; + *chosen_path_size = (int)(dist_array + dist_array_size - path); +} + +static int BackwardReferencesHashChainFollowChosenPath( + const uint32_t* const argb, int cache_bits, + const uint16_t* const chosen_path, int chosen_path_size, + const VP8LHashChain* const hash_chain, VP8LBackwardRefs* const refs) { + const int use_color_cache = (cache_bits > 0); + int ix; + int i = 0; + int ok = 0; + int cc_init = 0; + VP8LColorCache hashers; + + if (use_color_cache) { + cc_init = VP8LColorCacheInit(&hashers, cache_bits); + if (!cc_init) goto Error; + } + + ClearBackwardRefs(refs); + for (ix = 0; ix < chosen_path_size; ++ix) { + const int len = chosen_path[ix]; + if (len != 1) { + int k; + const int offset = HashChainFindOffset(hash_chain, i); + BackwardRefsCursorAdd(refs, PixOrCopyCreateCopy(offset, len)); + if (use_color_cache) { + for (k = 0; k < len; ++k) { + VP8LColorCacheInsert(&hashers, argb[i + k]); + } + } + i += len; + } else { + PixOrCopy v; + const int idx = + use_color_cache ? VP8LColorCacheContains(&hashers, argb[i]) : -1; + if (idx >= 0) { + // use_color_cache is true and hashers contains argb[i] + // push pixel as a color cache index + v = PixOrCopyCreateCacheIdx(idx); + } else { + if (use_color_cache) VP8LColorCacheInsert(&hashers, argb[i]); + v = PixOrCopyCreateLiteral(argb[i]); + } + BackwardRefsCursorAdd(refs, v); + ++i; + } + } + ok = !refs->error_; + Error: + if (cc_init) VP8LColorCacheClear(&hashers); + return ok; +} + +// Returns 1 on success. +static int BackwardReferencesTraceBackwards( + int xsize, int ysize, const uint32_t* const argb, int quality, + int cache_bits, const VP8LHashChain* const hash_chain, + VP8LBackwardRefs* const refs) { + int ok = 0; + const int dist_array_size = xsize * ysize; + uint16_t* chosen_path = NULL; + int chosen_path_size = 0; + uint16_t* dist_array = + (uint16_t*)WebPSafeMalloc(dist_array_size, sizeof(*dist_array)); + + if (dist_array == NULL) goto Error; + + if (!BackwardReferencesHashChainDistanceOnly( + xsize, ysize, argb, quality, cache_bits, hash_chain, + refs, dist_array)) { + goto Error; + } + TraceBackwards(dist_array, dist_array_size, &chosen_path, &chosen_path_size); + if (!BackwardReferencesHashChainFollowChosenPath( + argb, cache_bits, chosen_path, chosen_path_size, hash_chain, refs)) { + goto Error; + } + ok = 1; + Error: + WebPSafeFree(dist_array); + return ok; +} + +static void BackwardReferences2DLocality(int xsize, + const VP8LBackwardRefs* const refs) { + VP8LRefsCursor c = VP8LRefsCursorInit(refs); + while (VP8LRefsCursorOk(&c)) { + if (PixOrCopyIsCopy(c.cur_pos)) { + const int dist = c.cur_pos->argb_or_distance; + const int transformed_dist = DistanceToPlaneCode(xsize, dist); + c.cur_pos->argb_or_distance = transformed_dist; + } + VP8LRefsCursorNext(&c); + } +} + +// Computes the entropies for a color cache size (in bits) between 0 (unused) +// and cache_bits_max (inclusive). +// Returns 1 on success, 0 in case of allocation error. +static int ComputeCacheEntropies(const uint32_t* argb, + const VP8LBackwardRefs* const refs, + int cache_bits_max, double entropies[]) { + int cc_init[MAX_COLOR_CACHE_BITS + 1] = { 0 }; + VP8LColorCache hashers[MAX_COLOR_CACHE_BITS + 1]; + VP8LRefsCursor c = VP8LRefsCursorInit(refs); + VP8LHistogram* histos[MAX_COLOR_CACHE_BITS + 1] = { NULL }; + int ok = 0; + int i; + + for (i = 0; i <= cache_bits_max; ++i) { + histos[i] = VP8LAllocateHistogram(i); + if (histos[i] == NULL) goto Error; + if (i == 0) continue; + cc_init[i] = VP8LColorCacheInit(&hashers[i], i); + if (!cc_init[i]) goto Error; + } + + assert(cache_bits_max >= 0); + // Do not use the color cache for cache_bits=0. + while (VP8LRefsCursorOk(&c)) { + VP8LHistogramAddSinglePixOrCopy(histos[0], c.cur_pos); + VP8LRefsCursorNext(&c); + } + if (cache_bits_max > 0) { + c = VP8LRefsCursorInit(refs); + while (VP8LRefsCursorOk(&c)) { + const PixOrCopy* const v = c.cur_pos; + if (PixOrCopyIsLiteral(v)) { + const uint32_t pix = *argb++; + // The keys of the caches can be derived from the longest one. + int key = HashPix(pix, 32 - cache_bits_max); + for (i = cache_bits_max; i >= 1; --i, key >>= 1) { + if (VP8LColorCacheLookup(&hashers[i], key) == pix) { + ++histos[i]->literal_[NUM_LITERAL_CODES + NUM_LENGTH_CODES + key]; + } else { + VP8LColorCacheSet(&hashers[i], key, pix); + ++histos[i]->blue_[pix & 0xff]; + ++histos[i]->literal_[(pix >> 8) & 0xff]; + ++histos[i]->red_[(pix >> 16) & 0xff]; + ++histos[i]->alpha_[pix >> 24]; + } + } + } else { + // Update the histograms for distance/length. + int len = PixOrCopyLength(v); + int code_dist, code_len, extra_bits; + uint32_t argb_prev = *argb ^ 0xffffffffu; + VP8LPrefixEncodeBits(len, &code_len, &extra_bits); + VP8LPrefixEncodeBits(PixOrCopyDistance(v), &code_dist, &extra_bits); + for (i = 1; i <= cache_bits_max; ++i) { + ++histos[i]->literal_[NUM_LITERAL_CODES + code_len]; + ++histos[i]->distance_[code_dist]; + } + // Update the colors caches. + do { + if (*argb != argb_prev) { + // Efficiency: insert only if the color changes. + int key = HashPix(*argb, 32 - cache_bits_max); + for (i = cache_bits_max; i >= 1; --i, key >>= 1) { + hashers[i].colors_[key] = *argb; + } + argb_prev = *argb; + } + argb++; + } while (--len != 0); + } + VP8LRefsCursorNext(&c); + } + } + for (i = 0; i <= cache_bits_max; ++i) { + entropies[i] = VP8LHistogramEstimateBits(histos[i]); + } + ok = 1; +Error: + for (i = 0; i <= cache_bits_max; ++i) { + if (cc_init[i]) VP8LColorCacheClear(&hashers[i]); + VP8LFreeHistogram(histos[i]); + } + return ok; +} + +// Evaluate optimal cache bits for the local color cache. +// The input *best_cache_bits sets the maximum cache bits to use (passing 0 +// implies disabling the local color cache). The local color cache is also +// disabled for the lower (<= 25) quality. +// Returns 0 in case of memory error. +static int CalculateBestCacheSize(const uint32_t* const argb, + int xsize, int ysize, int quality, + const VP8LHashChain* const hash_chain, + VP8LBackwardRefs* const refs, + int* const lz77_computed, + int* const best_cache_bits) { + int i; + int cache_bits_high = (quality <= 25) ? 0 : *best_cache_bits; + double entropy_min = MAX_ENTROPY; + double entropies[MAX_COLOR_CACHE_BITS + 1]; + + assert(cache_bits_high <= MAX_COLOR_CACHE_BITS); + + *lz77_computed = 0; + if (cache_bits_high == 0) { + *best_cache_bits = 0; + // Local color cache is disabled. + return 1; + } + // Compute LZ77 with no cache (0 bits), as the ideal LZ77 with a color cache + // is not that different in practice. + if (!BackwardReferencesLz77(xsize, ysize, argb, 0, hash_chain, refs)) { + return 0; + } + // Find the cache_bits giving the lowest entropy. The search is done in a + // brute-force way as the function (entropy w.r.t cache_bits) can be + // anything in practice. + if (!ComputeCacheEntropies(argb, refs, cache_bits_high, entropies)) { + return 0; + } + for (i = 0; i <= cache_bits_high; ++i) { + if (i == 0 || entropies[i] < entropy_min) { + entropy_min = entropies[i]; + *best_cache_bits = i; + } + } + return 1; +} + +// Update (in-place) backward references for specified cache_bits. +static int BackwardRefsWithLocalCache(const uint32_t* const argb, + int cache_bits, + VP8LBackwardRefs* const refs) { + int pixel_index = 0; + VP8LColorCache hashers; + VP8LRefsCursor c = VP8LRefsCursorInit(refs); + if (!VP8LColorCacheInit(&hashers, cache_bits)) return 0; + + while (VP8LRefsCursorOk(&c)) { + PixOrCopy* const v = c.cur_pos; + if (PixOrCopyIsLiteral(v)) { + const uint32_t argb_literal = v->argb_or_distance; + const int ix = VP8LColorCacheContains(&hashers, argb_literal); + if (ix >= 0) { + // hashers contains argb_literal + *v = PixOrCopyCreateCacheIdx(ix); + } else { + VP8LColorCacheInsert(&hashers, argb_literal); + } + ++pixel_index; + } else { + // refs was created without local cache, so it can not have cache indexes. + int k; + assert(PixOrCopyIsCopy(v)); + for (k = 0; k < v->len; ++k) { + VP8LColorCacheInsert(&hashers, argb[pixel_index++]); + } + } + VP8LRefsCursorNext(&c); + } + VP8LColorCacheClear(&hashers); + return 1; +} + +static VP8LBackwardRefs* GetBackwardReferencesLowEffort( + int width, int height, const uint32_t* const argb, + int* const cache_bits, const VP8LHashChain* const hash_chain, + VP8LBackwardRefs refs_array[2]) { + VP8LBackwardRefs* refs_lz77 = &refs_array[0]; + *cache_bits = 0; + if (!BackwardReferencesLz77(width, height, argb, 0, hash_chain, refs_lz77)) { + return NULL; + } + BackwardReferences2DLocality(width, refs_lz77); + return refs_lz77; +} + +static VP8LBackwardRefs* GetBackwardReferences( + int width, int height, const uint32_t* const argb, int quality, + int* const cache_bits, const VP8LHashChain* const hash_chain, + VP8LBackwardRefs refs_array[2]) { + int lz77_is_useful; + int lz77_computed; + double bit_cost_lz77, bit_cost_rle; + VP8LBackwardRefs* best = NULL; + VP8LBackwardRefs* refs_lz77 = &refs_array[0]; + VP8LBackwardRefs* refs_rle = &refs_array[1]; + VP8LHistogram* histo = NULL; + + if (!CalculateBestCacheSize(argb, width, height, quality, hash_chain, + refs_lz77, &lz77_computed, cache_bits)) { + goto Error; + } + + if (lz77_computed) { + // Transform refs_lz77 for the optimized cache_bits. + if (*cache_bits > 0) { + if (!BackwardRefsWithLocalCache(argb, *cache_bits, refs_lz77)) { + goto Error; + } + } + } else { + if (!BackwardReferencesLz77(width, height, argb, *cache_bits, hash_chain, + refs_lz77)) { + goto Error; + } + } + + if (!BackwardReferencesRle(width, height, argb, *cache_bits, refs_rle)) { + goto Error; + } + + histo = VP8LAllocateHistogram(*cache_bits); + if (histo == NULL) goto Error; + + { + // Evaluate LZ77 coding. + VP8LHistogramCreate(histo, refs_lz77, *cache_bits); + bit_cost_lz77 = VP8LHistogramEstimateBits(histo); + // Evaluate RLE coding. + VP8LHistogramCreate(histo, refs_rle, *cache_bits); + bit_cost_rle = VP8LHistogramEstimateBits(histo); + // Decide if LZ77 is useful. + lz77_is_useful = (bit_cost_lz77 < bit_cost_rle); + } + + // Choose appropriate backward reference. + if (lz77_is_useful) { + // TraceBackwards is costly. Don't execute it at lower quality. + const int try_lz77_trace_backwards = (quality >= 25); + best = refs_lz77; // default guess: lz77 is better + if (try_lz77_trace_backwards) { + VP8LBackwardRefs* const refs_trace = refs_rle; + if (!VP8LBackwardRefsCopy(refs_lz77, refs_trace)) { + best = NULL; + goto Error; + } + if (BackwardReferencesTraceBackwards(width, height, argb, quality, + *cache_bits, hash_chain, + refs_trace)) { + double bit_cost_trace; + // Evaluate LZ77 coding. + VP8LHistogramCreate(histo, refs_trace, *cache_bits); + bit_cost_trace = VP8LHistogramEstimateBits(histo); + if (bit_cost_trace < bit_cost_lz77) { + best = refs_trace; + } + } + } + } else { + best = refs_rle; + } + + BackwardReferences2DLocality(width, best); + + Error: + VP8LFreeHistogram(histo); + return best; +} + +VP8LBackwardRefs* VP8LGetBackwardReferences( + int width, int height, const uint32_t* const argb, int quality, + int low_effort, int* const cache_bits, + const VP8LHashChain* const hash_chain, VP8LBackwardRefs refs_array[2]) { + if (low_effort) { + return GetBackwardReferencesLowEffort(width, height, argb, cache_bits, + hash_chain, refs_array); + } else { + return GetBackwardReferences(width, height, argb, quality, cache_bits, + hash_chain, refs_array); + } +} diff --git a/thirdparty/libwebp/enc/backward_references_enc.h b/thirdparty/libwebp/enc/backward_references_enc.h new file mode 100644 index 0000000000..3a19aa763e --- /dev/null +++ b/thirdparty/libwebp/enc/backward_references_enc.h @@ -0,0 +1,207 @@ +// Copyright 2012 Google Inc. All Rights Reserved. +// +// Use of this source code is governed by a BSD-style license +// that can be found in the COPYING file in the root of the source +// tree. An additional intellectual property rights grant can be found +// in the file PATENTS. All contributing project authors may +// be found in the AUTHORS file in the root of the source tree. +// ----------------------------------------------------------------------------- +// +// Author: Jyrki Alakuijala (jyrki@google.com) +// + +#ifndef WEBP_ENC_BACKWARD_REFERENCES_H_ +#define WEBP_ENC_BACKWARD_REFERENCES_H_ + +#include +#include +#include "../webp/types.h" +#include "../webp/format_constants.h" + +#ifdef __cplusplus +extern "C" { +#endif + +// The maximum allowed limit is 11. +#define MAX_COLOR_CACHE_BITS 10 + +// ----------------------------------------------------------------------------- +// PixOrCopy + +enum Mode { + kLiteral, + kCacheIdx, + kCopy, + kNone +}; + +typedef struct { + // mode as uint8_t to make the memory layout to be exactly 8 bytes. + uint8_t mode; + uint16_t len; + uint32_t argb_or_distance; +} PixOrCopy; + +static WEBP_INLINE PixOrCopy PixOrCopyCreateCopy(uint32_t distance, + uint16_t len) { + PixOrCopy retval; + retval.mode = kCopy; + retval.argb_or_distance = distance; + retval.len = len; + return retval; +} + +static WEBP_INLINE PixOrCopy PixOrCopyCreateCacheIdx(int idx) { + PixOrCopy retval; + assert(idx >= 0); + assert(idx < (1 << MAX_COLOR_CACHE_BITS)); + retval.mode = kCacheIdx; + retval.argb_or_distance = idx; + retval.len = 1; + return retval; +} + +static WEBP_INLINE PixOrCopy PixOrCopyCreateLiteral(uint32_t argb) { + PixOrCopy retval; + retval.mode = kLiteral; + retval.argb_or_distance = argb; + retval.len = 1; + return retval; +} + +static WEBP_INLINE int PixOrCopyIsLiteral(const PixOrCopy* const p) { + return (p->mode == kLiteral); +} + +static WEBP_INLINE int PixOrCopyIsCacheIdx(const PixOrCopy* const p) { + return (p->mode == kCacheIdx); +} + +static WEBP_INLINE int PixOrCopyIsCopy(const PixOrCopy* const p) { + return (p->mode == kCopy); +} + +static WEBP_INLINE uint32_t PixOrCopyLiteral(const PixOrCopy* const p, + int component) { + assert(p->mode == kLiteral); + return (p->argb_or_distance >> (component * 8)) & 0xff; +} + +static WEBP_INLINE uint32_t PixOrCopyLength(const PixOrCopy* const p) { + return p->len; +} + +static WEBP_INLINE uint32_t PixOrCopyArgb(const PixOrCopy* const p) { + assert(p->mode == kLiteral); + return p->argb_or_distance; +} + +static WEBP_INLINE uint32_t PixOrCopyCacheIdx(const PixOrCopy* const p) { + assert(p->mode == kCacheIdx); + assert(p->argb_or_distance < (1U << MAX_COLOR_CACHE_BITS)); + return p->argb_or_distance; +} + +static WEBP_INLINE uint32_t PixOrCopyDistance(const PixOrCopy* const p) { + assert(p->mode == kCopy); + return p->argb_or_distance; +} + +// ----------------------------------------------------------------------------- +// VP8LHashChain + +#define HASH_BITS 18 +#define HASH_SIZE (1 << HASH_BITS) + +typedef struct VP8LHashChain VP8LHashChain; +struct VP8LHashChain { + // The 20 most significant bits contain the offset at which the best match + // is found. These 20 bits are the limit defined by GetWindowSizeForHashChain + // (through WINDOW_SIZE = 1<<20). + // The lower 12 bits contain the length of the match. The 12 bit limit is + // defined in MaxFindCopyLength with MAX_LENGTH=4096. + uint32_t* offset_length_; + // This is the maximum size of the hash_chain that can be constructed. + // Typically this is the pixel count (width x height) for a given image. + int size_; +}; + +// Must be called first, to set size. +int VP8LHashChainInit(VP8LHashChain* const p, int size); +// Pre-compute the best matches for argb. +int VP8LHashChainFill(VP8LHashChain* const p, int quality, + const uint32_t* const argb, int xsize, int ysize, + int low_effort); +void VP8LHashChainClear(VP8LHashChain* const p); // release memory + +// ----------------------------------------------------------------------------- +// VP8LBackwardRefs (block-based backward-references storage) + +// maximum number of reference blocks the image will be segmented into +#define MAX_REFS_BLOCK_PER_IMAGE 16 + +typedef struct PixOrCopyBlock PixOrCopyBlock; // forward declaration +typedef struct VP8LBackwardRefs VP8LBackwardRefs; + +// Container for blocks chain +struct VP8LBackwardRefs { + int block_size_; // common block-size + int error_; // set to true if some memory error occurred + PixOrCopyBlock* refs_; // list of currently used blocks + PixOrCopyBlock** tail_; // for list recycling + PixOrCopyBlock* free_blocks_; // free-list + PixOrCopyBlock* last_block_; // used for adding new refs (internal) +}; + +// Initialize the object. 'block_size' is the common block size to store +// references (typically, width * height / MAX_REFS_BLOCK_PER_IMAGE). +void VP8LBackwardRefsInit(VP8LBackwardRefs* const refs, int block_size); +// Release memory for backward references. +void VP8LBackwardRefsClear(VP8LBackwardRefs* const refs); +// Copies the 'src' backward refs to the 'dst'. Returns 0 in case of error. +int VP8LBackwardRefsCopy(const VP8LBackwardRefs* const src, + VP8LBackwardRefs* const dst); + +// Cursor for iterating on references content +typedef struct { + // public: + PixOrCopy* cur_pos; // current position + // private: + PixOrCopyBlock* cur_block_; // current block in the refs list + const PixOrCopy* last_pos_; // sentinel for switching to next block +} VP8LRefsCursor; + +// Returns a cursor positioned at the beginning of the references list. +VP8LRefsCursor VP8LRefsCursorInit(const VP8LBackwardRefs* const refs); +// Returns true if cursor is pointing at a valid position. +static WEBP_INLINE int VP8LRefsCursorOk(const VP8LRefsCursor* const c) { + return (c->cur_pos != NULL); +} +// Move to next block of references. Internal, not to be called directly. +void VP8LRefsCursorNextBlock(VP8LRefsCursor* const c); +// Move to next position, or NULL. Should not be called if !VP8LRefsCursorOk(). +static WEBP_INLINE void VP8LRefsCursorNext(VP8LRefsCursor* const c) { + assert(c != NULL); + assert(VP8LRefsCursorOk(c)); + if (++c->cur_pos == c->last_pos_) VP8LRefsCursorNextBlock(c); +} + +// ----------------------------------------------------------------------------- +// Main entry points + +// Evaluates best possible backward references for specified quality. +// The input cache_bits to 'VP8LGetBackwardReferences' sets the maximum cache +// bits to use (passing 0 implies disabling the local color cache). +// The optimal cache bits is evaluated and set for the *cache_bits parameter. +// The return value is the pointer to the best of the two backward refs viz, +// refs[0] or refs[1]. +VP8LBackwardRefs* VP8LGetBackwardReferences( + int width, int height, const uint32_t* const argb, int quality, + int low_effort, int* const cache_bits, + const VP8LHashChain* const hash_chain, VP8LBackwardRefs refs[2]); + +#ifdef __cplusplus +} +#endif + +#endif // WEBP_ENC_BACKWARD_REFERENCES_H_ diff --git a/thirdparty/libwebp/enc/config.c b/thirdparty/libwebp/enc/config.c deleted file mode 100644 index f9f7961d58..0000000000 --- a/thirdparty/libwebp/enc/config.c +++ /dev/null @@ -1,173 +0,0 @@ -// Copyright 2011 Google Inc. All Rights Reserved. -// -// Use of this source code is governed by a BSD-style license -// that can be found in the COPYING file in the root of the source -// tree. An additional intellectual property rights grant can be found -// in the file PATENTS. All contributing project authors may -// be found in the AUTHORS file in the root of the source tree. -// ----------------------------------------------------------------------------- -// -// Coding tools configuration -// -// Author: Skal (pascal.massimino@gmail.com) - -#include "../webp/encode.h" - -//------------------------------------------------------------------------------ -// WebPConfig -//------------------------------------------------------------------------------ - -int WebPConfigInitInternal(WebPConfig* config, - WebPPreset preset, float quality, int version) { - if (WEBP_ABI_IS_INCOMPATIBLE(version, WEBP_ENCODER_ABI_VERSION)) { - return 0; // caller/system version mismatch! - } - if (config == NULL) return 0; - - config->quality = quality; - config->target_size = 0; - config->target_PSNR = 0.; - config->method = 4; - config->sns_strength = 50; - config->filter_strength = 60; // mid-filtering - config->filter_sharpness = 0; - config->filter_type = 1; // default: strong (so U/V is filtered too) - config->partitions = 0; - config->segments = 4; - config->pass = 1; - config->show_compressed = 0; - config->preprocessing = 0; - config->autofilter = 0; - config->partition_limit = 0; - config->alpha_compression = 1; - config->alpha_filtering = 1; - config->alpha_quality = 100; - config->lossless = 0; - config->exact = 0; - config->image_hint = WEBP_HINT_DEFAULT; - config->emulate_jpeg_size = 0; - config->thread_level = 0; - config->low_memory = 0; - config->near_lossless = 100; -#ifdef WEBP_EXPERIMENTAL_FEATURES - config->delta_palettization = 0; -#endif // WEBP_EXPERIMENTAL_FEATURES - - // TODO(skal): tune. - switch (preset) { - case WEBP_PRESET_PICTURE: - config->sns_strength = 80; - config->filter_sharpness = 4; - config->filter_strength = 35; - config->preprocessing &= ~2; // no dithering - break; - case WEBP_PRESET_PHOTO: - config->sns_strength = 80; - config->filter_sharpness = 3; - config->filter_strength = 30; - config->preprocessing |= 2; - break; - case WEBP_PRESET_DRAWING: - config->sns_strength = 25; - config->filter_sharpness = 6; - config->filter_strength = 10; - break; - case WEBP_PRESET_ICON: - config->sns_strength = 0; - config->filter_strength = 0; // disable filtering to retain sharpness - config->preprocessing &= ~2; // no dithering - break; - case WEBP_PRESET_TEXT: - config->sns_strength = 0; - config->filter_strength = 0; // disable filtering to retain sharpness - config->preprocessing &= ~2; // no dithering - config->segments = 2; - break; - case WEBP_PRESET_DEFAULT: - default: - break; - } - return WebPValidateConfig(config); -} - -int WebPValidateConfig(const WebPConfig* config) { - if (config == NULL) return 0; - if (config->quality < 0 || config->quality > 100) - return 0; - if (config->target_size < 0) - return 0; - if (config->target_PSNR < 0) - return 0; - if (config->method < 0 || config->method > 6) - return 0; - if (config->segments < 1 || config->segments > 4) - return 0; - if (config->sns_strength < 0 || config->sns_strength > 100) - return 0; - if (config->filter_strength < 0 || config->filter_strength > 100) - return 0; - if (config->filter_sharpness < 0 || config->filter_sharpness > 7) - return 0; - if (config->filter_type < 0 || config->filter_type > 1) - return 0; - if (config->autofilter < 0 || config->autofilter > 1) - return 0; - if (config->pass < 1 || config->pass > 10) - return 0; - if (config->show_compressed < 0 || config->show_compressed > 1) - return 0; - if (config->preprocessing < 0 || config->preprocessing > 7) - return 0; - if (config->partitions < 0 || config->partitions > 3) - return 0; - if (config->partition_limit < 0 || config->partition_limit > 100) - return 0; - if (config->alpha_compression < 0) - return 0; - if (config->alpha_filtering < 0) - return 0; - if (config->alpha_quality < 0 || config->alpha_quality > 100) - return 0; - if (config->lossless < 0 || config->lossless > 1) - return 0; - if (config->near_lossless < 0 || config->near_lossless > 100) - return 0; - if (config->image_hint >= WEBP_HINT_LAST) - return 0; - if (config->emulate_jpeg_size < 0 || config->emulate_jpeg_size > 1) - return 0; - if (config->thread_level < 0 || config->thread_level > 1) - return 0; - if (config->low_memory < 0 || config->low_memory > 1) - return 0; - if (config->exact < 0 || config->exact > 1) - return 0; -#ifdef WEBP_EXPERIMENTAL_FEATURES - if (config->delta_palettization < 0 || config->delta_palettization > 1) - return 0; -#endif // WEBP_EXPERIMENTAL_FEATURES - return 1; -} - -//------------------------------------------------------------------------------ - -#define MAX_LEVEL 9 - -// Mapping between -z level and -m / -q parameter settings. -static const struct { - uint8_t method_; - uint8_t quality_; -} kLosslessPresets[MAX_LEVEL + 1] = { - { 0, 0 }, { 1, 20 }, { 2, 25 }, { 3, 30 }, { 3, 50 }, - { 4, 50 }, { 4, 75 }, { 4, 90 }, { 5, 90 }, { 6, 100 } -}; - -int WebPConfigLosslessPreset(WebPConfig* config, int level) { - if (config == NULL || level < 0 || level > MAX_LEVEL) return 0; - config->lossless = 1; - config->method = kLosslessPresets[level].method_; - config->quality = kLosslessPresets[level].quality_; - return 1; -} - -//------------------------------------------------------------------------------ diff --git a/thirdparty/libwebp/enc/config_enc.c b/thirdparty/libwebp/enc/config_enc.c new file mode 100644 index 0000000000..4589dc0619 --- /dev/null +++ b/thirdparty/libwebp/enc/config_enc.c @@ -0,0 +1,152 @@ +// Copyright 2011 Google Inc. All Rights Reserved. +// +// Use of this source code is governed by a BSD-style license +// that can be found in the COPYING file in the root of the source +// tree. An additional intellectual property rights grant can be found +// in the file PATENTS. All contributing project authors may +// be found in the AUTHORS file in the root of the source tree. +// ----------------------------------------------------------------------------- +// +// Coding tools configuration +// +// Author: Skal (pascal.massimino@gmail.com) + +#ifdef HAVE_CONFIG_H +#include "../webp/config.h" +#endif + +#include "../webp/encode.h" + +//------------------------------------------------------------------------------ +// WebPConfig +//------------------------------------------------------------------------------ + +int WebPConfigInitInternal(WebPConfig* config, + WebPPreset preset, float quality, int version) { + if (WEBP_ABI_IS_INCOMPATIBLE(version, WEBP_ENCODER_ABI_VERSION)) { + return 0; // caller/system version mismatch! + } + if (config == NULL) return 0; + + config->quality = quality; + config->target_size = 0; + config->target_PSNR = 0.; + config->method = 4; + config->sns_strength = 50; + config->filter_strength = 60; // mid-filtering + config->filter_sharpness = 0; + config->filter_type = 1; // default: strong (so U/V is filtered too) + config->partitions = 0; + config->segments = 4; + config->pass = 1; + config->show_compressed = 0; + config->preprocessing = 0; + config->autofilter = 0; + config->partition_limit = 0; + config->alpha_compression = 1; + config->alpha_filtering = 1; + config->alpha_quality = 100; + config->lossless = 0; + config->exact = 0; + config->image_hint = WEBP_HINT_DEFAULT; + config->emulate_jpeg_size = 0; + config->thread_level = 0; + config->low_memory = 0; + config->near_lossless = 100; + config->use_delta_palette = 0; + config->use_sharp_yuv = 0; + + // TODO(skal): tune. + switch (preset) { + case WEBP_PRESET_PICTURE: + config->sns_strength = 80; + config->filter_sharpness = 4; + config->filter_strength = 35; + config->preprocessing &= ~2; // no dithering + break; + case WEBP_PRESET_PHOTO: + config->sns_strength = 80; + config->filter_sharpness = 3; + config->filter_strength = 30; + config->preprocessing |= 2; + break; + case WEBP_PRESET_DRAWING: + config->sns_strength = 25; + config->filter_sharpness = 6; + config->filter_strength = 10; + break; + case WEBP_PRESET_ICON: + config->sns_strength = 0; + config->filter_strength = 0; // disable filtering to retain sharpness + config->preprocessing &= ~2; // no dithering + break; + case WEBP_PRESET_TEXT: + config->sns_strength = 0; + config->filter_strength = 0; // disable filtering to retain sharpness + config->preprocessing &= ~2; // no dithering + config->segments = 2; + break; + case WEBP_PRESET_DEFAULT: + default: + break; + } + return WebPValidateConfig(config); +} + +int WebPValidateConfig(const WebPConfig* config) { + if (config == NULL) return 0; + if (config->quality < 0 || config->quality > 100) return 0; + if (config->target_size < 0) return 0; + if (config->target_PSNR < 0) return 0; + if (config->method < 0 || config->method > 6) return 0; + if (config->segments < 1 || config->segments > 4) return 0; + if (config->sns_strength < 0 || config->sns_strength > 100) return 0; + if (config->filter_strength < 0 || config->filter_strength > 100) return 0; + if (config->filter_sharpness < 0 || config->filter_sharpness > 7) return 0; + if (config->filter_type < 0 || config->filter_type > 1) return 0; + if (config->autofilter < 0 || config->autofilter > 1) return 0; + if (config->pass < 1 || config->pass > 10) return 0; + if (config->show_compressed < 0 || config->show_compressed > 1) return 0; + if (config->preprocessing < 0 || config->preprocessing > 7) return 0; + if (config->partitions < 0 || config->partitions > 3) return 0; + if (config->partition_limit < 0 || config->partition_limit > 100) return 0; + if (config->alpha_compression < 0) return 0; + if (config->alpha_filtering < 0) return 0; + if (config->alpha_quality < 0 || config->alpha_quality > 100) return 0; + if (config->lossless < 0 || config->lossless > 1) return 0; + if (config->near_lossless < 0 || config->near_lossless > 100) return 0; + if (config->image_hint >= WEBP_HINT_LAST) return 0; + if (config->emulate_jpeg_size < 0 || config->emulate_jpeg_size > 1) return 0; + if (config->thread_level < 0 || config->thread_level > 1) return 0; + if (config->low_memory < 0 || config->low_memory > 1) return 0; + if (config->exact < 0 || config->exact > 1) return 0; + if (config->use_delta_palette < 0 || config->use_delta_palette > 1) { + return 0; + } + if (config->use_sharp_yuv < 0 || config->use_sharp_yuv > 1) return 0; + + return 1; +} + +//------------------------------------------------------------------------------ + +#define MAX_LEVEL 9 + +// Mapping between -z level and -m / -q parameter settings. +static const struct { + uint8_t method_; + uint8_t quality_; +} kLosslessPresets[MAX_LEVEL + 1] = { + { 0, 0 }, { 1, 20 }, { 2, 25 }, { 3, 30 }, { 3, 50 }, + { 4, 50 }, { 4, 75 }, { 4, 90 }, { 5, 90 }, { 6, 100 } +}; + +int WebPConfigLosslessPreset(WebPConfig* config, int level) { + if (config == NULL || level < 0 || level > MAX_LEVEL) return 0; + config->lossless = 1; + config->method = kLosslessPresets[level].method_; + config->quality = kLosslessPresets[level].quality_; + return 1; +} + +//------------------------------------------------------------------------------ diff --git a/thirdparty/libwebp/enc/cost.c b/thirdparty/libwebp/enc/cost.c deleted file mode 100644 index 87f89378a7..0000000000 --- a/thirdparty/libwebp/enc/cost.c +++ /dev/null @@ -1,342 +0,0 @@ -// Copyright 2011 Google Inc. All Rights Reserved. -// -// Use of this source code is governed by a BSD-style license -// that can be found in the COPYING file in the root of the source -// tree. An additional intellectual property rights grant can be found -// in the file PATENTS. All contributing project authors may -// be found in the AUTHORS file in the root of the source tree. -// ----------------------------------------------------------------------------- -// -// Cost tables for level and modes -// -// Author: Skal (pascal.massimino@gmail.com) - -#include "./cost.h" - -//------------------------------------------------------------------------------ -// Level cost tables - -// For each given level, the following table gives the pattern of contexts to -// use for coding it (in [][0]) as well as the bit value to use for each -// context (in [][1]). -const uint16_t VP8LevelCodes[MAX_VARIABLE_LEVEL][2] = { - {0x001, 0x000}, {0x007, 0x001}, {0x00f, 0x005}, - {0x00f, 0x00d}, {0x033, 0x003}, {0x033, 0x003}, {0x033, 0x023}, - {0x033, 0x023}, {0x033, 0x023}, {0x033, 0x023}, {0x0d3, 0x013}, - {0x0d3, 0x013}, {0x0d3, 0x013}, {0x0d3, 0x013}, {0x0d3, 0x013}, - {0x0d3, 0x013}, {0x0d3, 0x013}, {0x0d3, 0x013}, {0x0d3, 0x093}, - {0x0d3, 0x093}, {0x0d3, 0x093}, {0x0d3, 0x093}, {0x0d3, 0x093}, - {0x0d3, 0x093}, {0x0d3, 0x093}, {0x0d3, 0x093}, {0x0d3, 0x093}, - {0x0d3, 0x093}, {0x0d3, 0x093}, {0x0d3, 0x093}, {0x0d3, 0x093}, - {0x0d3, 0x093}, {0x0d3, 0x093}, {0x0d3, 0x093}, {0x153, 0x053}, - {0x153, 0x053}, {0x153, 0x053}, {0x153, 0x053}, {0x153, 0x053}, - {0x153, 0x053}, {0x153, 0x053}, {0x153, 0x053}, {0x153, 0x053}, - {0x153, 0x053}, {0x153, 0x053}, {0x153, 0x053}, {0x153, 0x053}, - {0x153, 0x053}, {0x153, 0x053}, {0x153, 0x053}, {0x153, 0x053}, - {0x153, 0x053}, {0x153, 0x053}, {0x153, 0x053}, {0x153, 0x053}, - {0x153, 0x053}, {0x153, 0x053}, {0x153, 0x053}, {0x153, 0x053}, - {0x153, 0x053}, {0x153, 0x053}, {0x153, 0x053}, {0x153, 0x053}, - {0x153, 0x053}, {0x153, 0x053}, {0x153, 0x053}, {0x153, 0x153} -}; - -static int VariableLevelCost(int level, const uint8_t probas[NUM_PROBAS]) { - int pattern = VP8LevelCodes[level - 1][0]; - int bits = VP8LevelCodes[level - 1][1]; - int cost = 0; - int i; - for (i = 2; pattern; ++i) { - if (pattern & 1) { - cost += VP8BitCost(bits & 1, probas[i]); - } - bits >>= 1; - pattern >>= 1; - } - return cost; -} - -//------------------------------------------------------------------------------ -// Pre-calc level costs once for all - -void VP8CalculateLevelCosts(VP8EncProba* const proba) { - int ctype, band, ctx; - - if (!proba->dirty_) return; // nothing to do. - - for (ctype = 0; ctype < NUM_TYPES; ++ctype) { - int n; - for (band = 0; band < NUM_BANDS; ++band) { - for (ctx = 0; ctx < NUM_CTX; ++ctx) { - const uint8_t* const p = proba->coeffs_[ctype][band][ctx]; - uint16_t* const table = proba->level_cost_[ctype][band][ctx]; - const int cost0 = (ctx > 0) ? VP8BitCost(1, p[0]) : 0; - const int cost_base = VP8BitCost(1, p[1]) + cost0; - int v; - table[0] = VP8BitCost(0, p[1]) + cost0; - for (v = 1; v <= MAX_VARIABLE_LEVEL; ++v) { - table[v] = cost_base + VariableLevelCost(v, p); - } - // Starting at level 67 and up, the variable part of the cost is - // actually constant. - } - } - for (n = 0; n < 16; ++n) { // replicate bands. We don't need to sentinel. - for (ctx = 0; ctx < NUM_CTX; ++ctx) { - proba->remapped_costs_[ctype][n][ctx] = - proba->level_cost_[ctype][VP8EncBands[n]][ctx]; - } - } - } - proba->dirty_ = 0; -} - -//------------------------------------------------------------------------------ -// Mode cost tables. - -// These are the fixed probabilities (in the coding trees) turned into bit-cost -// by calling VP8BitCost(). -const uint16_t VP8FixedCostsUV[4] = { 302, 984, 439, 642 }; -// note: these values include the fixed VP8BitCost(1, 145) mode selection cost. -const uint16_t VP8FixedCostsI16[4] = { 663, 919, 872, 919 }; -const uint16_t VP8FixedCostsI4[NUM_BMODES][NUM_BMODES][NUM_BMODES] = { - { { 40, 1151, 1723, 1874, 2103, 2019, 1628, 1777, 2226, 2137 }, - { 192, 469, 1296, 1308, 1849, 1794, 1781, 1703, 1713, 1522 }, - { 142, 910, 762, 1684, 1849, 1576, 1460, 1305, 1801, 1657 }, - { 559, 641, 1370, 421, 1182, 1569, 1612, 1725, 863, 1007 }, - { 299, 1059, 1256, 1108, 636, 1068, 1581, 1883, 869, 1142 }, - { 277, 1111, 707, 1362, 1089, 672, 1603, 1541, 1545, 1291 }, - { 214, 781, 1609, 1303, 1632, 2229, 726, 1560, 1713, 918 }, - { 152, 1037, 1046, 1759, 1983, 2174, 1358, 742, 1740, 1390 }, - { 512, 1046, 1420, 753, 752, 1297, 1486, 1613, 460, 1207 }, - { 424, 827, 1362, 719, 1462, 1202, 1199, 1476, 1199, 538 } }, - { { 240, 402, 1134, 1491, 1659, 1505, 1517, 1555, 1979, 2099 }, - { 467, 242, 960, 1232, 1714, 1620, 1834, 1570, 1676, 1391 }, - { 500, 455, 463, 1507, 1699, 1282, 1564, 982, 2114, 2114 }, - { 672, 643, 1372, 331, 1589, 1667, 1453, 1938, 996, 876 }, - { 458, 783, 1037, 911, 738, 968, 1165, 1518, 859, 1033 }, - { 504, 815, 504, 1139, 1219, 719, 1506, 1085, 1268, 1268 }, - { 333, 630, 1445, 1239, 1883, 3672, 799, 1548, 1865, 598 }, - { 399, 644, 746, 1342, 1856, 1350, 1493, 613, 1855, 1015 }, - { 622, 749, 1205, 608, 1066, 1408, 1290, 1406, 546, 971 }, - { 500, 753, 1041, 668, 1230, 1617, 1297, 1425, 1383, 523 } }, - { { 394, 553, 523, 1502, 1536, 981, 1608, 1142, 1666, 2181 }, - { 655, 430, 375, 1411, 1861, 1220, 1677, 1135, 1978, 1553 }, - { 690, 640, 245, 1954, 2070, 1194, 1528, 982, 1972, 2232 }, - { 559, 834, 741, 867, 1131, 980, 1225, 852, 1092, 784 }, - { 690, 875, 516, 959, 673, 894, 1056, 1190, 1528, 1126 }, - { 740, 951, 384, 1277, 1177, 492, 1579, 1155, 1846, 1513 }, - { 323, 775, 1062, 1776, 3062, 1274, 813, 1188, 1372, 655 }, - { 488, 971, 484, 1767, 1515, 1775, 1115, 503, 1539, 1461 }, - { 740, 1006, 998, 709, 851, 1230, 1337, 788, 741, 721 }, - { 522, 1073, 573, 1045, 1346, 887, 1046, 1146, 1203, 697 } }, - { { 105, 864, 1442, 1009, 1934, 1840, 1519, 1920, 1673, 1579 }, - { 534, 305, 1193, 683, 1388, 2164, 1802, 1894, 1264, 1170 }, - { 305, 518, 877, 1108, 1426, 3215, 1425, 1064, 1320, 1242 }, - { 683, 732, 1927, 257, 1493, 2048, 1858, 1552, 1055, 947 }, - { 394, 814, 1024, 660, 959, 1556, 1282, 1289, 893, 1047 }, - { 528, 615, 996, 940, 1201, 635, 1094, 2515, 803, 1358 }, - { 347, 614, 1609, 1187, 3133, 1345, 1007, 1339, 1017, 667 }, - { 218, 740, 878, 1605, 3650, 3650, 1345, 758, 1357, 1617 }, - { 672, 750, 1541, 558, 1257, 1599, 1870, 2135, 402, 1087 }, - { 592, 684, 1161, 430, 1092, 1497, 1475, 1489, 1095, 822 } }, - { { 228, 1056, 1059, 1368, 752, 982, 1512, 1518, 987, 1782 }, - { 494, 514, 818, 942, 965, 892, 1610, 1356, 1048, 1363 }, - { 512, 648, 591, 1042, 761, 991, 1196, 1454, 1309, 1463 }, - { 683, 749, 1043, 676, 841, 1396, 1133, 1138, 654, 939 }, - { 622, 1101, 1126, 994, 361, 1077, 1203, 1318, 877, 1219 }, - { 631, 1068, 857, 1650, 651, 477, 1650, 1419, 828, 1170 }, - { 555, 727, 1068, 1335, 3127, 1339, 820, 1331, 1077, 429 }, - { 504, 879, 624, 1398, 889, 889, 1392, 808, 891, 1406 }, - { 683, 1602, 1289, 977, 578, 983, 1280, 1708, 406, 1122 }, - { 399, 865, 1433, 1070, 1072, 764, 968, 1477, 1223, 678 } }, - { { 333, 760, 935, 1638, 1010, 529, 1646, 1410, 1472, 2219 }, - { 512, 494, 750, 1160, 1215, 610, 1870, 1868, 1628, 1169 }, - { 572, 646, 492, 1934, 1208, 603, 1580, 1099, 1398, 1995 }, - { 786, 789, 942, 581, 1018, 951, 1599, 1207, 731, 768 }, - { 690, 1015, 672, 1078, 582, 504, 1693, 1438, 1108, 2897 }, - { 768, 1267, 571, 2005, 1243, 244, 2881, 1380, 1786, 1453 }, - { 452, 899, 1293, 903, 1311, 3100, 465, 1311, 1319, 813 }, - { 394, 927, 942, 1103, 1358, 1104, 946, 593, 1363, 1109 }, - { 559, 1005, 1007, 1016, 658, 1173, 1021, 1164, 623, 1028 }, - { 564, 796, 632, 1005, 1014, 863, 2316, 1268, 938, 764 } }, - { { 266, 606, 1098, 1228, 1497, 1243, 948, 1030, 1734, 1461 }, - { 366, 585, 901, 1060, 1407, 1247, 876, 1134, 1620, 1054 }, - { 452, 565, 542, 1729, 1479, 1479, 1016, 886, 2938, 1150 }, - { 555, 1088, 1533, 950, 1354, 895, 834, 1019, 1021, 496 }, - { 704, 815, 1193, 971, 973, 640, 1217, 2214, 832, 578 }, - { 672, 1245, 579, 871, 875, 774, 872, 1273, 1027, 949 }, - { 296, 1134, 2050, 1784, 1636, 3425, 442, 1550, 2076, 722 }, - { 342, 982, 1259, 1846, 1848, 1848, 622, 568, 1847, 1052 }, - { 555, 1064, 1304, 828, 746, 1343, 1075, 1329, 1078, 494 }, - { 288, 1167, 1285, 1174, 1639, 1639, 833, 2254, 1304, 509 } }, - { { 342, 719, 767, 1866, 1757, 1270, 1246, 550, 1746, 2151 }, - { 483, 653, 694, 1509, 1459, 1410, 1218, 507, 1914, 1266 }, - { 488, 757, 447, 2979, 1813, 1268, 1654, 539, 1849, 2109 }, - { 522, 1097, 1085, 851, 1365, 1111, 851, 901, 961, 605 }, - { 709, 716, 841, 728, 736, 945, 941, 862, 2845, 1057 }, - { 512, 1323, 500, 1336, 1083, 681, 1342, 717, 1604, 1350 }, - { 452, 1155, 1372, 1900, 1501, 3290, 311, 944, 1919, 922 }, - { 403, 1520, 977, 2132, 1733, 3522, 1076, 276, 3335, 1547 }, - { 559, 1374, 1101, 615, 673, 2462, 974, 795, 984, 984 }, - { 547, 1122, 1062, 812, 1410, 951, 1140, 622, 1268, 651 } }, - { { 165, 982, 1235, 938, 1334, 1366, 1659, 1578, 964, 1612 }, - { 592, 422, 925, 847, 1139, 1112, 1387, 2036, 861, 1041 }, - { 403, 837, 732, 770, 941, 1658, 1250, 809, 1407, 1407 }, - { 896, 874, 1071, 381, 1568, 1722, 1437, 2192, 480, 1035 }, - { 640, 1098, 1012, 1032, 684, 1382, 1581, 2106, 416, 865 }, - { 559, 1005, 819, 914, 710, 770, 1418, 920, 838, 1435 }, - { 415, 1258, 1245, 870, 1278, 3067, 770, 1021, 1287, 522 }, - { 406, 990, 601, 1009, 1265, 1265, 1267, 759, 1017, 1277 }, - { 968, 1182, 1329, 788, 1032, 1292, 1705, 1714, 203, 1403 }, - { 732, 877, 1279, 471, 901, 1161, 1545, 1294, 755, 755 } }, - { { 111, 931, 1378, 1185, 1933, 1648, 1148, 1714, 1873, 1307 }, - { 406, 414, 1030, 1023, 1910, 1404, 1313, 1647, 1509, 793 }, - { 342, 640, 575, 1088, 1241, 1349, 1161, 1350, 1756, 1502 }, - { 559, 766, 1185, 357, 1682, 1428, 1329, 1897, 1219, 802 }, - { 473, 909, 1164, 771, 719, 2508, 1427, 1432, 722, 782 }, - { 342, 892, 785, 1145, 1150, 794, 1296, 1550, 973, 1057 }, - { 208, 1036, 1326, 1343, 1606, 3395, 815, 1455, 1618, 712 }, - { 228, 928, 890, 1046, 3499, 1711, 994, 829, 1720, 1318 }, - { 768, 724, 1058, 636, 991, 1075, 1319, 1324, 616, 825 }, - { 305, 1167, 1358, 899, 1587, 1587, 987, 1988, 1332, 501 } } -}; - -//------------------------------------------------------------------------------ -// helper functions for residuals struct VP8Residual. - -void VP8InitResidual(int first, int coeff_type, - VP8Encoder* const enc, VP8Residual* const res) { - res->coeff_type = coeff_type; - res->prob = enc->proba_.coeffs_[coeff_type]; - res->stats = enc->proba_.stats_[coeff_type]; - res->costs = enc->proba_.remapped_costs_[coeff_type]; - res->first = first; -} - -//------------------------------------------------------------------------------ -// Mode costs - -int VP8GetCostLuma4(VP8EncIterator* const it, const int16_t levels[16]) { - const int x = (it->i4_ & 3), y = (it->i4_ >> 2); - VP8Residual res; - VP8Encoder* const enc = it->enc_; - int R = 0; - int ctx; - - VP8InitResidual(0, 3, enc, &res); - ctx = it->top_nz_[x] + it->left_nz_[y]; - VP8SetResidualCoeffs(levels, &res); - R += VP8GetResidualCost(ctx, &res); - return R; -} - -int VP8GetCostLuma16(VP8EncIterator* const it, const VP8ModeScore* const rd) { - VP8Residual res; - VP8Encoder* const enc = it->enc_; - int x, y; - int R = 0; - - VP8IteratorNzToBytes(it); // re-import the non-zero context - - // DC - VP8InitResidual(0, 1, enc, &res); - VP8SetResidualCoeffs(rd->y_dc_levels, &res); - R += VP8GetResidualCost(it->top_nz_[8] + it->left_nz_[8], &res); - - // AC - VP8InitResidual(1, 0, enc, &res); - for (y = 0; y < 4; ++y) { - for (x = 0; x < 4; ++x) { - const int ctx = it->top_nz_[x] + it->left_nz_[y]; - VP8SetResidualCoeffs(rd->y_ac_levels[x + y * 4], &res); - R += VP8GetResidualCost(ctx, &res); - it->top_nz_[x] = it->left_nz_[y] = (res.last >= 0); - } - } - return R; -} - -int VP8GetCostUV(VP8EncIterator* const it, const VP8ModeScore* const rd) { - VP8Residual res; - VP8Encoder* const enc = it->enc_; - int ch, x, y; - int R = 0; - - VP8IteratorNzToBytes(it); // re-import the non-zero context - - VP8InitResidual(0, 2, enc, &res); - for (ch = 0; ch <= 2; ch += 2) { - for (y = 0; y < 2; ++y) { - for (x = 0; x < 2; ++x) { - const int ctx = it->top_nz_[4 + ch + x] + it->left_nz_[4 + ch + y]; - VP8SetResidualCoeffs(rd->uv_levels[ch * 2 + x + y * 2], &res); - R += VP8GetResidualCost(ctx, &res); - it->top_nz_[4 + ch + x] = it->left_nz_[4 + ch + y] = (res.last >= 0); - } - } - } - return R; -} - - -//------------------------------------------------------------------------------ -// Recording of token probabilities. - -// We keep the table-free variant around for reference, in case. -#define USE_LEVEL_CODE_TABLE - -// Simulate block coding, but only record statistics. -// Note: no need to record the fixed probas. -int VP8RecordCoeffs(int ctx, const VP8Residual* const res) { - int n = res->first; - // should be stats[VP8EncBands[n]], but it's equivalent for n=0 or 1 - proba_t* s = res->stats[n][ctx]; - if (res->last < 0) { - VP8RecordStats(0, s + 0); - return 0; - } - while (n <= res->last) { - int v; - VP8RecordStats(1, s + 0); // order of record doesn't matter - while ((v = res->coeffs[n++]) == 0) { - VP8RecordStats(0, s + 1); - s = res->stats[VP8EncBands[n]][0]; - } - VP8RecordStats(1, s + 1); - if (!VP8RecordStats(2u < (unsigned int)(v + 1), s + 2)) { // v = -1 or 1 - s = res->stats[VP8EncBands[n]][1]; - } else { - v = abs(v); -#if !defined(USE_LEVEL_CODE_TABLE) - if (!VP8RecordStats(v > 4, s + 3)) { - if (VP8RecordStats(v != 2, s + 4)) - VP8RecordStats(v == 4, s + 5); - } else if (!VP8RecordStats(v > 10, s + 6)) { - VP8RecordStats(v > 6, s + 7); - } else if (!VP8RecordStats((v >= 3 + (8 << 2)), s + 8)) { - VP8RecordStats((v >= 3 + (8 << 1)), s + 9); - } else { - VP8RecordStats((v >= 3 + (8 << 3)), s + 10); - } -#else - if (v > MAX_VARIABLE_LEVEL) { - v = MAX_VARIABLE_LEVEL; - } - - { - const int bits = VP8LevelCodes[v - 1][1]; - int pattern = VP8LevelCodes[v - 1][0]; - int i; - for (i = 0; (pattern >>= 1) != 0; ++i) { - const int mask = 2 << i; - if (pattern & 1) VP8RecordStats(!!(bits & mask), s + 3 + i); - } - } -#endif - s = res->stats[VP8EncBands[n]][2]; - } - } - if (n < 16) VP8RecordStats(0, s + 0); - return 1; -} - -//------------------------------------------------------------------------------ diff --git a/thirdparty/libwebp/enc/cost.h b/thirdparty/libwebp/enc/cost.h deleted file mode 100644 index ad7959feb4..0000000000 --- a/thirdparty/libwebp/enc/cost.h +++ /dev/null @@ -1,82 +0,0 @@ -// Copyright 2011 Google Inc. All Rights Reserved. -// -// Use of this source code is governed by a BSD-style license -// that can be found in the COPYING file in the root of the source -// tree. An additional intellectual property rights grant can be found -// in the file PATENTS. All contributing project authors may -// be found in the AUTHORS file in the root of the source tree. -// ----------------------------------------------------------------------------- -// -// Cost tables for level and modes. -// -// Author: Skal (pascal.massimino@gmail.com) - -#ifndef WEBP_ENC_COST_H_ -#define WEBP_ENC_COST_H_ - -#include -#include -#include "./vp8enci.h" - -#ifdef __cplusplus -extern "C" { -#endif - -// On-the-fly info about the current set of residuals. Handy to avoid -// passing zillions of params. -typedef struct VP8Residual VP8Residual; -struct VP8Residual { - int first; - int last; - const int16_t* coeffs; - - int coeff_type; - ProbaArray* prob; - StatsArray* stats; - CostArrayPtr costs; -}; - -void VP8InitResidual(int first, int coeff_type, - VP8Encoder* const enc, VP8Residual* const res); - -int VP8RecordCoeffs(int ctx, const VP8Residual* const res); - -// Record proba context used. -static WEBP_INLINE int VP8RecordStats(int bit, proba_t* const stats) { - proba_t p = *stats; - // An overflow is inbound. Note we handle this at 0xfffe0000u instead of - // 0xffff0000u to make sure p + 1u does not overflow. - if (p >= 0xfffe0000u) { - p = ((p + 1u) >> 1) & 0x7fff7fffu; // -> divide the stats by 2. - } - // record bit count (lower 16 bits) and increment total count (upper 16 bits). - p += 0x00010000u + bit; - *stats = p; - return bit; -} - -// Cost of coding one event with probability 'proba'. -static WEBP_INLINE int VP8BitCost(int bit, uint8_t proba) { - return !bit ? VP8EntropyCost[proba] : VP8EntropyCost[255 - proba]; -} - -// Level cost calculations -extern const uint16_t VP8LevelCodes[MAX_VARIABLE_LEVEL][2]; -void VP8CalculateLevelCosts(VP8EncProba* const proba); -static WEBP_INLINE int VP8LevelCost(const uint16_t* const table, int level) { - return VP8LevelFixedCosts[level] - + table[(level > MAX_VARIABLE_LEVEL) ? MAX_VARIABLE_LEVEL : level]; -} - -// Mode costs -extern const uint16_t VP8FixedCostsUV[4]; -extern const uint16_t VP8FixedCostsI16[4]; -extern const uint16_t VP8FixedCostsI4[NUM_BMODES][NUM_BMODES][NUM_BMODES]; - -//------------------------------------------------------------------------------ - -#ifdef __cplusplus -} // extern "C" -#endif - -#endif /* WEBP_ENC_COST_H_ */ diff --git a/thirdparty/libwebp/enc/cost_enc.c b/thirdparty/libwebp/enc/cost_enc.c new file mode 100644 index 0000000000..c823f5a664 --- /dev/null +++ b/thirdparty/libwebp/enc/cost_enc.c @@ -0,0 +1,342 @@ +// Copyright 2011 Google Inc. All Rights Reserved. +// +// Use of this source code is governed by a BSD-style license +// that can be found in the COPYING file in the root of the source +// tree. An additional intellectual property rights grant can be found +// in the file PATENTS. All contributing project authors may +// be found in the AUTHORS file in the root of the source tree. +// ----------------------------------------------------------------------------- +// +// Cost tables for level and modes +// +// Author: Skal (pascal.massimino@gmail.com) + +#include "./cost_enc.h" + +//------------------------------------------------------------------------------ +// Level cost tables + +// For each given level, the following table gives the pattern of contexts to +// use for coding it (in [][0]) as well as the bit value to use for each +// context (in [][1]). +const uint16_t VP8LevelCodes[MAX_VARIABLE_LEVEL][2] = { + {0x001, 0x000}, {0x007, 0x001}, {0x00f, 0x005}, + {0x00f, 0x00d}, {0x033, 0x003}, {0x033, 0x003}, {0x033, 0x023}, + {0x033, 0x023}, {0x033, 0x023}, {0x033, 0x023}, {0x0d3, 0x013}, + {0x0d3, 0x013}, {0x0d3, 0x013}, {0x0d3, 0x013}, {0x0d3, 0x013}, + {0x0d3, 0x013}, {0x0d3, 0x013}, {0x0d3, 0x013}, {0x0d3, 0x093}, + {0x0d3, 0x093}, {0x0d3, 0x093}, {0x0d3, 0x093}, {0x0d3, 0x093}, + {0x0d3, 0x093}, {0x0d3, 0x093}, {0x0d3, 0x093}, {0x0d3, 0x093}, + {0x0d3, 0x093}, {0x0d3, 0x093}, {0x0d3, 0x093}, {0x0d3, 0x093}, + {0x0d3, 0x093}, {0x0d3, 0x093}, {0x0d3, 0x093}, {0x153, 0x053}, + {0x153, 0x053}, {0x153, 0x053}, {0x153, 0x053}, {0x153, 0x053}, + {0x153, 0x053}, {0x153, 0x053}, {0x153, 0x053}, {0x153, 0x053}, + {0x153, 0x053}, {0x153, 0x053}, {0x153, 0x053}, {0x153, 0x053}, + {0x153, 0x053}, {0x153, 0x053}, {0x153, 0x053}, {0x153, 0x053}, + {0x153, 0x053}, {0x153, 0x053}, {0x153, 0x053}, {0x153, 0x053}, + {0x153, 0x053}, {0x153, 0x053}, {0x153, 0x053}, {0x153, 0x053}, + {0x153, 0x053}, {0x153, 0x053}, {0x153, 0x053}, {0x153, 0x053}, + {0x153, 0x053}, {0x153, 0x053}, {0x153, 0x053}, {0x153, 0x153} +}; + +static int VariableLevelCost(int level, const uint8_t probas[NUM_PROBAS]) { + int pattern = VP8LevelCodes[level - 1][0]; + int bits = VP8LevelCodes[level - 1][1]; + int cost = 0; + int i; + for (i = 2; pattern; ++i) { + if (pattern & 1) { + cost += VP8BitCost(bits & 1, probas[i]); + } + bits >>= 1; + pattern >>= 1; + } + return cost; +} + +//------------------------------------------------------------------------------ +// Pre-calc level costs once for all + +void VP8CalculateLevelCosts(VP8EncProba* const proba) { + int ctype, band, ctx; + + if (!proba->dirty_) return; // nothing to do. + + for (ctype = 0; ctype < NUM_TYPES; ++ctype) { + int n; + for (band = 0; band < NUM_BANDS; ++band) { + for (ctx = 0; ctx < NUM_CTX; ++ctx) { + const uint8_t* const p = proba->coeffs_[ctype][band][ctx]; + uint16_t* const table = proba->level_cost_[ctype][band][ctx]; + const int cost0 = (ctx > 0) ? VP8BitCost(1, p[0]) : 0; + const int cost_base = VP8BitCost(1, p[1]) + cost0; + int v; + table[0] = VP8BitCost(0, p[1]) + cost0; + for (v = 1; v <= MAX_VARIABLE_LEVEL; ++v) { + table[v] = cost_base + VariableLevelCost(v, p); + } + // Starting at level 67 and up, the variable part of the cost is + // actually constant. + } + } + for (n = 0; n < 16; ++n) { // replicate bands. We don't need to sentinel. + for (ctx = 0; ctx < NUM_CTX; ++ctx) { + proba->remapped_costs_[ctype][n][ctx] = + proba->level_cost_[ctype][VP8EncBands[n]][ctx]; + } + } + } + proba->dirty_ = 0; +} + +//------------------------------------------------------------------------------ +// Mode cost tables. + +// These are the fixed probabilities (in the coding trees) turned into bit-cost +// by calling VP8BitCost(). +const uint16_t VP8FixedCostsUV[4] = { 302, 984, 439, 642 }; +// note: these values include the fixed VP8BitCost(1, 145) mode selection cost. +const uint16_t VP8FixedCostsI16[4] = { 663, 919, 872, 919 }; +const uint16_t VP8FixedCostsI4[NUM_BMODES][NUM_BMODES][NUM_BMODES] = { + { { 40, 1151, 1723, 1874, 2103, 2019, 1628, 1777, 2226, 2137 }, + { 192, 469, 1296, 1308, 1849, 1794, 1781, 1703, 1713, 1522 }, + { 142, 910, 762, 1684, 1849, 1576, 1460, 1305, 1801, 1657 }, + { 559, 641, 1370, 421, 1182, 1569, 1612, 1725, 863, 1007 }, + { 299, 1059, 1256, 1108, 636, 1068, 1581, 1883, 869, 1142 }, + { 277, 1111, 707, 1362, 1089, 672, 1603, 1541, 1545, 1291 }, + { 214, 781, 1609, 1303, 1632, 2229, 726, 1560, 1713, 918 }, + { 152, 1037, 1046, 1759, 1983, 2174, 1358, 742, 1740, 1390 }, + { 512, 1046, 1420, 753, 752, 1297, 1486, 1613, 460, 1207 }, + { 424, 827, 1362, 719, 1462, 1202, 1199, 1476, 1199, 538 } }, + { { 240, 402, 1134, 1491, 1659, 1505, 1517, 1555, 1979, 2099 }, + { 467, 242, 960, 1232, 1714, 1620, 1834, 1570, 1676, 1391 }, + { 500, 455, 463, 1507, 1699, 1282, 1564, 982, 2114, 2114 }, + { 672, 643, 1372, 331, 1589, 1667, 1453, 1938, 996, 876 }, + { 458, 783, 1037, 911, 738, 968, 1165, 1518, 859, 1033 }, + { 504, 815, 504, 1139, 1219, 719, 1506, 1085, 1268, 1268 }, + { 333, 630, 1445, 1239, 1883, 3672, 799, 1548, 1865, 598 }, + { 399, 644, 746, 1342, 1856, 1350, 1493, 613, 1855, 1015 }, + { 622, 749, 1205, 608, 1066, 1408, 1290, 1406, 546, 971 }, + { 500, 753, 1041, 668, 1230, 1617, 1297, 1425, 1383, 523 } }, + { { 394, 553, 523, 1502, 1536, 981, 1608, 1142, 1666, 2181 }, + { 655, 430, 375, 1411, 1861, 1220, 1677, 1135, 1978, 1553 }, + { 690, 640, 245, 1954, 2070, 1194, 1528, 982, 1972, 2232 }, + { 559, 834, 741, 867, 1131, 980, 1225, 852, 1092, 784 }, + { 690, 875, 516, 959, 673, 894, 1056, 1190, 1528, 1126 }, + { 740, 951, 384, 1277, 1177, 492, 1579, 1155, 1846, 1513 }, + { 323, 775, 1062, 1776, 3062, 1274, 813, 1188, 1372, 655 }, + { 488, 971, 484, 1767, 1515, 1775, 1115, 503, 1539, 1461 }, + { 740, 1006, 998, 709, 851, 1230, 1337, 788, 741, 721 }, + { 522, 1073, 573, 1045, 1346, 887, 1046, 1146, 1203, 697 } }, + { { 105, 864, 1442, 1009, 1934, 1840, 1519, 1920, 1673, 1579 }, + { 534, 305, 1193, 683, 1388, 2164, 1802, 1894, 1264, 1170 }, + { 305, 518, 877, 1108, 1426, 3215, 1425, 1064, 1320, 1242 }, + { 683, 732, 1927, 257, 1493, 2048, 1858, 1552, 1055, 947 }, + { 394, 814, 1024, 660, 959, 1556, 1282, 1289, 893, 1047 }, + { 528, 615, 996, 940, 1201, 635, 1094, 2515, 803, 1358 }, + { 347, 614, 1609, 1187, 3133, 1345, 1007, 1339, 1017, 667 }, + { 218, 740, 878, 1605, 3650, 3650, 1345, 758, 1357, 1617 }, + { 672, 750, 1541, 558, 1257, 1599, 1870, 2135, 402, 1087 }, + { 592, 684, 1161, 430, 1092, 1497, 1475, 1489, 1095, 822 } }, + { { 228, 1056, 1059, 1368, 752, 982, 1512, 1518, 987, 1782 }, + { 494, 514, 818, 942, 965, 892, 1610, 1356, 1048, 1363 }, + { 512, 648, 591, 1042, 761, 991, 1196, 1454, 1309, 1463 }, + { 683, 749, 1043, 676, 841, 1396, 1133, 1138, 654, 939 }, + { 622, 1101, 1126, 994, 361, 1077, 1203, 1318, 877, 1219 }, + { 631, 1068, 857, 1650, 651, 477, 1650, 1419, 828, 1170 }, + { 555, 727, 1068, 1335, 3127, 1339, 820, 1331, 1077, 429 }, + { 504, 879, 624, 1398, 889, 889, 1392, 808, 891, 1406 }, + { 683, 1602, 1289, 977, 578, 983, 1280, 1708, 406, 1122 }, + { 399, 865, 1433, 1070, 1072, 764, 968, 1477, 1223, 678 } }, + { { 333, 760, 935, 1638, 1010, 529, 1646, 1410, 1472, 2219 }, + { 512, 494, 750, 1160, 1215, 610, 1870, 1868, 1628, 1169 }, + { 572, 646, 492, 1934, 1208, 603, 1580, 1099, 1398, 1995 }, + { 786, 789, 942, 581, 1018, 951, 1599, 1207, 731, 768 }, + { 690, 1015, 672, 1078, 582, 504, 1693, 1438, 1108, 2897 }, + { 768, 1267, 571, 2005, 1243, 244, 2881, 1380, 1786, 1453 }, + { 452, 899, 1293, 903, 1311, 3100, 465, 1311, 1319, 813 }, + { 394, 927, 942, 1103, 1358, 1104, 946, 593, 1363, 1109 }, + { 559, 1005, 1007, 1016, 658, 1173, 1021, 1164, 623, 1028 }, + { 564, 796, 632, 1005, 1014, 863, 2316, 1268, 938, 764 } }, + { { 266, 606, 1098, 1228, 1497, 1243, 948, 1030, 1734, 1461 }, + { 366, 585, 901, 1060, 1407, 1247, 876, 1134, 1620, 1054 }, + { 452, 565, 542, 1729, 1479, 1479, 1016, 886, 2938, 1150 }, + { 555, 1088, 1533, 950, 1354, 895, 834, 1019, 1021, 496 }, + { 704, 815, 1193, 971, 973, 640, 1217, 2214, 832, 578 }, + { 672, 1245, 579, 871, 875, 774, 872, 1273, 1027, 949 }, + { 296, 1134, 2050, 1784, 1636, 3425, 442, 1550, 2076, 722 }, + { 342, 982, 1259, 1846, 1848, 1848, 622, 568, 1847, 1052 }, + { 555, 1064, 1304, 828, 746, 1343, 1075, 1329, 1078, 494 }, + { 288, 1167, 1285, 1174, 1639, 1639, 833, 2254, 1304, 509 } }, + { { 342, 719, 767, 1866, 1757, 1270, 1246, 550, 1746, 2151 }, + { 483, 653, 694, 1509, 1459, 1410, 1218, 507, 1914, 1266 }, + { 488, 757, 447, 2979, 1813, 1268, 1654, 539, 1849, 2109 }, + { 522, 1097, 1085, 851, 1365, 1111, 851, 901, 961, 605 }, + { 709, 716, 841, 728, 736, 945, 941, 862, 2845, 1057 }, + { 512, 1323, 500, 1336, 1083, 681, 1342, 717, 1604, 1350 }, + { 452, 1155, 1372, 1900, 1501, 3290, 311, 944, 1919, 922 }, + { 403, 1520, 977, 2132, 1733, 3522, 1076, 276, 3335, 1547 }, + { 559, 1374, 1101, 615, 673, 2462, 974, 795, 984, 984 }, + { 547, 1122, 1062, 812, 1410, 951, 1140, 622, 1268, 651 } }, + { { 165, 982, 1235, 938, 1334, 1366, 1659, 1578, 964, 1612 }, + { 592, 422, 925, 847, 1139, 1112, 1387, 2036, 861, 1041 }, + { 403, 837, 732, 770, 941, 1658, 1250, 809, 1407, 1407 }, + { 896, 874, 1071, 381, 1568, 1722, 1437, 2192, 480, 1035 }, + { 640, 1098, 1012, 1032, 684, 1382, 1581, 2106, 416, 865 }, + { 559, 1005, 819, 914, 710, 770, 1418, 920, 838, 1435 }, + { 415, 1258, 1245, 870, 1278, 3067, 770, 1021, 1287, 522 }, + { 406, 990, 601, 1009, 1265, 1265, 1267, 759, 1017, 1277 }, + { 968, 1182, 1329, 788, 1032, 1292, 1705, 1714, 203, 1403 }, + { 732, 877, 1279, 471, 901, 1161, 1545, 1294, 755, 755 } }, + { { 111, 931, 1378, 1185, 1933, 1648, 1148, 1714, 1873, 1307 }, + { 406, 414, 1030, 1023, 1910, 1404, 1313, 1647, 1509, 793 }, + { 342, 640, 575, 1088, 1241, 1349, 1161, 1350, 1756, 1502 }, + { 559, 766, 1185, 357, 1682, 1428, 1329, 1897, 1219, 802 }, + { 473, 909, 1164, 771, 719, 2508, 1427, 1432, 722, 782 }, + { 342, 892, 785, 1145, 1150, 794, 1296, 1550, 973, 1057 }, + { 208, 1036, 1326, 1343, 1606, 3395, 815, 1455, 1618, 712 }, + { 228, 928, 890, 1046, 3499, 1711, 994, 829, 1720, 1318 }, + { 768, 724, 1058, 636, 991, 1075, 1319, 1324, 616, 825 }, + { 305, 1167, 1358, 899, 1587, 1587, 987, 1988, 1332, 501 } } +}; + +//------------------------------------------------------------------------------ +// helper functions for residuals struct VP8Residual. + +void VP8InitResidual(int first, int coeff_type, + VP8Encoder* const enc, VP8Residual* const res) { + res->coeff_type = coeff_type; + res->prob = enc->proba_.coeffs_[coeff_type]; + res->stats = enc->proba_.stats_[coeff_type]; + res->costs = enc->proba_.remapped_costs_[coeff_type]; + res->first = first; +} + +//------------------------------------------------------------------------------ +// Mode costs + +int VP8GetCostLuma4(VP8EncIterator* const it, const int16_t levels[16]) { + const int x = (it->i4_ & 3), y = (it->i4_ >> 2); + VP8Residual res; + VP8Encoder* const enc = it->enc_; + int R = 0; + int ctx; + + VP8InitResidual(0, 3, enc, &res); + ctx = it->top_nz_[x] + it->left_nz_[y]; + VP8SetResidualCoeffs(levels, &res); + R += VP8GetResidualCost(ctx, &res); + return R; +} + +int VP8GetCostLuma16(VP8EncIterator* const it, const VP8ModeScore* const rd) { + VP8Residual res; + VP8Encoder* const enc = it->enc_; + int x, y; + int R = 0; + + VP8IteratorNzToBytes(it); // re-import the non-zero context + + // DC + VP8InitResidual(0, 1, enc, &res); + VP8SetResidualCoeffs(rd->y_dc_levels, &res); + R += VP8GetResidualCost(it->top_nz_[8] + it->left_nz_[8], &res); + + // AC + VP8InitResidual(1, 0, enc, &res); + for (y = 0; y < 4; ++y) { + for (x = 0; x < 4; ++x) { + const int ctx = it->top_nz_[x] + it->left_nz_[y]; + VP8SetResidualCoeffs(rd->y_ac_levels[x + y * 4], &res); + R += VP8GetResidualCost(ctx, &res); + it->top_nz_[x] = it->left_nz_[y] = (res.last >= 0); + } + } + return R; +} + +int VP8GetCostUV(VP8EncIterator* const it, const VP8ModeScore* const rd) { + VP8Residual res; + VP8Encoder* const enc = it->enc_; + int ch, x, y; + int R = 0; + + VP8IteratorNzToBytes(it); // re-import the non-zero context + + VP8InitResidual(0, 2, enc, &res); + for (ch = 0; ch <= 2; ch += 2) { + for (y = 0; y < 2; ++y) { + for (x = 0; x < 2; ++x) { + const int ctx = it->top_nz_[4 + ch + x] + it->left_nz_[4 + ch + y]; + VP8SetResidualCoeffs(rd->uv_levels[ch * 2 + x + y * 2], &res); + R += VP8GetResidualCost(ctx, &res); + it->top_nz_[4 + ch + x] = it->left_nz_[4 + ch + y] = (res.last >= 0); + } + } + } + return R; +} + + +//------------------------------------------------------------------------------ +// Recording of token probabilities. + +// We keep the table-free variant around for reference, in case. +#define USE_LEVEL_CODE_TABLE + +// Simulate block coding, but only record statistics. +// Note: no need to record the fixed probas. +int VP8RecordCoeffs(int ctx, const VP8Residual* const res) { + int n = res->first; + // should be stats[VP8EncBands[n]], but it's equivalent for n=0 or 1 + proba_t* s = res->stats[n][ctx]; + if (res->last < 0) { + VP8RecordStats(0, s + 0); + return 0; + } + while (n <= res->last) { + int v; + VP8RecordStats(1, s + 0); // order of record doesn't matter + while ((v = res->coeffs[n++]) == 0) { + VP8RecordStats(0, s + 1); + s = res->stats[VP8EncBands[n]][0]; + } + VP8RecordStats(1, s + 1); + if (!VP8RecordStats(2u < (unsigned int)(v + 1), s + 2)) { // v = -1 or 1 + s = res->stats[VP8EncBands[n]][1]; + } else { + v = abs(v); +#if !defined(USE_LEVEL_CODE_TABLE) + if (!VP8RecordStats(v > 4, s + 3)) { + if (VP8RecordStats(v != 2, s + 4)) + VP8RecordStats(v == 4, s + 5); + } else if (!VP8RecordStats(v > 10, s + 6)) { + VP8RecordStats(v > 6, s + 7); + } else if (!VP8RecordStats((v >= 3 + (8 << 2)), s + 8)) { + VP8RecordStats((v >= 3 + (8 << 1)), s + 9); + } else { + VP8RecordStats((v >= 3 + (8 << 3)), s + 10); + } +#else + if (v > MAX_VARIABLE_LEVEL) { + v = MAX_VARIABLE_LEVEL; + } + + { + const int bits = VP8LevelCodes[v - 1][1]; + int pattern = VP8LevelCodes[v - 1][0]; + int i; + for (i = 0; (pattern >>= 1) != 0; ++i) { + const int mask = 2 << i; + if (pattern & 1) VP8RecordStats(!!(bits & mask), s + 3 + i); + } + } +#endif + s = res->stats[VP8EncBands[n]][2]; + } + } + if (n < 16) VP8RecordStats(0, s + 0); + return 1; +} + +//------------------------------------------------------------------------------ diff --git a/thirdparty/libwebp/enc/cost_enc.h b/thirdparty/libwebp/enc/cost_enc.h new file mode 100644 index 0000000000..99e4b37aa3 --- /dev/null +++ b/thirdparty/libwebp/enc/cost_enc.h @@ -0,0 +1,82 @@ +// Copyright 2011 Google Inc. All Rights Reserved. +// +// Use of this source code is governed by a BSD-style license +// that can be found in the COPYING file in the root of the source +// tree. An additional intellectual property rights grant can be found +// in the file PATENTS. All contributing project authors may +// be found in the AUTHORS file in the root of the source tree. +// ----------------------------------------------------------------------------- +// +// Cost tables for level and modes. +// +// Author: Skal (pascal.massimino@gmail.com) + +#ifndef WEBP_ENC_COST_H_ +#define WEBP_ENC_COST_H_ + +#include +#include +#include "./vp8i_enc.h" + +#ifdef __cplusplus +extern "C" { +#endif + +// On-the-fly info about the current set of residuals. Handy to avoid +// passing zillions of params. +typedef struct VP8Residual VP8Residual; +struct VP8Residual { + int first; + int last; + const int16_t* coeffs; + + int coeff_type; + ProbaArray* prob; + StatsArray* stats; + CostArrayPtr costs; +}; + +void VP8InitResidual(int first, int coeff_type, + VP8Encoder* const enc, VP8Residual* const res); + +int VP8RecordCoeffs(int ctx, const VP8Residual* const res); + +// Record proba context used. +static WEBP_INLINE int VP8RecordStats(int bit, proba_t* const stats) { + proba_t p = *stats; + // An overflow is inbound. Note we handle this at 0xfffe0000u instead of + // 0xffff0000u to make sure p + 1u does not overflow. + if (p >= 0xfffe0000u) { + p = ((p + 1u) >> 1) & 0x7fff7fffu; // -> divide the stats by 2. + } + // record bit count (lower 16 bits) and increment total count (upper 16 bits). + p += 0x00010000u + bit; + *stats = p; + return bit; +} + +// Cost of coding one event with probability 'proba'. +static WEBP_INLINE int VP8BitCost(int bit, uint8_t proba) { + return !bit ? VP8EntropyCost[proba] : VP8EntropyCost[255 - proba]; +} + +// Level cost calculations +extern const uint16_t VP8LevelCodes[MAX_VARIABLE_LEVEL][2]; +void VP8CalculateLevelCosts(VP8EncProba* const proba); +static WEBP_INLINE int VP8LevelCost(const uint16_t* const table, int level) { + return VP8LevelFixedCosts[level] + + table[(level > MAX_VARIABLE_LEVEL) ? MAX_VARIABLE_LEVEL : level]; +} + +// Mode costs +extern const uint16_t VP8FixedCostsUV[4]; +extern const uint16_t VP8FixedCostsI16[4]; +extern const uint16_t VP8FixedCostsI4[NUM_BMODES][NUM_BMODES][NUM_BMODES]; + +//------------------------------------------------------------------------------ + +#ifdef __cplusplus +} // extern "C" +#endif + +#endif /* WEBP_ENC_COST_H_ */ diff --git a/thirdparty/libwebp/enc/delta_palettization.c b/thirdparty/libwebp/enc/delta_palettization.c deleted file mode 100644 index 062e588d79..0000000000 --- a/thirdparty/libwebp/enc/delta_palettization.c +++ /dev/null @@ -1,455 +0,0 @@ -// Copyright 2015 Google Inc. All Rights Reserved. -// -// Use of this source code is governed by a BSD-style license -// that can be found in the COPYING file in the root of the source -// tree. An additional intellectual property rights grant can be found -// in the file PATENTS. All contributing project authors may -// be found in the AUTHORS file in the root of the source tree. -// ----------------------------------------------------------------------------- -// -// Author: Mislav Bradac (mislavm@google.com) -// - -#include "./delta_palettization.h" - -#ifdef WEBP_EXPERIMENTAL_FEATURES -#include "../webp/types.h" -#include "../dsp/lossless.h" - -#define MK_COL(r, g, b) (((r) << 16) + ((g) << 8) + (b)) - -// Format allows palette up to 256 entries, but more palette entries produce -// bigger entropy. In the future it will probably be useful to add more entries -// that are far from the origin of the palette or choose remaining entries -// dynamically. -#define DELTA_PALETTE_SIZE 226 - -// Palette used for delta_palettization. Entries are roughly sorted by distance -// of their signed equivalents from the origin. -static const uint32_t kDeltaPalette[DELTA_PALETTE_SIZE] = { - MK_COL(0u, 0u, 0u), - MK_COL(255u, 255u, 255u), - MK_COL(1u, 1u, 1u), - MK_COL(254u, 254u, 254u), - MK_COL(2u, 2u, 2u), - MK_COL(4u, 4u, 4u), - MK_COL(252u, 252u, 252u), - MK_COL(250u, 0u, 0u), - MK_COL(0u, 250u, 0u), - MK_COL(0u, 0u, 250u), - MK_COL(6u, 0u, 0u), - MK_COL(0u, 6u, 0u), - MK_COL(0u, 0u, 6u), - MK_COL(0u, 0u, 248u), - MK_COL(0u, 0u, 8u), - MK_COL(0u, 248u, 0u), - MK_COL(0u, 248u, 248u), - MK_COL(0u, 248u, 8u), - MK_COL(0u, 8u, 0u), - MK_COL(0u, 8u, 248u), - MK_COL(0u, 8u, 8u), - MK_COL(8u, 8u, 8u), - MK_COL(248u, 0u, 0u), - MK_COL(248u, 0u, 248u), - MK_COL(248u, 0u, 8u), - MK_COL(248u, 248u, 0u), - MK_COL(248u, 8u, 0u), - MK_COL(8u, 0u, 0u), - MK_COL(8u, 0u, 248u), - MK_COL(8u, 0u, 8u), - MK_COL(8u, 248u, 0u), - MK_COL(8u, 8u, 0u), - MK_COL(23u, 23u, 23u), - MK_COL(13u, 13u, 13u), - MK_COL(232u, 232u, 232u), - MK_COL(244u, 244u, 244u), - MK_COL(245u, 245u, 250u), - MK_COL(50u, 50u, 50u), - MK_COL(204u, 204u, 204u), - MK_COL(236u, 236u, 236u), - MK_COL(16u, 16u, 16u), - MK_COL(240u, 16u, 16u), - MK_COL(16u, 240u, 16u), - MK_COL(240u, 240u, 16u), - MK_COL(16u, 16u, 240u), - MK_COL(240u, 16u, 240u), - MK_COL(16u, 240u, 240u), - MK_COL(240u, 240u, 240u), - MK_COL(0u, 0u, 232u), - MK_COL(0u, 232u, 0u), - MK_COL(232u, 0u, 0u), - MK_COL(0u, 0u, 24u), - MK_COL(0u, 24u, 0u), - MK_COL(24u, 0u, 0u), - MK_COL(32u, 32u, 32u), - MK_COL(224u, 32u, 32u), - MK_COL(32u, 224u, 32u), - MK_COL(224u, 224u, 32u), - MK_COL(32u, 32u, 224u), - MK_COL(224u, 32u, 224u), - MK_COL(32u, 224u, 224u), - MK_COL(224u, 224u, 224u), - MK_COL(0u, 0u, 176u), - MK_COL(0u, 0u, 80u), - MK_COL(0u, 176u, 0u), - MK_COL(0u, 176u, 176u), - MK_COL(0u, 176u, 80u), - MK_COL(0u, 80u, 0u), - MK_COL(0u, 80u, 176u), - MK_COL(0u, 80u, 80u), - MK_COL(176u, 0u, 0u), - MK_COL(176u, 0u, 176u), - MK_COL(176u, 0u, 80u), - MK_COL(176u, 176u, 0u), - MK_COL(176u, 80u, 0u), - MK_COL(80u, 0u, 0u), - MK_COL(80u, 0u, 176u), - MK_COL(80u, 0u, 80u), - MK_COL(80u, 176u, 0u), - MK_COL(80u, 80u, 0u), - MK_COL(0u, 0u, 152u), - MK_COL(0u, 0u, 104u), - MK_COL(0u, 152u, 0u), - MK_COL(0u, 152u, 152u), - MK_COL(0u, 152u, 104u), - MK_COL(0u, 104u, 0u), - MK_COL(0u, 104u, 152u), - MK_COL(0u, 104u, 104u), - MK_COL(152u, 0u, 0u), - MK_COL(152u, 0u, 152u), - MK_COL(152u, 0u, 104u), - MK_COL(152u, 152u, 0u), - MK_COL(152u, 104u, 0u), - MK_COL(104u, 0u, 0u), - MK_COL(104u, 0u, 152u), - MK_COL(104u, 0u, 104u), - MK_COL(104u, 152u, 0u), - MK_COL(104u, 104u, 0u), - MK_COL(216u, 216u, 216u), - MK_COL(216u, 216u, 40u), - MK_COL(216u, 216u, 176u), - MK_COL(216u, 216u, 80u), - MK_COL(216u, 40u, 216u), - MK_COL(216u, 40u, 40u), - MK_COL(216u, 40u, 176u), - MK_COL(216u, 40u, 80u), - MK_COL(216u, 176u, 216u), - MK_COL(216u, 176u, 40u), - MK_COL(216u, 176u, 176u), - MK_COL(216u, 176u, 80u), - MK_COL(216u, 80u, 216u), - MK_COL(216u, 80u, 40u), - MK_COL(216u, 80u, 176u), - MK_COL(216u, 80u, 80u), - MK_COL(40u, 216u, 216u), - MK_COL(40u, 216u, 40u), - MK_COL(40u, 216u, 176u), - MK_COL(40u, 216u, 80u), - MK_COL(40u, 40u, 216u), - MK_COL(40u, 40u, 40u), - MK_COL(40u, 40u, 176u), - MK_COL(40u, 40u, 80u), - MK_COL(40u, 176u, 216u), - MK_COL(40u, 176u, 40u), - MK_COL(40u, 176u, 176u), - MK_COL(40u, 176u, 80u), - MK_COL(40u, 80u, 216u), - MK_COL(40u, 80u, 40u), - MK_COL(40u, 80u, 176u), - MK_COL(40u, 80u, 80u), - MK_COL(80u, 216u, 216u), - MK_COL(80u, 216u, 40u), - MK_COL(80u, 216u, 176u), - MK_COL(80u, 216u, 80u), - MK_COL(80u, 40u, 216u), - MK_COL(80u, 40u, 40u), - MK_COL(80u, 40u, 176u), - MK_COL(80u, 40u, 80u), - MK_COL(80u, 176u, 216u), - MK_COL(80u, 176u, 40u), - MK_COL(80u, 176u, 176u), - MK_COL(80u, 176u, 80u), - MK_COL(80u, 80u, 216u), - MK_COL(80u, 80u, 40u), - MK_COL(80u, 80u, 176u), - MK_COL(80u, 80u, 80u), - MK_COL(0u, 0u, 192u), - MK_COL(0u, 0u, 64u), - MK_COL(0u, 0u, 128u), - MK_COL(0u, 192u, 0u), - MK_COL(0u, 192u, 192u), - MK_COL(0u, 192u, 64u), - MK_COL(0u, 192u, 128u), - MK_COL(0u, 64u, 0u), - MK_COL(0u, 64u, 192u), - MK_COL(0u, 64u, 64u), - MK_COL(0u, 64u, 128u), - MK_COL(0u, 128u, 0u), - MK_COL(0u, 128u, 192u), - MK_COL(0u, 128u, 64u), - MK_COL(0u, 128u, 128u), - MK_COL(176u, 216u, 216u), - MK_COL(176u, 216u, 40u), - MK_COL(176u, 216u, 176u), - MK_COL(176u, 216u, 80u), - MK_COL(176u, 40u, 216u), - MK_COL(176u, 40u, 40u), - MK_COL(176u, 40u, 176u), - MK_COL(176u, 40u, 80u), - MK_COL(176u, 176u, 216u), - MK_COL(176u, 176u, 40u), - MK_COL(176u, 176u, 176u), - MK_COL(176u, 176u, 80u), - MK_COL(176u, 80u, 216u), - MK_COL(176u, 80u, 40u), - MK_COL(176u, 80u, 176u), - MK_COL(176u, 80u, 80u), - MK_COL(192u, 0u, 0u), - MK_COL(192u, 0u, 192u), - MK_COL(192u, 0u, 64u), - MK_COL(192u, 0u, 128u), - MK_COL(192u, 192u, 0u), - MK_COL(192u, 192u, 192u), - MK_COL(192u, 192u, 64u), - MK_COL(192u, 192u, 128u), - MK_COL(192u, 64u, 0u), - MK_COL(192u, 64u, 192u), - MK_COL(192u, 64u, 64u), - MK_COL(192u, 64u, 128u), - MK_COL(192u, 128u, 0u), - MK_COL(192u, 128u, 192u), - MK_COL(192u, 128u, 64u), - MK_COL(192u, 128u, 128u), - MK_COL(64u, 0u, 0u), - MK_COL(64u, 0u, 192u), - MK_COL(64u, 0u, 64u), - MK_COL(64u, 0u, 128u), - MK_COL(64u, 192u, 0u), - MK_COL(64u, 192u, 192u), - MK_COL(64u, 192u, 64u), - MK_COL(64u, 192u, 128u), - MK_COL(64u, 64u, 0u), - MK_COL(64u, 64u, 192u), - MK_COL(64u, 64u, 64u), - MK_COL(64u, 64u, 128u), - MK_COL(64u, 128u, 0u), - MK_COL(64u, 128u, 192u), - MK_COL(64u, 128u, 64u), - MK_COL(64u, 128u, 128u), - MK_COL(128u, 0u, 0u), - MK_COL(128u, 0u, 192u), - MK_COL(128u, 0u, 64u), - MK_COL(128u, 0u, 128u), - MK_COL(128u, 192u, 0u), - MK_COL(128u, 192u, 192u), - MK_COL(128u, 192u, 64u), - MK_COL(128u, 192u, 128u), - MK_COL(128u, 64u, 0u), - MK_COL(128u, 64u, 192u), - MK_COL(128u, 64u, 64u), - MK_COL(128u, 64u, 128u), - MK_COL(128u, 128u, 0u), - MK_COL(128u, 128u, 192u), - MK_COL(128u, 128u, 64u), - MK_COL(128u, 128u, 128u), -}; - -#undef MK_COL - -//------------------------------------------------------------------------------ -// TODO(skal): move the functions to dsp/lossless.c when the correct -// granularity is found. For now, we'll just copy-paste some useful bits -// here instead. - -// In-place sum of each component with mod 256. -static WEBP_INLINE void AddPixelsEq(uint32_t* a, uint32_t b) { - const uint32_t alpha_and_green = (*a & 0xff00ff00u) + (b & 0xff00ff00u); - const uint32_t red_and_blue = (*a & 0x00ff00ffu) + (b & 0x00ff00ffu); - *a = (alpha_and_green & 0xff00ff00u) | (red_and_blue & 0x00ff00ffu); -} - -static WEBP_INLINE uint32_t Clip255(uint32_t a) { - if (a < 256) { - return a; - } - // return 0, when a is a negative integer. - // return 255, when a is positive. - return ~a >> 24; -} - -// Delta palettization functions. -static WEBP_INLINE int Square(int x) { - return x * x; -} - -static WEBP_INLINE uint32_t Intensity(uint32_t a) { - return - 30 * ((a >> 16) & 0xff) + - 59 * ((a >> 8) & 0xff) + - 11 * ((a >> 0) & 0xff); -} - -static uint32_t CalcDist(uint32_t predicted_value, uint32_t actual_value, - uint32_t palette_entry) { - int i; - uint32_t distance = 0; - AddPixelsEq(&predicted_value, palette_entry); - for (i = 0; i < 32; i += 8) { - const int32_t av = (actual_value >> i) & 0xff; - const int32_t pv = (predicted_value >> i) & 0xff; - distance += Square(pv - av); - } - // We sum square of intensity difference with factor 10, but because Intensity - // returns 100 times real intensity we need to multiply differences of colors - // by 1000. - distance *= 1000u; - distance += Square(Intensity(predicted_value) - - Intensity(actual_value)); - return distance; -} - -static uint32_t Predict(int x, int y, uint32_t* image) { - const uint32_t t = (y == 0) ? ARGB_BLACK : image[x]; - const uint32_t l = (x == 0) ? ARGB_BLACK : image[x - 1]; - const uint32_t p = - (((((t >> 24) & 0xff) + ((l >> 24) & 0xff)) / 2) << 24) + - (((((t >> 16) & 0xff) + ((l >> 16) & 0xff)) / 2) << 16) + - (((((t >> 8) & 0xff) + ((l >> 8) & 0xff)) / 2) << 8) + - (((((t >> 0) & 0xff) + ((l >> 0) & 0xff)) / 2) << 0); - if (x == 0 && y == 0) return ARGB_BLACK; - if (x == 0) return t; - if (y == 0) return l; - return p; -} - -static WEBP_INLINE int AddSubtractComponentFullWithCoefficient( - int a, int b, int c) { - return Clip255(a + ((b - c) >> 2)); -} - -static WEBP_INLINE uint32_t ClampedAddSubtractFullWithCoefficient( - uint32_t c0, uint32_t c1, uint32_t c2) { - const int a = AddSubtractComponentFullWithCoefficient( - c0 >> 24, c1 >> 24, c2 >> 24); - const int r = AddSubtractComponentFullWithCoefficient((c0 >> 16) & 0xff, - (c1 >> 16) & 0xff, - (c2 >> 16) & 0xff); - const int g = AddSubtractComponentFullWithCoefficient((c0 >> 8) & 0xff, - (c1 >> 8) & 0xff, - (c2 >> 8) & 0xff); - const int b = AddSubtractComponentFullWithCoefficient( - c0 & 0xff, c1 & 0xff, c2 & 0xff); - return ((uint32_t)a << 24) | (r << 16) | (g << 8) | b; -} - -//------------------------------------------------------------------------------ - -// Find palette entry with minimum error from difference of actual pixel value -// and predicted pixel value. Propagate error of pixel to its top and left pixel -// in src array. Write predicted_value + palette_entry to new_image. Return -// index of best palette entry. -static int FindBestPaletteEntry(uint32_t src, uint32_t predicted_value, - const uint32_t palette[], int palette_size) { - int i; - int idx = 0; - uint32_t best_distance = CalcDist(predicted_value, src, palette[0]); - for (i = 1; i < palette_size; ++i) { - const uint32_t distance = CalcDist(predicted_value, src, palette[i]); - if (distance < best_distance) { - best_distance = distance; - idx = i; - } - } - return idx; -} - -static void ApplyBestPaletteEntry(int x, int y, - uint32_t new_value, uint32_t palette_value, - uint32_t* src, int src_stride, - uint32_t* new_image) { - AddPixelsEq(&new_value, palette_value); - if (x > 0) { - src[x - 1] = ClampedAddSubtractFullWithCoefficient(src[x - 1], - new_value, src[x]); - } - if (y > 0) { - src[x - src_stride] = - ClampedAddSubtractFullWithCoefficient(src[x - src_stride], - new_value, src[x]); - } - new_image[x] = new_value; -} - -//------------------------------------------------------------------------------ -// Main entry point - -static WebPEncodingError ApplyDeltaPalette(uint32_t* src, uint32_t* dst, - uint32_t src_stride, - uint32_t dst_stride, - const uint32_t* palette, - int palette_size, - int width, int height, - int num_passes) { - int x, y; - WebPEncodingError err = VP8_ENC_OK; - uint32_t* new_image = (uint32_t*)WebPSafeMalloc(width, sizeof(*new_image)); - uint8_t* const tmp_row = (uint8_t*)WebPSafeMalloc(width, sizeof(*tmp_row)); - if (new_image == NULL || tmp_row == NULL) { - err = VP8_ENC_ERROR_OUT_OF_MEMORY; - goto Error; - } - - while (num_passes--) { - uint32_t* cur_src = src; - uint32_t* cur_dst = dst; - for (y = 0; y < height; ++y) { - for (x = 0; x < width; ++x) { - const uint32_t predicted_value = Predict(x, y, new_image); - tmp_row[x] = FindBestPaletteEntry(cur_src[x], predicted_value, - palette, palette_size); - ApplyBestPaletteEntry(x, y, predicted_value, palette[tmp_row[x]], - cur_src, src_stride, new_image); - } - for (x = 0; x < width; ++x) { - cur_dst[x] = palette[tmp_row[x]]; - } - cur_src += src_stride; - cur_dst += dst_stride; - } - } - Error: - WebPSafeFree(new_image); - WebPSafeFree(tmp_row); - return err; -} - -// replaces enc->argb_ by a palettizable approximation of it, -// and generates optimal enc->palette_[] -WebPEncodingError WebPSearchOptimalDeltaPalette(VP8LEncoder* const enc) { - const WebPPicture* const pic = enc->pic_; - uint32_t* src = pic->argb; - uint32_t* dst = enc->argb_; - const int width = pic->width; - const int height = pic->height; - - WebPEncodingError err = VP8_ENC_OK; - memcpy(enc->palette_, kDeltaPalette, sizeof(kDeltaPalette)); - enc->palette_[DELTA_PALETTE_SIZE - 1] = src[0] - 0xff000000u; - enc->palette_size_ = DELTA_PALETTE_SIZE; - err = ApplyDeltaPalette(src, dst, pic->argb_stride, enc->current_width_, - enc->palette_, enc->palette_size_, - width, height, 2); - if (err != VP8_ENC_OK) goto Error; - - Error: - return err; -} - -#else // !WEBP_EXPERIMENTAL_FEATURES - -WebPEncodingError WebPSearchOptimalDeltaPalette(VP8LEncoder* const enc) { - (void)enc; - return VP8_ENC_ERROR_INVALID_CONFIGURATION; -} - -#endif // WEBP_EXPERIMENTAL_FEATURES diff --git a/thirdparty/libwebp/enc/delta_palettization.h b/thirdparty/libwebp/enc/delta_palettization.h deleted file mode 100644 index e41c0c5ab5..0000000000 --- a/thirdparty/libwebp/enc/delta_palettization.h +++ /dev/null @@ -1,25 +0,0 @@ -// Copyright 2015 Google Inc. All Rights Reserved. -// -// Use of this source code is governed by a BSD-style license -// that can be found in the COPYING file in the root of the source -// tree. An additional intellectual property rights grant can be found -// in the file PATENTS. All contributing project authors may -// be found in the AUTHORS file in the root of the source tree. -// ----------------------------------------------------------------------------- -// -// Author: Mislav Bradac (mislavm@google.com) -// - -#ifndef WEBP_ENC_DELTA_PALETTIZATION_H_ -#define WEBP_ENC_DELTA_PALETTIZATION_H_ - -#include "../webp/encode.h" -#include "../enc/vp8li.h" - -// Replaces enc->argb_[] input by a palettizable approximation of it, -// and generates optimal enc->palette_[]. -// This function can revert enc->use_palette_ / enc->use_predict_ flag -// if delta-palettization is not producing expected saving. -WebPEncodingError WebPSearchOptimalDeltaPalette(VP8LEncoder* const enc); - -#endif // WEBP_ENC_DELTA_PALETTIZATION_H_ diff --git a/thirdparty/libwebp/enc/delta_palettization_enc.c b/thirdparty/libwebp/enc/delta_palettization_enc.c new file mode 100644 index 0000000000..eaf0f050ea --- /dev/null +++ b/thirdparty/libwebp/enc/delta_palettization_enc.c @@ -0,0 +1,455 @@ +// Copyright 2015 Google Inc. All Rights Reserved. +// +// Use of this source code is governed by a BSD-style license +// that can be found in the COPYING file in the root of the source +// tree. An additional intellectual property rights grant can be found +// in the file PATENTS. All contributing project authors may +// be found in the AUTHORS file in the root of the source tree. +// ----------------------------------------------------------------------------- +// +// Author: Mislav Bradac (mislavm@google.com) +// + +#include "./delta_palettization_enc.h" + +#ifdef WEBP_EXPERIMENTAL_FEATURES +#include "../webp/types.h" +#include "../dsp/lossless.h" + +#define MK_COL(r, g, b) (((r) << 16) + ((g) << 8) + (b)) + +// Format allows palette up to 256 entries, but more palette entries produce +// bigger entropy. In the future it will probably be useful to add more entries +// that are far from the origin of the palette or choose remaining entries +// dynamically. +#define DELTA_PALETTE_SIZE 226 + +// Palette used for delta_palettization. Entries are roughly sorted by distance +// of their signed equivalents from the origin. +static const uint32_t kDeltaPalette[DELTA_PALETTE_SIZE] = { + MK_COL(0u, 0u, 0u), + MK_COL(255u, 255u, 255u), + MK_COL(1u, 1u, 1u), + MK_COL(254u, 254u, 254u), + MK_COL(2u, 2u, 2u), + MK_COL(4u, 4u, 4u), + MK_COL(252u, 252u, 252u), + MK_COL(250u, 0u, 0u), + MK_COL(0u, 250u, 0u), + MK_COL(0u, 0u, 250u), + MK_COL(6u, 0u, 0u), + MK_COL(0u, 6u, 0u), + MK_COL(0u, 0u, 6u), + MK_COL(0u, 0u, 248u), + MK_COL(0u, 0u, 8u), + MK_COL(0u, 248u, 0u), + MK_COL(0u, 248u, 248u), + MK_COL(0u, 248u, 8u), + MK_COL(0u, 8u, 0u), + MK_COL(0u, 8u, 248u), + MK_COL(0u, 8u, 8u), + MK_COL(8u, 8u, 8u), + MK_COL(248u, 0u, 0u), + MK_COL(248u, 0u, 248u), + MK_COL(248u, 0u, 8u), + MK_COL(248u, 248u, 0u), + MK_COL(248u, 8u, 0u), + MK_COL(8u, 0u, 0u), + MK_COL(8u, 0u, 248u), + MK_COL(8u, 0u, 8u), + MK_COL(8u, 248u, 0u), + MK_COL(8u, 8u, 0u), + MK_COL(23u, 23u, 23u), + MK_COL(13u, 13u, 13u), + MK_COL(232u, 232u, 232u), + MK_COL(244u, 244u, 244u), + MK_COL(245u, 245u, 250u), + MK_COL(50u, 50u, 50u), + MK_COL(204u, 204u, 204u), + MK_COL(236u, 236u, 236u), + MK_COL(16u, 16u, 16u), + MK_COL(240u, 16u, 16u), + MK_COL(16u, 240u, 16u), + MK_COL(240u, 240u, 16u), + MK_COL(16u, 16u, 240u), + MK_COL(240u, 16u, 240u), + MK_COL(16u, 240u, 240u), + MK_COL(240u, 240u, 240u), + MK_COL(0u, 0u, 232u), + MK_COL(0u, 232u, 0u), + MK_COL(232u, 0u, 0u), + MK_COL(0u, 0u, 24u), + MK_COL(0u, 24u, 0u), + MK_COL(24u, 0u, 0u), + MK_COL(32u, 32u, 32u), + MK_COL(224u, 32u, 32u), + MK_COL(32u, 224u, 32u), + MK_COL(224u, 224u, 32u), + MK_COL(32u, 32u, 224u), + MK_COL(224u, 32u, 224u), + MK_COL(32u, 224u, 224u), + MK_COL(224u, 224u, 224u), + MK_COL(0u, 0u, 176u), + MK_COL(0u, 0u, 80u), + MK_COL(0u, 176u, 0u), + MK_COL(0u, 176u, 176u), + MK_COL(0u, 176u, 80u), + MK_COL(0u, 80u, 0u), + MK_COL(0u, 80u, 176u), + MK_COL(0u, 80u, 80u), + MK_COL(176u, 0u, 0u), + MK_COL(176u, 0u, 176u), + MK_COL(176u, 0u, 80u), + MK_COL(176u, 176u, 0u), + MK_COL(176u, 80u, 0u), + MK_COL(80u, 0u, 0u), + MK_COL(80u, 0u, 176u), + MK_COL(80u, 0u, 80u), + MK_COL(80u, 176u, 0u), + MK_COL(80u, 80u, 0u), + MK_COL(0u, 0u, 152u), + MK_COL(0u, 0u, 104u), + MK_COL(0u, 152u, 0u), + MK_COL(0u, 152u, 152u), + MK_COL(0u, 152u, 104u), + MK_COL(0u, 104u, 0u), + MK_COL(0u, 104u, 152u), + MK_COL(0u, 104u, 104u), + MK_COL(152u, 0u, 0u), + MK_COL(152u, 0u, 152u), + MK_COL(152u, 0u, 104u), + MK_COL(152u, 152u, 0u), + MK_COL(152u, 104u, 0u), + MK_COL(104u, 0u, 0u), + MK_COL(104u, 0u, 152u), + MK_COL(104u, 0u, 104u), + MK_COL(104u, 152u, 0u), + MK_COL(104u, 104u, 0u), + MK_COL(216u, 216u, 216u), + MK_COL(216u, 216u, 40u), + MK_COL(216u, 216u, 176u), + MK_COL(216u, 216u, 80u), + MK_COL(216u, 40u, 216u), + MK_COL(216u, 40u, 40u), + MK_COL(216u, 40u, 176u), + MK_COL(216u, 40u, 80u), + MK_COL(216u, 176u, 216u), + MK_COL(216u, 176u, 40u), + MK_COL(216u, 176u, 176u), + MK_COL(216u, 176u, 80u), + MK_COL(216u, 80u, 216u), + MK_COL(216u, 80u, 40u), + MK_COL(216u, 80u, 176u), + MK_COL(216u, 80u, 80u), + MK_COL(40u, 216u, 216u), + MK_COL(40u, 216u, 40u), + MK_COL(40u, 216u, 176u), + MK_COL(40u, 216u, 80u), + MK_COL(40u, 40u, 216u), + MK_COL(40u, 40u, 40u), + MK_COL(40u, 40u, 176u), + MK_COL(40u, 40u, 80u), + MK_COL(40u, 176u, 216u), + MK_COL(40u, 176u, 40u), + MK_COL(40u, 176u, 176u), + MK_COL(40u, 176u, 80u), + MK_COL(40u, 80u, 216u), + MK_COL(40u, 80u, 40u), + MK_COL(40u, 80u, 176u), + MK_COL(40u, 80u, 80u), + MK_COL(80u, 216u, 216u), + MK_COL(80u, 216u, 40u), + MK_COL(80u, 216u, 176u), + MK_COL(80u, 216u, 80u), + MK_COL(80u, 40u, 216u), + MK_COL(80u, 40u, 40u), + MK_COL(80u, 40u, 176u), + MK_COL(80u, 40u, 80u), + MK_COL(80u, 176u, 216u), + MK_COL(80u, 176u, 40u), + MK_COL(80u, 176u, 176u), + MK_COL(80u, 176u, 80u), + MK_COL(80u, 80u, 216u), + MK_COL(80u, 80u, 40u), + MK_COL(80u, 80u, 176u), + MK_COL(80u, 80u, 80u), + MK_COL(0u, 0u, 192u), + MK_COL(0u, 0u, 64u), + MK_COL(0u, 0u, 128u), + MK_COL(0u, 192u, 0u), + MK_COL(0u, 192u, 192u), + MK_COL(0u, 192u, 64u), + MK_COL(0u, 192u, 128u), + MK_COL(0u, 64u, 0u), + MK_COL(0u, 64u, 192u), + MK_COL(0u, 64u, 64u), + MK_COL(0u, 64u, 128u), + MK_COL(0u, 128u, 0u), + MK_COL(0u, 128u, 192u), + MK_COL(0u, 128u, 64u), + MK_COL(0u, 128u, 128u), + MK_COL(176u, 216u, 216u), + MK_COL(176u, 216u, 40u), + MK_COL(176u, 216u, 176u), + MK_COL(176u, 216u, 80u), + MK_COL(176u, 40u, 216u), + MK_COL(176u, 40u, 40u), + MK_COL(176u, 40u, 176u), + MK_COL(176u, 40u, 80u), + MK_COL(176u, 176u, 216u), + MK_COL(176u, 176u, 40u), + MK_COL(176u, 176u, 176u), + MK_COL(176u, 176u, 80u), + MK_COL(176u, 80u, 216u), + MK_COL(176u, 80u, 40u), + MK_COL(176u, 80u, 176u), + MK_COL(176u, 80u, 80u), + MK_COL(192u, 0u, 0u), + MK_COL(192u, 0u, 192u), + MK_COL(192u, 0u, 64u), + MK_COL(192u, 0u, 128u), + MK_COL(192u, 192u, 0u), + MK_COL(192u, 192u, 192u), + MK_COL(192u, 192u, 64u), + MK_COL(192u, 192u, 128u), + MK_COL(192u, 64u, 0u), + MK_COL(192u, 64u, 192u), + MK_COL(192u, 64u, 64u), + MK_COL(192u, 64u, 128u), + MK_COL(192u, 128u, 0u), + MK_COL(192u, 128u, 192u), + MK_COL(192u, 128u, 64u), + MK_COL(192u, 128u, 128u), + MK_COL(64u, 0u, 0u), + MK_COL(64u, 0u, 192u), + MK_COL(64u, 0u, 64u), + MK_COL(64u, 0u, 128u), + MK_COL(64u, 192u, 0u), + MK_COL(64u, 192u, 192u), + MK_COL(64u, 192u, 64u), + MK_COL(64u, 192u, 128u), + MK_COL(64u, 64u, 0u), + MK_COL(64u, 64u, 192u), + MK_COL(64u, 64u, 64u), + MK_COL(64u, 64u, 128u), + MK_COL(64u, 128u, 0u), + MK_COL(64u, 128u, 192u), + MK_COL(64u, 128u, 64u), + MK_COL(64u, 128u, 128u), + MK_COL(128u, 0u, 0u), + MK_COL(128u, 0u, 192u), + MK_COL(128u, 0u, 64u), + MK_COL(128u, 0u, 128u), + MK_COL(128u, 192u, 0u), + MK_COL(128u, 192u, 192u), + MK_COL(128u, 192u, 64u), + MK_COL(128u, 192u, 128u), + MK_COL(128u, 64u, 0u), + MK_COL(128u, 64u, 192u), + MK_COL(128u, 64u, 64u), + MK_COL(128u, 64u, 128u), + MK_COL(128u, 128u, 0u), + MK_COL(128u, 128u, 192u), + MK_COL(128u, 128u, 64u), + MK_COL(128u, 128u, 128u), +}; + +#undef MK_COL + +//------------------------------------------------------------------------------ +// TODO(skal): move the functions to dsp/lossless.c when the correct +// granularity is found. For now, we'll just copy-paste some useful bits +// here instead. + +// In-place sum of each component with mod 256. +static WEBP_INLINE void AddPixelsEq(uint32_t* a, uint32_t b) { + const uint32_t alpha_and_green = (*a & 0xff00ff00u) + (b & 0xff00ff00u); + const uint32_t red_and_blue = (*a & 0x00ff00ffu) + (b & 0x00ff00ffu); + *a = (alpha_and_green & 0xff00ff00u) | (red_and_blue & 0x00ff00ffu); +} + +static WEBP_INLINE uint32_t Clip255(uint32_t a) { + if (a < 256) { + return a; + } + // return 0, when a is a negative integer. + // return 255, when a is positive. + return ~a >> 24; +} + +// Delta palettization functions. +static WEBP_INLINE int Square(int x) { + return x * x; +} + +static WEBP_INLINE uint32_t Intensity(uint32_t a) { + return + 30 * ((a >> 16) & 0xff) + + 59 * ((a >> 8) & 0xff) + + 11 * ((a >> 0) & 0xff); +} + +static uint32_t CalcDist(uint32_t predicted_value, uint32_t actual_value, + uint32_t palette_entry) { + int i; + uint32_t distance = 0; + AddPixelsEq(&predicted_value, palette_entry); + for (i = 0; i < 32; i += 8) { + const int32_t av = (actual_value >> i) & 0xff; + const int32_t pv = (predicted_value >> i) & 0xff; + distance += Square(pv - av); + } + // We sum square of intensity difference with factor 10, but because Intensity + // returns 100 times real intensity we need to multiply differences of colors + // by 1000. + distance *= 1000u; + distance += Square(Intensity(predicted_value) + - Intensity(actual_value)); + return distance; +} + +static uint32_t Predict(int x, int y, uint32_t* image) { + const uint32_t t = (y == 0) ? ARGB_BLACK : image[x]; + const uint32_t l = (x == 0) ? ARGB_BLACK : image[x - 1]; + const uint32_t p = + (((((t >> 24) & 0xff) + ((l >> 24) & 0xff)) / 2) << 24) + + (((((t >> 16) & 0xff) + ((l >> 16) & 0xff)) / 2) << 16) + + (((((t >> 8) & 0xff) + ((l >> 8) & 0xff)) / 2) << 8) + + (((((t >> 0) & 0xff) + ((l >> 0) & 0xff)) / 2) << 0); + if (x == 0 && y == 0) return ARGB_BLACK; + if (x == 0) return t; + if (y == 0) return l; + return p; +} + +static WEBP_INLINE int AddSubtractComponentFullWithCoefficient( + int a, int b, int c) { + return Clip255(a + ((b - c) >> 2)); +} + +static WEBP_INLINE uint32_t ClampedAddSubtractFullWithCoefficient( + uint32_t c0, uint32_t c1, uint32_t c2) { + const int a = AddSubtractComponentFullWithCoefficient( + c0 >> 24, c1 >> 24, c2 >> 24); + const int r = AddSubtractComponentFullWithCoefficient((c0 >> 16) & 0xff, + (c1 >> 16) & 0xff, + (c2 >> 16) & 0xff); + const int g = AddSubtractComponentFullWithCoefficient((c0 >> 8) & 0xff, + (c1 >> 8) & 0xff, + (c2 >> 8) & 0xff); + const int b = AddSubtractComponentFullWithCoefficient( + c0 & 0xff, c1 & 0xff, c2 & 0xff); + return ((uint32_t)a << 24) | (r << 16) | (g << 8) | b; +} + +//------------------------------------------------------------------------------ + +// Find palette entry with minimum error from difference of actual pixel value +// and predicted pixel value. Propagate error of pixel to its top and left pixel +// in src array. Write predicted_value + palette_entry to new_image. Return +// index of best palette entry. +static int FindBestPaletteEntry(uint32_t src, uint32_t predicted_value, + const uint32_t palette[], int palette_size) { + int i; + int idx = 0; + uint32_t best_distance = CalcDist(predicted_value, src, palette[0]); + for (i = 1; i < palette_size; ++i) { + const uint32_t distance = CalcDist(predicted_value, src, palette[i]); + if (distance < best_distance) { + best_distance = distance; + idx = i; + } + } + return idx; +} + +static void ApplyBestPaletteEntry(int x, int y, + uint32_t new_value, uint32_t palette_value, + uint32_t* src, int src_stride, + uint32_t* new_image) { + AddPixelsEq(&new_value, palette_value); + if (x > 0) { + src[x - 1] = ClampedAddSubtractFullWithCoefficient(src[x - 1], + new_value, src[x]); + } + if (y > 0) { + src[x - src_stride] = + ClampedAddSubtractFullWithCoefficient(src[x - src_stride], + new_value, src[x]); + } + new_image[x] = new_value; +} + +//------------------------------------------------------------------------------ +// Main entry point + +static WebPEncodingError ApplyDeltaPalette(uint32_t* src, uint32_t* dst, + uint32_t src_stride, + uint32_t dst_stride, + const uint32_t* palette, + int palette_size, + int width, int height, + int num_passes) { + int x, y; + WebPEncodingError err = VP8_ENC_OK; + uint32_t* new_image = (uint32_t*)WebPSafeMalloc(width, sizeof(*new_image)); + uint8_t* const tmp_row = (uint8_t*)WebPSafeMalloc(width, sizeof(*tmp_row)); + if (new_image == NULL || tmp_row == NULL) { + err = VP8_ENC_ERROR_OUT_OF_MEMORY; + goto Error; + } + + while (num_passes--) { + uint32_t* cur_src = src; + uint32_t* cur_dst = dst; + for (y = 0; y < height; ++y) { + for (x = 0; x < width; ++x) { + const uint32_t predicted_value = Predict(x, y, new_image); + tmp_row[x] = FindBestPaletteEntry(cur_src[x], predicted_value, + palette, palette_size); + ApplyBestPaletteEntry(x, y, predicted_value, palette[tmp_row[x]], + cur_src, src_stride, new_image); + } + for (x = 0; x < width; ++x) { + cur_dst[x] = palette[tmp_row[x]]; + } + cur_src += src_stride; + cur_dst += dst_stride; + } + } + Error: + WebPSafeFree(new_image); + WebPSafeFree(tmp_row); + return err; +} + +// replaces enc->argb_ by a palettizable approximation of it, +// and generates optimal enc->palette_[] +WebPEncodingError WebPSearchOptimalDeltaPalette(VP8LEncoder* const enc) { + const WebPPicture* const pic = enc->pic_; + uint32_t* src = pic->argb; + uint32_t* dst = enc->argb_; + const int width = pic->width; + const int height = pic->height; + + WebPEncodingError err = VP8_ENC_OK; + memcpy(enc->palette_, kDeltaPalette, sizeof(kDeltaPalette)); + enc->palette_[DELTA_PALETTE_SIZE - 1] = src[0] - 0xff000000u; + enc->palette_size_ = DELTA_PALETTE_SIZE; + err = ApplyDeltaPalette(src, dst, pic->argb_stride, enc->current_width_, + enc->palette_, enc->palette_size_, + width, height, 2); + if (err != VP8_ENC_OK) goto Error; + + Error: + return err; +} + +#else // !WEBP_EXPERIMENTAL_FEATURES + +WebPEncodingError WebPSearchOptimalDeltaPalette(VP8LEncoder* const enc) { + (void)enc; + return VP8_ENC_ERROR_INVALID_CONFIGURATION; +} + +#endif // WEBP_EXPERIMENTAL_FEATURES diff --git a/thirdparty/libwebp/enc/delta_palettization_enc.h b/thirdparty/libwebp/enc/delta_palettization_enc.h new file mode 100644 index 0000000000..63048ec6e8 --- /dev/null +++ b/thirdparty/libwebp/enc/delta_palettization_enc.h @@ -0,0 +1,25 @@ +// Copyright 2015 Google Inc. All Rights Reserved. +// +// Use of this source code is governed by a BSD-style license +// that can be found in the COPYING file in the root of the source +// tree. An additional intellectual property rights grant can be found +// in the file PATENTS. All contributing project authors may +// be found in the AUTHORS file in the root of the source tree. +// ----------------------------------------------------------------------------- +// +// Author: Mislav Bradac (mislavm@google.com) +// + +#ifndef WEBP_ENC_DELTA_PALETTIZATION_H_ +#define WEBP_ENC_DELTA_PALETTIZATION_H_ + +#include "../webp/encode.h" +#include "../enc/vp8li_enc.h" + +// Replaces enc->argb_[] input by a palettizable approximation of it, +// and generates optimal enc->palette_[]. +// This function can revert enc->use_palette_ / enc->use_predict_ flag +// if delta-palettization is not producing expected saving. +WebPEncodingError WebPSearchOptimalDeltaPalette(VP8LEncoder* const enc); + +#endif // WEBP_ENC_DELTA_PALETTIZATION_H_ diff --git a/thirdparty/libwebp/enc/filter.c b/thirdparty/libwebp/enc/filter.c deleted file mode 100644 index e8ea8b4ff2..0000000000 --- a/thirdparty/libwebp/enc/filter.c +++ /dev/null @@ -1,306 +0,0 @@ -// Copyright 2011 Google Inc. All Rights Reserved. -// -// Use of this source code is governed by a BSD-style license -// that can be found in the COPYING file in the root of the source -// tree. An additional intellectual property rights grant can be found -// in the file PATENTS. All contributing project authors may -// be found in the AUTHORS file in the root of the source tree. -// ----------------------------------------------------------------------------- -// -// Selecting filter level -// -// Author: somnath@google.com (Somnath Banerjee) - -#include -#include "./vp8enci.h" -#include "../dsp/dsp.h" - -// This table gives, for a given sharpness, the filtering strength to be -// used (at least) in order to filter a given edge step delta. -// This is constructed by brute force inspection: for all delta, we iterate -// over all possible filtering strength / thresh until needs_filter() returns -// true. -#define MAX_DELTA_SIZE 64 -static const uint8_t kLevelsFromDelta[8][MAX_DELTA_SIZE] = { - { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, - 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, - 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, - 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63 }, - { 0, 1, 2, 3, 5, 6, 7, 8, 9, 11, 12, 13, 14, 15, 17, 18, - 20, 21, 23, 24, 26, 27, 29, 30, 32, 33, 35, 36, 38, 39, 41, 42, - 44, 45, 47, 48, 50, 51, 53, 54, 56, 57, 59, 60, 62, 63, 63, 63, - 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63 }, - { 0, 1, 2, 3, 5, 6, 7, 8, 9, 11, 12, 13, 14, 16, 17, 19, - 20, 22, 23, 25, 26, 28, 29, 31, 32, 34, 35, 37, 38, 40, 41, 43, - 44, 46, 47, 49, 50, 52, 53, 55, 56, 58, 59, 61, 62, 63, 63, 63, - 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63 }, - { 0, 1, 2, 3, 5, 6, 7, 8, 9, 11, 12, 13, 15, 16, 18, 19, - 21, 22, 24, 25, 27, 28, 30, 31, 33, 34, 36, 37, 39, 40, 42, 43, - 45, 46, 48, 49, 51, 52, 54, 55, 57, 58, 60, 61, 63, 63, 63, 63, - 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63 }, - { 0, 1, 2, 3, 5, 6, 7, 8, 9, 11, 12, 14, 15, 17, 18, 20, - 21, 23, 24, 26, 27, 29, 30, 32, 33, 35, 36, 38, 39, 41, 42, 44, - 45, 47, 48, 50, 51, 53, 54, 56, 57, 59, 60, 62, 63, 63, 63, 63, - 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63 }, - { 0, 1, 2, 4, 5, 7, 8, 9, 11, 12, 13, 15, 16, 17, 19, 20, - 22, 23, 25, 26, 28, 29, 31, 32, 34, 35, 37, 38, 40, 41, 43, 44, - 46, 47, 49, 50, 52, 53, 55, 56, 58, 59, 61, 62, 63, 63, 63, 63, - 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63 }, - { 0, 1, 2, 4, 5, 7, 8, 9, 11, 12, 13, 15, 16, 18, 19, 21, - 22, 24, 25, 27, 28, 30, 31, 33, 34, 36, 37, 39, 40, 42, 43, 45, - 46, 48, 49, 51, 52, 54, 55, 57, 58, 60, 61, 63, 63, 63, 63, 63, - 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63 }, - { 0, 1, 2, 4, 5, 7, 8, 9, 11, 12, 14, 15, 17, 18, 20, 21, - 23, 24, 26, 27, 29, 30, 32, 33, 35, 36, 38, 39, 41, 42, 44, 45, - 47, 48, 50, 51, 53, 54, 56, 57, 59, 60, 62, 63, 63, 63, 63, 63, - 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63 } -}; - -int VP8FilterStrengthFromDelta(int sharpness, int delta) { - const int pos = (delta < MAX_DELTA_SIZE) ? delta : MAX_DELTA_SIZE - 1; - assert(sharpness >= 0 && sharpness <= 7); - return kLevelsFromDelta[sharpness][pos]; -} - -//------------------------------------------------------------------------------ -// Paragraph 15.4: compute the inner-edge filtering strength - -static int GetILevel(int sharpness, int level) { - if (sharpness > 0) { - if (sharpness > 4) { - level >>= 2; - } else { - level >>= 1; - } - if (level > 9 - sharpness) { - level = 9 - sharpness; - } - } - if (level < 1) level = 1; - return level; -} - -static void DoFilter(const VP8EncIterator* const it, int level) { - const VP8Encoder* const enc = it->enc_; - const int ilevel = GetILevel(enc->config_->filter_sharpness, level); - const int limit = 2 * level + ilevel; - - uint8_t* const y_dst = it->yuv_out2_ + Y_OFF_ENC; - uint8_t* const u_dst = it->yuv_out2_ + U_OFF_ENC; - uint8_t* const v_dst = it->yuv_out2_ + V_OFF_ENC; - - // copy current block to yuv_out2_ - memcpy(y_dst, it->yuv_out_, YUV_SIZE_ENC * sizeof(uint8_t)); - - if (enc->filter_hdr_.simple_ == 1) { // simple - VP8SimpleHFilter16i(y_dst, BPS, limit); - VP8SimpleVFilter16i(y_dst, BPS, limit); - } else { // complex - const int hev_thresh = (level >= 40) ? 2 : (level >= 15) ? 1 : 0; - VP8HFilter16i(y_dst, BPS, limit, ilevel, hev_thresh); - VP8HFilter8i(u_dst, v_dst, BPS, limit, ilevel, hev_thresh); - VP8VFilter16i(y_dst, BPS, limit, ilevel, hev_thresh); - VP8VFilter8i(u_dst, v_dst, BPS, limit, ilevel, hev_thresh); - } -} - -//------------------------------------------------------------------------------ -// SSIM metric - -static const double kMinValue = 1.e-10; // minimal threshold - -void VP8SSIMAddStats(const VP8DistoStats* const src, VP8DistoStats* const dst) { - dst->w += src->w; - dst->xm += src->xm; - dst->ym += src->ym; - dst->xxm += src->xxm; - dst->xym += src->xym; - dst->yym += src->yym; -} - -double VP8SSIMGet(const VP8DistoStats* const stats) { - const double xmxm = stats->xm * stats->xm; - const double ymym = stats->ym * stats->ym; - const double xmym = stats->xm * stats->ym; - const double w2 = stats->w * stats->w; - double sxx = stats->xxm * stats->w - xmxm; - double syy = stats->yym * stats->w - ymym; - double sxy = stats->xym * stats->w - xmym; - double C1, C2; - double fnum; - double fden; - // small errors are possible, due to rounding. Clamp to zero. - if (sxx < 0.) sxx = 0.; - if (syy < 0.) syy = 0.; - C1 = 6.5025 * w2; - C2 = 58.5225 * w2; - fnum = (2 * xmym + C1) * (2 * sxy + C2); - fden = (xmxm + ymym + C1) * (sxx + syy + C2); - return (fden != 0.) ? fnum / fden : kMinValue; -} - -double VP8SSIMGetSquaredError(const VP8DistoStats* const s) { - if (s->w > 0.) { - const double iw2 = 1. / (s->w * s->w); - const double sxx = s->xxm * s->w - s->xm * s->xm; - const double syy = s->yym * s->w - s->ym * s->ym; - const double sxy = s->xym * s->w - s->xm * s->ym; - const double SSE = iw2 * (sxx + syy - 2. * sxy); - if (SSE > kMinValue) return SSE; - } - return kMinValue; -} - -#define LIMIT(A, M) ((A) > (M) ? (M) : (A)) -static void VP8SSIMAccumulateRow(const uint8_t* src1, int stride1, - const uint8_t* src2, int stride2, - int y, int W, int H, - VP8DistoStats* const stats) { - int x = 0; - const int w0 = LIMIT(VP8_SSIM_KERNEL, W); - for (x = 0; x < w0; ++x) { - VP8SSIMAccumulateClipped(src1, stride1, src2, stride2, x, y, W, H, stats); - } - for (; x <= W - 8 + VP8_SSIM_KERNEL; ++x) { - VP8SSIMAccumulate( - src1 + (y - VP8_SSIM_KERNEL) * stride1 + (x - VP8_SSIM_KERNEL), stride1, - src2 + (y - VP8_SSIM_KERNEL) * stride2 + (x - VP8_SSIM_KERNEL), stride2, - stats); - } - for (; x < W; ++x) { - VP8SSIMAccumulateClipped(src1, stride1, src2, stride2, x, y, W, H, stats); - } -} - -void VP8SSIMAccumulatePlane(const uint8_t* src1, int stride1, - const uint8_t* src2, int stride2, - int W, int H, VP8DistoStats* const stats) { - int x, y; - const int h0 = LIMIT(VP8_SSIM_KERNEL, H); - const int h1 = LIMIT(VP8_SSIM_KERNEL, H - VP8_SSIM_KERNEL); - for (y = 0; y < h0; ++y) { - for (x = 0; x < W; ++x) { - VP8SSIMAccumulateClipped(src1, stride1, src2, stride2, x, y, W, H, stats); - } - } - for (; y < h1; ++y) { - VP8SSIMAccumulateRow(src1, stride1, src2, stride2, y, W, H, stats); - } - for (; y < H; ++y) { - for (x = 0; x < W; ++x) { - VP8SSIMAccumulateClipped(src1, stride1, src2, stride2, x, y, W, H, stats); - } - } -} -#undef LIMIT - -static double GetMBSSIM(const uint8_t* yuv1, const uint8_t* yuv2) { - int x, y; - VP8DistoStats s = { .0, .0, .0, .0, .0, .0 }; - - // compute SSIM in a 10 x 10 window - for (y = VP8_SSIM_KERNEL; y < 16 - VP8_SSIM_KERNEL; y++) { - for (x = VP8_SSIM_KERNEL; x < 16 - VP8_SSIM_KERNEL; x++) { - VP8SSIMAccumulateClipped(yuv1 + Y_OFF_ENC, BPS, yuv2 + Y_OFF_ENC, BPS, - x, y, 16, 16, &s); - } - } - for (x = 1; x < 7; x++) { - for (y = 1; y < 7; y++) { - VP8SSIMAccumulateClipped(yuv1 + U_OFF_ENC, BPS, yuv2 + U_OFF_ENC, BPS, - x, y, 8, 8, &s); - VP8SSIMAccumulateClipped(yuv1 + V_OFF_ENC, BPS, yuv2 + V_OFF_ENC, BPS, - x, y, 8, 8, &s); - } - } - return VP8SSIMGet(&s); -} - -//------------------------------------------------------------------------------ -// Exposed APIs: Encoder should call the following 3 functions to adjust -// loop filter strength - -void VP8InitFilter(VP8EncIterator* const it) { - if (it->lf_stats_ != NULL) { - int s, i; - for (s = 0; s < NUM_MB_SEGMENTS; s++) { - for (i = 0; i < MAX_LF_LEVELS; i++) { - (*it->lf_stats_)[s][i] = 0; - } - } - VP8SSIMDspInit(); - } -} - -void VP8StoreFilterStats(VP8EncIterator* const it) { - int d; - VP8Encoder* const enc = it->enc_; - const int s = it->mb_->segment_; - const int level0 = enc->dqm_[s].fstrength_; - - // explore +/-quant range of values around level0 - const int delta_min = -enc->dqm_[s].quant_; - const int delta_max = enc->dqm_[s].quant_; - const int step_size = (delta_max - delta_min >= 4) ? 4 : 1; - - if (it->lf_stats_ == NULL) return; - - // NOTE: Currently we are applying filter only across the sublock edges - // There are two reasons for that. - // 1. Applying filter on macro block edges will change the pixels in - // the left and top macro blocks. That will be hard to restore - // 2. Macro Blocks on the bottom and right are not yet compressed. So we - // cannot apply filter on the right and bottom macro block edges. - if (it->mb_->type_ == 1 && it->mb_->skip_) return; - - // Always try filter level zero - (*it->lf_stats_)[s][0] += GetMBSSIM(it->yuv_in_, it->yuv_out_); - - for (d = delta_min; d <= delta_max; d += step_size) { - const int level = level0 + d; - if (level <= 0 || level >= MAX_LF_LEVELS) { - continue; - } - DoFilter(it, level); - (*it->lf_stats_)[s][level] += GetMBSSIM(it->yuv_in_, it->yuv_out2_); - } -} - -void VP8AdjustFilterStrength(VP8EncIterator* const it) { - VP8Encoder* const enc = it->enc_; - if (it->lf_stats_ != NULL) { - int s; - for (s = 0; s < NUM_MB_SEGMENTS; s++) { - int i, best_level = 0; - // Improvement over filter level 0 should be at least 1e-5 (relatively) - double best_v = 1.00001 * (*it->lf_stats_)[s][0]; - for (i = 1; i < MAX_LF_LEVELS; i++) { - const double v = (*it->lf_stats_)[s][i]; - if (v > best_v) { - best_v = v; - best_level = i; - } - } - enc->dqm_[s].fstrength_ = best_level; - } - } else if (enc->config_->filter_strength > 0) { - int max_level = 0; - int s; - for (s = 0; s < NUM_MB_SEGMENTS; s++) { - VP8SegmentInfo* const dqm = &enc->dqm_[s]; - // this '>> 3' accounts for some inverse WHT scaling - const int delta = (dqm->max_edge_ * dqm->y2_.q_[1]) >> 3; - const int level = - VP8FilterStrengthFromDelta(enc->filter_hdr_.sharpness_, delta); - if (level > dqm->fstrength_) { - dqm->fstrength_ = level; - } - if (max_level < dqm->fstrength_) { - max_level = dqm->fstrength_; - } - } - enc->filter_hdr_.level_ = max_level; - } -} - -// ----------------------------------------------------------------------------- diff --git a/thirdparty/libwebp/enc/filter_enc.c b/thirdparty/libwebp/enc/filter_enc.c new file mode 100644 index 0000000000..4bc367274c --- /dev/null +++ b/thirdparty/libwebp/enc/filter_enc.c @@ -0,0 +1,219 @@ +// Copyright 2011 Google Inc. All Rights Reserved. +// +// Use of this source code is governed by a BSD-style license +// that can be found in the COPYING file in the root of the source +// tree. An additional intellectual property rights grant can be found +// in the file PATENTS. All contributing project authors may +// be found in the AUTHORS file in the root of the source tree. +// ----------------------------------------------------------------------------- +// +// Selecting filter level +// +// Author: somnath@google.com (Somnath Banerjee) + +#include +#include "./vp8i_enc.h" +#include "../dsp/dsp.h" + +// This table gives, for a given sharpness, the filtering strength to be +// used (at least) in order to filter a given edge step delta. +// This is constructed by brute force inspection: for all delta, we iterate +// over all possible filtering strength / thresh until needs_filter() returns +// true. +#define MAX_DELTA_SIZE 64 +static const uint8_t kLevelsFromDelta[8][MAX_DELTA_SIZE] = { + { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, + 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, + 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, + 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63 }, + { 0, 1, 2, 3, 5, 6, 7, 8, 9, 11, 12, 13, 14, 15, 17, 18, + 20, 21, 23, 24, 26, 27, 29, 30, 32, 33, 35, 36, 38, 39, 41, 42, + 44, 45, 47, 48, 50, 51, 53, 54, 56, 57, 59, 60, 62, 63, 63, 63, + 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63 }, + { 0, 1, 2, 3, 5, 6, 7, 8, 9, 11, 12, 13, 14, 16, 17, 19, + 20, 22, 23, 25, 26, 28, 29, 31, 32, 34, 35, 37, 38, 40, 41, 43, + 44, 46, 47, 49, 50, 52, 53, 55, 56, 58, 59, 61, 62, 63, 63, 63, + 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63 }, + { 0, 1, 2, 3, 5, 6, 7, 8, 9, 11, 12, 13, 15, 16, 18, 19, + 21, 22, 24, 25, 27, 28, 30, 31, 33, 34, 36, 37, 39, 40, 42, 43, + 45, 46, 48, 49, 51, 52, 54, 55, 57, 58, 60, 61, 63, 63, 63, 63, + 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63 }, + { 0, 1, 2, 3, 5, 6, 7, 8, 9, 11, 12, 14, 15, 17, 18, 20, + 21, 23, 24, 26, 27, 29, 30, 32, 33, 35, 36, 38, 39, 41, 42, 44, + 45, 47, 48, 50, 51, 53, 54, 56, 57, 59, 60, 62, 63, 63, 63, 63, + 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63 }, + { 0, 1, 2, 4, 5, 7, 8, 9, 11, 12, 13, 15, 16, 17, 19, 20, + 22, 23, 25, 26, 28, 29, 31, 32, 34, 35, 37, 38, 40, 41, 43, 44, + 46, 47, 49, 50, 52, 53, 55, 56, 58, 59, 61, 62, 63, 63, 63, 63, + 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63 }, + { 0, 1, 2, 4, 5, 7, 8, 9, 11, 12, 13, 15, 16, 18, 19, 21, + 22, 24, 25, 27, 28, 30, 31, 33, 34, 36, 37, 39, 40, 42, 43, 45, + 46, 48, 49, 51, 52, 54, 55, 57, 58, 60, 61, 63, 63, 63, 63, 63, + 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63 }, + { 0, 1, 2, 4, 5, 7, 8, 9, 11, 12, 14, 15, 17, 18, 20, 21, + 23, 24, 26, 27, 29, 30, 32, 33, 35, 36, 38, 39, 41, 42, 44, 45, + 47, 48, 50, 51, 53, 54, 56, 57, 59, 60, 62, 63, 63, 63, 63, 63, + 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63 } +}; + +int VP8FilterStrengthFromDelta(int sharpness, int delta) { + const int pos = (delta < MAX_DELTA_SIZE) ? delta : MAX_DELTA_SIZE - 1; + assert(sharpness >= 0 && sharpness <= 7); + return kLevelsFromDelta[sharpness][pos]; +} + +//------------------------------------------------------------------------------ +// Paragraph 15.4: compute the inner-edge filtering strength + +static int GetILevel(int sharpness, int level) { + if (sharpness > 0) { + if (sharpness > 4) { + level >>= 2; + } else { + level >>= 1; + } + if (level > 9 - sharpness) { + level = 9 - sharpness; + } + } + if (level < 1) level = 1; + return level; +} + +static void DoFilter(const VP8EncIterator* const it, int level) { + const VP8Encoder* const enc = it->enc_; + const int ilevel = GetILevel(enc->config_->filter_sharpness, level); + const int limit = 2 * level + ilevel; + + uint8_t* const y_dst = it->yuv_out2_ + Y_OFF_ENC; + uint8_t* const u_dst = it->yuv_out2_ + U_OFF_ENC; + uint8_t* const v_dst = it->yuv_out2_ + V_OFF_ENC; + + // copy current block to yuv_out2_ + memcpy(y_dst, it->yuv_out_, YUV_SIZE_ENC * sizeof(uint8_t)); + + if (enc->filter_hdr_.simple_ == 1) { // simple + VP8SimpleHFilter16i(y_dst, BPS, limit); + VP8SimpleVFilter16i(y_dst, BPS, limit); + } else { // complex + const int hev_thresh = (level >= 40) ? 2 : (level >= 15) ? 1 : 0; + VP8HFilter16i(y_dst, BPS, limit, ilevel, hev_thresh); + VP8HFilter8i(u_dst, v_dst, BPS, limit, ilevel, hev_thresh); + VP8VFilter16i(y_dst, BPS, limit, ilevel, hev_thresh); + VP8VFilter8i(u_dst, v_dst, BPS, limit, ilevel, hev_thresh); + } +} + +//------------------------------------------------------------------------------ +// SSIM metric for one macroblock + +static double GetMBSSIM(const uint8_t* yuv1, const uint8_t* yuv2) { + int x, y; + double sum = 0.; + + // compute SSIM in a 10 x 10 window + for (y = VP8_SSIM_KERNEL; y < 16 - VP8_SSIM_KERNEL; y++) { + for (x = VP8_SSIM_KERNEL; x < 16 - VP8_SSIM_KERNEL; x++) { + sum += VP8SSIMGetClipped(yuv1 + Y_OFF_ENC, BPS, yuv2 + Y_OFF_ENC, BPS, + x, y, 16, 16); + } + } + for (x = 1; x < 7; x++) { + for (y = 1; y < 7; y++) { + sum += VP8SSIMGetClipped(yuv1 + U_OFF_ENC, BPS, yuv2 + U_OFF_ENC, BPS, + x, y, 8, 8); + sum += VP8SSIMGetClipped(yuv1 + V_OFF_ENC, BPS, yuv2 + V_OFF_ENC, BPS, + x, y, 8, 8); + } + } + return sum; +} + +//------------------------------------------------------------------------------ +// Exposed APIs: Encoder should call the following 3 functions to adjust +// loop filter strength + +void VP8InitFilter(VP8EncIterator* const it) { + if (it->lf_stats_ != NULL) { + int s, i; + for (s = 0; s < NUM_MB_SEGMENTS; s++) { + for (i = 0; i < MAX_LF_LEVELS; i++) { + (*it->lf_stats_)[s][i] = 0; + } + } + VP8SSIMDspInit(); + } +} + +void VP8StoreFilterStats(VP8EncIterator* const it) { + int d; + VP8Encoder* const enc = it->enc_; + const int s = it->mb_->segment_; + const int level0 = enc->dqm_[s].fstrength_; + + // explore +/-quant range of values around level0 + const int delta_min = -enc->dqm_[s].quant_; + const int delta_max = enc->dqm_[s].quant_; + const int step_size = (delta_max - delta_min >= 4) ? 4 : 1; + + if (it->lf_stats_ == NULL) return; + + // NOTE: Currently we are applying filter only across the sublock edges + // There are two reasons for that. + // 1. Applying filter on macro block edges will change the pixels in + // the left and top macro blocks. That will be hard to restore + // 2. Macro Blocks on the bottom and right are not yet compressed. So we + // cannot apply filter on the right and bottom macro block edges. + if (it->mb_->type_ == 1 && it->mb_->skip_) return; + + // Always try filter level zero + (*it->lf_stats_)[s][0] += GetMBSSIM(it->yuv_in_, it->yuv_out_); + + for (d = delta_min; d <= delta_max; d += step_size) { + const int level = level0 + d; + if (level <= 0 || level >= MAX_LF_LEVELS) { + continue; + } + DoFilter(it, level); + (*it->lf_stats_)[s][level] += GetMBSSIM(it->yuv_in_, it->yuv_out2_); + } +} + +void VP8AdjustFilterStrength(VP8EncIterator* const it) { + VP8Encoder* const enc = it->enc_; + if (it->lf_stats_ != NULL) { + int s; + for (s = 0; s < NUM_MB_SEGMENTS; s++) { + int i, best_level = 0; + // Improvement over filter level 0 should be at least 1e-5 (relatively) + double best_v = 1.00001 * (*it->lf_stats_)[s][0]; + for (i = 1; i < MAX_LF_LEVELS; i++) { + const double v = (*it->lf_stats_)[s][i]; + if (v > best_v) { + best_v = v; + best_level = i; + } + } + enc->dqm_[s].fstrength_ = best_level; + } + } else if (enc->config_->filter_strength > 0) { + int max_level = 0; + int s; + for (s = 0; s < NUM_MB_SEGMENTS; s++) { + VP8SegmentInfo* const dqm = &enc->dqm_[s]; + // this '>> 3' accounts for some inverse WHT scaling + const int delta = (dqm->max_edge_ * dqm->y2_.q_[1]) >> 3; + const int level = + VP8FilterStrengthFromDelta(enc->filter_hdr_.sharpness_, delta); + if (level > dqm->fstrength_) { + dqm->fstrength_ = level; + } + if (max_level < dqm->fstrength_) { + max_level = dqm->fstrength_; + } + } + enc->filter_hdr_.level_ = max_level; + } +} + +// ----------------------------------------------------------------------------- diff --git a/thirdparty/libwebp/enc/frame.c b/thirdparty/libwebp/enc/frame.c deleted file mode 100644 index 57fc471d17..0000000000 --- a/thirdparty/libwebp/enc/frame.c +++ /dev/null @@ -1,852 +0,0 @@ -// Copyright 2011 Google Inc. All Rights Reserved. -// -// Use of this source code is governed by a BSD-style license -// that can be found in the COPYING file in the root of the source -// tree. An additional intellectual property rights grant can be found -// in the file PATENTS. All contributing project authors may -// be found in the AUTHORS file in the root of the source tree. -// ----------------------------------------------------------------------------- -// -// frame coding and analysis -// -// Author: Skal (pascal.massimino@gmail.com) - -#include -#include - -#include "./cost.h" -#include "./vp8enci.h" -#include "../dsp/dsp.h" -#include "../webp/format_constants.h" // RIFF constants - -#define SEGMENT_VISU 0 -#define DEBUG_SEARCH 0 // useful to track search convergence - -//------------------------------------------------------------------------------ -// multi-pass convergence - -#define HEADER_SIZE_ESTIMATE (RIFF_HEADER_SIZE + CHUNK_HEADER_SIZE + \ - VP8_FRAME_HEADER_SIZE) -#define DQ_LIMIT 0.4 // convergence is considered reached if dq < DQ_LIMIT -// we allow 2k of extra head-room in PARTITION0 limit. -#define PARTITION0_SIZE_LIMIT ((VP8_MAX_PARTITION0_SIZE - 2048ULL) << 11) - -typedef struct { // struct for organizing convergence in either size or PSNR - int is_first; - float dq; - float q, last_q; - double value, last_value; // PSNR or size - double target; - int do_size_search; -} PassStats; - -static int InitPassStats(const VP8Encoder* const enc, PassStats* const s) { - const uint64_t target_size = (uint64_t)enc->config_->target_size; - const int do_size_search = (target_size != 0); - const float target_PSNR = enc->config_->target_PSNR; - - s->is_first = 1; - s->dq = 10.f; - s->q = s->last_q = enc->config_->quality; - s->target = do_size_search ? (double)target_size - : (target_PSNR > 0.) ? target_PSNR - : 40.; // default, just in case - s->value = s->last_value = 0.; - s->do_size_search = do_size_search; - return do_size_search; -} - -static float Clamp(float v, float min, float max) { - return (v < min) ? min : (v > max) ? max : v; -} - -static float ComputeNextQ(PassStats* const s) { - float dq; - if (s->is_first) { - dq = (s->value > s->target) ? -s->dq : s->dq; - s->is_first = 0; - } else if (s->value != s->last_value) { - const double slope = (s->target - s->value) / (s->last_value - s->value); - dq = (float)(slope * (s->last_q - s->q)); - } else { - dq = 0.; // we're done?! - } - // Limit variable to avoid large swings. - s->dq = Clamp(dq, -30.f, 30.f); - s->last_q = s->q; - s->last_value = s->value; - s->q = Clamp(s->q + s->dq, 0.f, 100.f); - return s->q; -} - -//------------------------------------------------------------------------------ -// Tables for level coding - -const uint8_t VP8Cat3[] = { 173, 148, 140 }; -const uint8_t VP8Cat4[] = { 176, 155, 140, 135 }; -const uint8_t VP8Cat5[] = { 180, 157, 141, 134, 130 }; -const uint8_t VP8Cat6[] = - { 254, 254, 243, 230, 196, 177, 153, 140, 133, 130, 129 }; - -//------------------------------------------------------------------------------ -// Reset the statistics about: number of skips, token proba, level cost,... - -static void ResetStats(VP8Encoder* const enc) { - VP8EncProba* const proba = &enc->proba_; - VP8CalculateLevelCosts(proba); - proba->nb_skip_ = 0; -} - -//------------------------------------------------------------------------------ -// Skip decision probability - -#define SKIP_PROBA_THRESHOLD 250 // value below which using skip_proba is OK. - -static int CalcSkipProba(uint64_t nb, uint64_t total) { - return (int)(total ? (total - nb) * 255 / total : 255); -} - -// Returns the bit-cost for coding the skip probability. -static int FinalizeSkipProba(VP8Encoder* const enc) { - VP8EncProba* const proba = &enc->proba_; - const int nb_mbs = enc->mb_w_ * enc->mb_h_; - const int nb_events = proba->nb_skip_; - int size; - proba->skip_proba_ = CalcSkipProba(nb_events, nb_mbs); - proba->use_skip_proba_ = (proba->skip_proba_ < SKIP_PROBA_THRESHOLD); - size = 256; // 'use_skip_proba' bit - if (proba->use_skip_proba_) { - size += nb_events * VP8BitCost(1, proba->skip_proba_) - + (nb_mbs - nb_events) * VP8BitCost(0, proba->skip_proba_); - size += 8 * 256; // cost of signaling the skip_proba_ itself. - } - return size; -} - -// Collect statistics and deduce probabilities for next coding pass. -// Return the total bit-cost for coding the probability updates. -static int CalcTokenProba(int nb, int total) { - assert(nb <= total); - return nb ? (255 - nb * 255 / total) : 255; -} - -// Cost of coding 'nb' 1's and 'total-nb' 0's using 'proba' probability. -static int BranchCost(int nb, int total, int proba) { - return nb * VP8BitCost(1, proba) + (total - nb) * VP8BitCost(0, proba); -} - -static void ResetTokenStats(VP8Encoder* const enc) { - VP8EncProba* const proba = &enc->proba_; - memset(proba->stats_, 0, sizeof(proba->stats_)); -} - -static int FinalizeTokenProbas(VP8EncProba* const proba) { - int has_changed = 0; - int size = 0; - int t, b, c, p; - for (t = 0; t < NUM_TYPES; ++t) { - for (b = 0; b < NUM_BANDS; ++b) { - for (c = 0; c < NUM_CTX; ++c) { - for (p = 0; p < NUM_PROBAS; ++p) { - const proba_t stats = proba->stats_[t][b][c][p]; - const int nb = (stats >> 0) & 0xffff; - const int total = (stats >> 16) & 0xffff; - const int update_proba = VP8CoeffsUpdateProba[t][b][c][p]; - const int old_p = VP8CoeffsProba0[t][b][c][p]; - const int new_p = CalcTokenProba(nb, total); - const int old_cost = BranchCost(nb, total, old_p) - + VP8BitCost(0, update_proba); - const int new_cost = BranchCost(nb, total, new_p) - + VP8BitCost(1, update_proba) - + 8 * 256; - const int use_new_p = (old_cost > new_cost); - size += VP8BitCost(use_new_p, update_proba); - if (use_new_p) { // only use proba that seem meaningful enough. - proba->coeffs_[t][b][c][p] = new_p; - has_changed |= (new_p != old_p); - size += 8 * 256; - } else { - proba->coeffs_[t][b][c][p] = old_p; - } - } - } - } - } - proba->dirty_ = has_changed; - return size; -} - -//------------------------------------------------------------------------------ -// Finalize Segment probability based on the coding tree - -static int GetProba(int a, int b) { - const int total = a + b; - return (total == 0) ? 255 // that's the default probability. - : (255 * a + total / 2) / total; // rounded proba -} - -static void ResetSegments(VP8Encoder* const enc) { - int n; - for (n = 0; n < enc->mb_w_ * enc->mb_h_; ++n) { - enc->mb_info_[n].segment_ = 0; - } -} - -static void SetSegmentProbas(VP8Encoder* const enc) { - int p[NUM_MB_SEGMENTS] = { 0 }; - int n; - - for (n = 0; n < enc->mb_w_ * enc->mb_h_; ++n) { - const VP8MBInfo* const mb = &enc->mb_info_[n]; - p[mb->segment_]++; - } - if (enc->pic_->stats != NULL) { - for (n = 0; n < NUM_MB_SEGMENTS; ++n) { - enc->pic_->stats->segment_size[n] = p[n]; - } - } - if (enc->segment_hdr_.num_segments_ > 1) { - uint8_t* const probas = enc->proba_.segments_; - probas[0] = GetProba(p[0] + p[1], p[2] + p[3]); - probas[1] = GetProba(p[0], p[1]); - probas[2] = GetProba(p[2], p[3]); - - enc->segment_hdr_.update_map_ = - (probas[0] != 255) || (probas[1] != 255) || (probas[2] != 255); - if (!enc->segment_hdr_.update_map_) ResetSegments(enc); - enc->segment_hdr_.size_ = - p[0] * (VP8BitCost(0, probas[0]) + VP8BitCost(0, probas[1])) + - p[1] * (VP8BitCost(0, probas[0]) + VP8BitCost(1, probas[1])) + - p[2] * (VP8BitCost(1, probas[0]) + VP8BitCost(0, probas[2])) + - p[3] * (VP8BitCost(1, probas[0]) + VP8BitCost(1, probas[2])); - } else { - enc->segment_hdr_.update_map_ = 0; - enc->segment_hdr_.size_ = 0; - } -} - -//------------------------------------------------------------------------------ -// Coefficient coding - -static int PutCoeffs(VP8BitWriter* const bw, int ctx, const VP8Residual* res) { - int n = res->first; - // should be prob[VP8EncBands[n]], but it's equivalent for n=0 or 1 - const uint8_t* p = res->prob[n][ctx]; - if (!VP8PutBit(bw, res->last >= 0, p[0])) { - return 0; - } - - while (n < 16) { - const int c = res->coeffs[n++]; - const int sign = c < 0; - int v = sign ? -c : c; - if (!VP8PutBit(bw, v != 0, p[1])) { - p = res->prob[VP8EncBands[n]][0]; - continue; - } - if (!VP8PutBit(bw, v > 1, p[2])) { - p = res->prob[VP8EncBands[n]][1]; - } else { - if (!VP8PutBit(bw, v > 4, p[3])) { - if (VP8PutBit(bw, v != 2, p[4])) - VP8PutBit(bw, v == 4, p[5]); - } else if (!VP8PutBit(bw, v > 10, p[6])) { - if (!VP8PutBit(bw, v > 6, p[7])) { - VP8PutBit(bw, v == 6, 159); - } else { - VP8PutBit(bw, v >= 9, 165); - VP8PutBit(bw, !(v & 1), 145); - } - } else { - int mask; - const uint8_t* tab; - if (v < 3 + (8 << 1)) { // VP8Cat3 (3b) - VP8PutBit(bw, 0, p[8]); - VP8PutBit(bw, 0, p[9]); - v -= 3 + (8 << 0); - mask = 1 << 2; - tab = VP8Cat3; - } else if (v < 3 + (8 << 2)) { // VP8Cat4 (4b) - VP8PutBit(bw, 0, p[8]); - VP8PutBit(bw, 1, p[9]); - v -= 3 + (8 << 1); - mask = 1 << 3; - tab = VP8Cat4; - } else if (v < 3 + (8 << 3)) { // VP8Cat5 (5b) - VP8PutBit(bw, 1, p[8]); - VP8PutBit(bw, 0, p[10]); - v -= 3 + (8 << 2); - mask = 1 << 4; - tab = VP8Cat5; - } else { // VP8Cat6 (11b) - VP8PutBit(bw, 1, p[8]); - VP8PutBit(bw, 1, p[10]); - v -= 3 + (8 << 3); - mask = 1 << 10; - tab = VP8Cat6; - } - while (mask) { - VP8PutBit(bw, !!(v & mask), *tab++); - mask >>= 1; - } - } - p = res->prob[VP8EncBands[n]][2]; - } - VP8PutBitUniform(bw, sign); - if (n == 16 || !VP8PutBit(bw, n <= res->last, p[0])) { - return 1; // EOB - } - } - return 1; -} - -static void CodeResiduals(VP8BitWriter* const bw, VP8EncIterator* const it, - const VP8ModeScore* const rd) { - int x, y, ch; - VP8Residual res; - uint64_t pos1, pos2, pos3; - const int i16 = (it->mb_->type_ == 1); - const int segment = it->mb_->segment_; - VP8Encoder* const enc = it->enc_; - - VP8IteratorNzToBytes(it); - - pos1 = VP8BitWriterPos(bw); - if (i16) { - VP8InitResidual(0, 1, enc, &res); - VP8SetResidualCoeffs(rd->y_dc_levels, &res); - it->top_nz_[8] = it->left_nz_[8] = - PutCoeffs(bw, it->top_nz_[8] + it->left_nz_[8], &res); - VP8InitResidual(1, 0, enc, &res); - } else { - VP8InitResidual(0, 3, enc, &res); - } - - // luma-AC - for (y = 0; y < 4; ++y) { - for (x = 0; x < 4; ++x) { - const int ctx = it->top_nz_[x] + it->left_nz_[y]; - VP8SetResidualCoeffs(rd->y_ac_levels[x + y * 4], &res); - it->top_nz_[x] = it->left_nz_[y] = PutCoeffs(bw, ctx, &res); - } - } - pos2 = VP8BitWriterPos(bw); - - // U/V - VP8InitResidual(0, 2, enc, &res); - for (ch = 0; ch <= 2; ch += 2) { - for (y = 0; y < 2; ++y) { - for (x = 0; x < 2; ++x) { - const int ctx = it->top_nz_[4 + ch + x] + it->left_nz_[4 + ch + y]; - VP8SetResidualCoeffs(rd->uv_levels[ch * 2 + x + y * 2], &res); - it->top_nz_[4 + ch + x] = it->left_nz_[4 + ch + y] = - PutCoeffs(bw, ctx, &res); - } - } - } - pos3 = VP8BitWriterPos(bw); - it->luma_bits_ = pos2 - pos1; - it->uv_bits_ = pos3 - pos2; - it->bit_count_[segment][i16] += it->luma_bits_; - it->bit_count_[segment][2] += it->uv_bits_; - VP8IteratorBytesToNz(it); -} - -// Same as CodeResiduals, but doesn't actually write anything. -// Instead, it just records the event distribution. -static void RecordResiduals(VP8EncIterator* const it, - const VP8ModeScore* const rd) { - int x, y, ch; - VP8Residual res; - VP8Encoder* const enc = it->enc_; - - VP8IteratorNzToBytes(it); - - if (it->mb_->type_ == 1) { // i16x16 - VP8InitResidual(0, 1, enc, &res); - VP8SetResidualCoeffs(rd->y_dc_levels, &res); - it->top_nz_[8] = it->left_nz_[8] = - VP8RecordCoeffs(it->top_nz_[8] + it->left_nz_[8], &res); - VP8InitResidual(1, 0, enc, &res); - } else { - VP8InitResidual(0, 3, enc, &res); - } - - // luma-AC - for (y = 0; y < 4; ++y) { - for (x = 0; x < 4; ++x) { - const int ctx = it->top_nz_[x] + it->left_nz_[y]; - VP8SetResidualCoeffs(rd->y_ac_levels[x + y * 4], &res); - it->top_nz_[x] = it->left_nz_[y] = VP8RecordCoeffs(ctx, &res); - } - } - - // U/V - VP8InitResidual(0, 2, enc, &res); - for (ch = 0; ch <= 2; ch += 2) { - for (y = 0; y < 2; ++y) { - for (x = 0; x < 2; ++x) { - const int ctx = it->top_nz_[4 + ch + x] + it->left_nz_[4 + ch + y]; - VP8SetResidualCoeffs(rd->uv_levels[ch * 2 + x + y * 2], &res); - it->top_nz_[4 + ch + x] = it->left_nz_[4 + ch + y] = - VP8RecordCoeffs(ctx, &res); - } - } - } - - VP8IteratorBytesToNz(it); -} - -//------------------------------------------------------------------------------ -// Token buffer - -#if !defined(DISABLE_TOKEN_BUFFER) - -static int RecordTokens(VP8EncIterator* const it, const VP8ModeScore* const rd, - VP8TBuffer* const tokens) { - int x, y, ch; - VP8Residual res; - VP8Encoder* const enc = it->enc_; - - VP8IteratorNzToBytes(it); - if (it->mb_->type_ == 1) { // i16x16 - const int ctx = it->top_nz_[8] + it->left_nz_[8]; - VP8InitResidual(0, 1, enc, &res); - VP8SetResidualCoeffs(rd->y_dc_levels, &res); - it->top_nz_[8] = it->left_nz_[8] = - VP8RecordCoeffTokens(ctx, &res, tokens); - VP8InitResidual(1, 0, enc, &res); - } else { - VP8InitResidual(0, 3, enc, &res); - } - - // luma-AC - for (y = 0; y < 4; ++y) { - for (x = 0; x < 4; ++x) { - const int ctx = it->top_nz_[x] + it->left_nz_[y]; - VP8SetResidualCoeffs(rd->y_ac_levels[x + y * 4], &res); - it->top_nz_[x] = it->left_nz_[y] = - VP8RecordCoeffTokens(ctx, &res, tokens); - } - } - - // U/V - VP8InitResidual(0, 2, enc, &res); - for (ch = 0; ch <= 2; ch += 2) { - for (y = 0; y < 2; ++y) { - for (x = 0; x < 2; ++x) { - const int ctx = it->top_nz_[4 + ch + x] + it->left_nz_[4 + ch + y]; - VP8SetResidualCoeffs(rd->uv_levels[ch * 2 + x + y * 2], &res); - it->top_nz_[4 + ch + x] = it->left_nz_[4 + ch + y] = - VP8RecordCoeffTokens(ctx, &res, tokens); - } - } - } - VP8IteratorBytesToNz(it); - return !tokens->error_; -} - -#endif // !DISABLE_TOKEN_BUFFER - -//------------------------------------------------------------------------------ -// ExtraInfo map / Debug function - -#if SEGMENT_VISU -static void SetBlock(uint8_t* p, int value, int size) { - int y; - for (y = 0; y < size; ++y) { - memset(p, value, size); - p += BPS; - } -} -#endif - -static void ResetSSE(VP8Encoder* const enc) { - enc->sse_[0] = 0; - enc->sse_[1] = 0; - enc->sse_[2] = 0; - // Note: enc->sse_[3] is managed by alpha.c - enc->sse_count_ = 0; -} - -static void StoreSSE(const VP8EncIterator* const it) { - VP8Encoder* const enc = it->enc_; - const uint8_t* const in = it->yuv_in_; - const uint8_t* const out = it->yuv_out_; - // Note: not totally accurate at boundary. And doesn't include in-loop filter. - enc->sse_[0] += VP8SSE16x16(in + Y_OFF_ENC, out + Y_OFF_ENC); - enc->sse_[1] += VP8SSE8x8(in + U_OFF_ENC, out + U_OFF_ENC); - enc->sse_[2] += VP8SSE8x8(in + V_OFF_ENC, out + V_OFF_ENC); - enc->sse_count_ += 16 * 16; -} - -static void StoreSideInfo(const VP8EncIterator* const it) { - VP8Encoder* const enc = it->enc_; - const VP8MBInfo* const mb = it->mb_; - WebPPicture* const pic = enc->pic_; - - if (pic->stats != NULL) { - StoreSSE(it); - enc->block_count_[0] += (mb->type_ == 0); - enc->block_count_[1] += (mb->type_ == 1); - enc->block_count_[2] += (mb->skip_ != 0); - } - - if (pic->extra_info != NULL) { - uint8_t* const info = &pic->extra_info[it->x_ + it->y_ * enc->mb_w_]; - switch (pic->extra_info_type) { - case 1: *info = mb->type_; break; - case 2: *info = mb->segment_; break; - case 3: *info = enc->dqm_[mb->segment_].quant_; break; - case 4: *info = (mb->type_ == 1) ? it->preds_[0] : 0xff; break; - case 5: *info = mb->uv_mode_; break; - case 6: { - const int b = (int)((it->luma_bits_ + it->uv_bits_ + 7) >> 3); - *info = (b > 255) ? 255 : b; break; - } - case 7: *info = mb->alpha_; break; - default: *info = 0; break; - } - } -#if SEGMENT_VISU // visualize segments and prediction modes - SetBlock(it->yuv_out_ + Y_OFF_ENC, mb->segment_ * 64, 16); - SetBlock(it->yuv_out_ + U_OFF_ENC, it->preds_[0] * 64, 8); - SetBlock(it->yuv_out_ + V_OFF_ENC, mb->uv_mode_ * 64, 8); -#endif -} - -static double GetPSNR(uint64_t mse, uint64_t size) { - return (mse > 0 && size > 0) ? 10. * log10(255. * 255. * size / mse) : 99; -} - -//------------------------------------------------------------------------------ -// StatLoop(): only collect statistics (number of skips, token usage, ...). -// This is used for deciding optimal probabilities. It also modifies the -// quantizer value if some target (size, PSNR) was specified. - -static void SetLoopParams(VP8Encoder* const enc, float q) { - // Make sure the quality parameter is inside valid bounds - q = Clamp(q, 0.f, 100.f); - - VP8SetSegmentParams(enc, q); // setup segment quantizations and filters - SetSegmentProbas(enc); // compute segment probabilities - - ResetStats(enc); - ResetSSE(enc); -} - -static uint64_t OneStatPass(VP8Encoder* const enc, VP8RDLevel rd_opt, - int nb_mbs, int percent_delta, - PassStats* const s) { - VP8EncIterator it; - uint64_t size = 0; - uint64_t size_p0 = 0; - uint64_t distortion = 0; - const uint64_t pixel_count = nb_mbs * 384; - - VP8IteratorInit(enc, &it); - SetLoopParams(enc, s->q); - do { - VP8ModeScore info; - VP8IteratorImport(&it, NULL); - if (VP8Decimate(&it, &info, rd_opt)) { - // Just record the number of skips and act like skip_proba is not used. - enc->proba_.nb_skip_++; - } - RecordResiduals(&it, &info); - size += info.R + info.H; - size_p0 += info.H; - distortion += info.D; - if (percent_delta && !VP8IteratorProgress(&it, percent_delta)) - return 0; - VP8IteratorSaveBoundary(&it); - } while (VP8IteratorNext(&it) && --nb_mbs > 0); - - size_p0 += enc->segment_hdr_.size_; - if (s->do_size_search) { - size += FinalizeSkipProba(enc); - size += FinalizeTokenProbas(&enc->proba_); - size = ((size + size_p0 + 1024) >> 11) + HEADER_SIZE_ESTIMATE; - s->value = (double)size; - } else { - s->value = GetPSNR(distortion, pixel_count); - } - return size_p0; -} - -static int StatLoop(VP8Encoder* const enc) { - const int method = enc->method_; - const int do_search = enc->do_search_; - const int fast_probe = ((method == 0 || method == 3) && !do_search); - int num_pass_left = enc->config_->pass; - const int task_percent = 20; - const int percent_per_pass = - (task_percent + num_pass_left / 2) / num_pass_left; - const int final_percent = enc->percent_ + task_percent; - const VP8RDLevel rd_opt = - (method >= 3 || do_search) ? RD_OPT_BASIC : RD_OPT_NONE; - int nb_mbs = enc->mb_w_ * enc->mb_h_; - PassStats stats; - - InitPassStats(enc, &stats); - ResetTokenStats(enc); - - // Fast mode: quick analysis pass over few mbs. Better than nothing. - if (fast_probe) { - if (method == 3) { // we need more stats for method 3 to be reliable. - nb_mbs = (nb_mbs > 200) ? nb_mbs >> 1 : 100; - } else { - nb_mbs = (nb_mbs > 200) ? nb_mbs >> 2 : 50; - } - } - - while (num_pass_left-- > 0) { - const int is_last_pass = (fabs(stats.dq) <= DQ_LIMIT) || - (num_pass_left == 0) || - (enc->max_i4_header_bits_ == 0); - const uint64_t size_p0 = - OneStatPass(enc, rd_opt, nb_mbs, percent_per_pass, &stats); - if (size_p0 == 0) return 0; -#if (DEBUG_SEARCH > 0) - printf("#%d value:%.1lf -> %.1lf q:%.2f -> %.2f\n", - num_pass_left, stats.last_value, stats.value, stats.last_q, stats.q); -#endif - if (enc->max_i4_header_bits_ > 0 && size_p0 > PARTITION0_SIZE_LIMIT) { - ++num_pass_left; - enc->max_i4_header_bits_ >>= 1; // strengthen header bit limitation... - continue; // ...and start over - } - if (is_last_pass) { - break; - } - // If no target size: just do several pass without changing 'q' - if (do_search) { - ComputeNextQ(&stats); - if (fabs(stats.dq) <= DQ_LIMIT) break; - } - } - if (!do_search || !stats.do_size_search) { - // Need to finalize probas now, since it wasn't done during the search. - FinalizeSkipProba(enc); - FinalizeTokenProbas(&enc->proba_); - } - VP8CalculateLevelCosts(&enc->proba_); // finalize costs - return WebPReportProgress(enc->pic_, final_percent, &enc->percent_); -} - -//------------------------------------------------------------------------------ -// Main loops -// - -static const int kAverageBytesPerMB[8] = { 50, 24, 16, 9, 7, 5, 3, 2 }; - -static int PreLoopInitialize(VP8Encoder* const enc) { - int p; - int ok = 1; - const int average_bytes_per_MB = kAverageBytesPerMB[enc->base_quant_ >> 4]; - const int bytes_per_parts = - enc->mb_w_ * enc->mb_h_ * average_bytes_per_MB / enc->num_parts_; - // Initialize the bit-writers - for (p = 0; ok && p < enc->num_parts_; ++p) { - ok = VP8BitWriterInit(enc->parts_ + p, bytes_per_parts); - } - if (!ok) { - VP8EncFreeBitWriters(enc); // malloc error occurred - WebPEncodingSetError(enc->pic_, VP8_ENC_ERROR_OUT_OF_MEMORY); - } - return ok; -} - -static int PostLoopFinalize(VP8EncIterator* const it, int ok) { - VP8Encoder* const enc = it->enc_; - if (ok) { // Finalize the partitions, check for extra errors. - int p; - for (p = 0; p < enc->num_parts_; ++p) { - VP8BitWriterFinish(enc->parts_ + p); - ok &= !enc->parts_[p].error_; - } - } - - if (ok) { // All good. Finish up. - if (enc->pic_->stats != NULL) { // finalize byte counters... - int i, s; - for (i = 0; i <= 2; ++i) { - for (s = 0; s < NUM_MB_SEGMENTS; ++s) { - enc->residual_bytes_[i][s] = (int)((it->bit_count_[s][i] + 7) >> 3); - } - } - } - VP8AdjustFilterStrength(it); // ...and store filter stats. - } else { - // Something bad happened -> need to do some memory cleanup. - VP8EncFreeBitWriters(enc); - } - return ok; -} - -//------------------------------------------------------------------------------ -// VP8EncLoop(): does the final bitstream coding. - -static void ResetAfterSkip(VP8EncIterator* const it) { - if (it->mb_->type_ == 1) { - *it->nz_ = 0; // reset all predictors - it->left_nz_[8] = 0; - } else { - *it->nz_ &= (1 << 24); // preserve the dc_nz bit - } -} - -int VP8EncLoop(VP8Encoder* const enc) { - VP8EncIterator it; - int ok = PreLoopInitialize(enc); - if (!ok) return 0; - - StatLoop(enc); // stats-collection loop - - VP8IteratorInit(enc, &it); - VP8InitFilter(&it); - do { - VP8ModeScore info; - const int dont_use_skip = !enc->proba_.use_skip_proba_; - const VP8RDLevel rd_opt = enc->rd_opt_level_; - - VP8IteratorImport(&it, NULL); - // Warning! order is important: first call VP8Decimate() and - // *then* decide how to code the skip decision if there's one. - if (!VP8Decimate(&it, &info, rd_opt) || dont_use_skip) { - CodeResiduals(it.bw_, &it, &info); - } else { // reset predictors after a skip - ResetAfterSkip(&it); - } - StoreSideInfo(&it); - VP8StoreFilterStats(&it); - VP8IteratorExport(&it); - ok = VP8IteratorProgress(&it, 20); - VP8IteratorSaveBoundary(&it); - } while (ok && VP8IteratorNext(&it)); - - return PostLoopFinalize(&it, ok); -} - -//------------------------------------------------------------------------------ -// Single pass using Token Buffer. - -#if !defined(DISABLE_TOKEN_BUFFER) - -#define MIN_COUNT 96 // minimum number of macroblocks before updating stats - -int VP8EncTokenLoop(VP8Encoder* const enc) { - // Roughly refresh the proba eight times per pass - int max_count = (enc->mb_w_ * enc->mb_h_) >> 3; - int num_pass_left = enc->config_->pass; - const int do_search = enc->do_search_; - VP8EncIterator it; - VP8EncProba* const proba = &enc->proba_; - const VP8RDLevel rd_opt = enc->rd_opt_level_; - const uint64_t pixel_count = enc->mb_w_ * enc->mb_h_ * 384; - PassStats stats; - int ok; - - InitPassStats(enc, &stats); - ok = PreLoopInitialize(enc); - if (!ok) return 0; - - if (max_count < MIN_COUNT) max_count = MIN_COUNT; - - assert(enc->num_parts_ == 1); - assert(enc->use_tokens_); - assert(proba->use_skip_proba_ == 0); - assert(rd_opt >= RD_OPT_BASIC); // otherwise, token-buffer won't be useful - assert(num_pass_left > 0); - - while (ok && num_pass_left-- > 0) { - const int is_last_pass = (fabs(stats.dq) <= DQ_LIMIT) || - (num_pass_left == 0) || - (enc->max_i4_header_bits_ == 0); - uint64_t size_p0 = 0; - uint64_t distortion = 0; - int cnt = max_count; - VP8IteratorInit(enc, &it); - SetLoopParams(enc, stats.q); - if (is_last_pass) { - ResetTokenStats(enc); - VP8InitFilter(&it); // don't collect stats until last pass (too costly) - } - VP8TBufferClear(&enc->tokens_); - do { - VP8ModeScore info; - VP8IteratorImport(&it, NULL); - if (--cnt < 0) { - FinalizeTokenProbas(proba); - VP8CalculateLevelCosts(proba); // refresh cost tables for rd-opt - cnt = max_count; - } - VP8Decimate(&it, &info, rd_opt); - ok = RecordTokens(&it, &info, &enc->tokens_); - if (!ok) { - WebPEncodingSetError(enc->pic_, VP8_ENC_ERROR_OUT_OF_MEMORY); - break; - } - size_p0 += info.H; - distortion += info.D; - if (is_last_pass) { - StoreSideInfo(&it); - VP8StoreFilterStats(&it); - VP8IteratorExport(&it); - ok = VP8IteratorProgress(&it, 20); - } - VP8IteratorSaveBoundary(&it); - } while (ok && VP8IteratorNext(&it)); - if (!ok) break; - - size_p0 += enc->segment_hdr_.size_; - if (stats.do_size_search) { - uint64_t size = FinalizeTokenProbas(&enc->proba_); - size += VP8EstimateTokenSize(&enc->tokens_, - (const uint8_t*)proba->coeffs_); - size = (size + size_p0 + 1024) >> 11; // -> size in bytes - size += HEADER_SIZE_ESTIMATE; - stats.value = (double)size; - } else { // compute and store PSNR - stats.value = GetPSNR(distortion, pixel_count); - } - -#if (DEBUG_SEARCH > 0) - printf("#%2d metric:%.1lf -> %.1lf last_q=%.2lf q=%.2lf dq=%.2lf\n", - num_pass_left, stats.last_value, stats.value, - stats.last_q, stats.q, stats.dq); -#endif - if (enc->max_i4_header_bits_ > 0 && size_p0 > PARTITION0_SIZE_LIMIT) { - ++num_pass_left; - enc->max_i4_header_bits_ >>= 1; // strengthen header bit limitation... - continue; // ...and start over - } - if (is_last_pass) { - break; // done - } - if (do_search) { - ComputeNextQ(&stats); // Adjust q - } - } - if (ok) { - if (!stats.do_size_search) { - FinalizeTokenProbas(&enc->proba_); - } - ok = VP8EmitTokens(&enc->tokens_, enc->parts_ + 0, - (const uint8_t*)proba->coeffs_, 1); - } - ok = ok && WebPReportProgress(enc->pic_, enc->percent_ + 20, &enc->percent_); - return PostLoopFinalize(&it, ok); -} - -#else - -int VP8EncTokenLoop(VP8Encoder* const enc) { - (void)enc; - return 0; // we shouldn't be here. -} - -#endif // DISABLE_TOKEN_BUFFER - -//------------------------------------------------------------------------------ - diff --git a/thirdparty/libwebp/enc/frame_enc.c b/thirdparty/libwebp/enc/frame_enc.c new file mode 100644 index 0000000000..abef523bbf --- /dev/null +++ b/thirdparty/libwebp/enc/frame_enc.c @@ -0,0 +1,854 @@ +// Copyright 2011 Google Inc. All Rights Reserved. +// +// Use of this source code is governed by a BSD-style license +// that can be found in the COPYING file in the root of the source +// tree. An additional intellectual property rights grant can be found +// in the file PATENTS. All contributing project authors may +// be found in the AUTHORS file in the root of the source tree. +// ----------------------------------------------------------------------------- +// +// frame coding and analysis +// +// Author: Skal (pascal.massimino@gmail.com) + +#include +#include + +#include "./cost_enc.h" +#include "./vp8i_enc.h" +#include "../dsp/dsp.h" +#include "../webp/format_constants.h" // RIFF constants + +#define SEGMENT_VISU 0 +#define DEBUG_SEARCH 0 // useful to track search convergence + +//------------------------------------------------------------------------------ +// multi-pass convergence + +#define HEADER_SIZE_ESTIMATE (RIFF_HEADER_SIZE + CHUNK_HEADER_SIZE + \ + VP8_FRAME_HEADER_SIZE) +#define DQ_LIMIT 0.4 // convergence is considered reached if dq < DQ_LIMIT +// we allow 2k of extra head-room in PARTITION0 limit. +#define PARTITION0_SIZE_LIMIT ((VP8_MAX_PARTITION0_SIZE - 2048ULL) << 11) + +typedef struct { // struct for organizing convergence in either size or PSNR + int is_first; + float dq; + float q, last_q; + double value, last_value; // PSNR or size + double target; + int do_size_search; +} PassStats; + +static int InitPassStats(const VP8Encoder* const enc, PassStats* const s) { + const uint64_t target_size = (uint64_t)enc->config_->target_size; + const int do_size_search = (target_size != 0); + const float target_PSNR = enc->config_->target_PSNR; + + s->is_first = 1; + s->dq = 10.f; + s->q = s->last_q = enc->config_->quality; + s->target = do_size_search ? (double)target_size + : (target_PSNR > 0.) ? target_PSNR + : 40.; // default, just in case + s->value = s->last_value = 0.; + s->do_size_search = do_size_search; + return do_size_search; +} + +static float Clamp(float v, float min, float max) { + return (v < min) ? min : (v > max) ? max : v; +} + +static float ComputeNextQ(PassStats* const s) { + float dq; + if (s->is_first) { + dq = (s->value > s->target) ? -s->dq : s->dq; + s->is_first = 0; + } else if (s->value != s->last_value) { + const double slope = (s->target - s->value) / (s->last_value - s->value); + dq = (float)(slope * (s->last_q - s->q)); + } else { + dq = 0.; // we're done?! + } + // Limit variable to avoid large swings. + s->dq = Clamp(dq, -30.f, 30.f); + s->last_q = s->q; + s->last_value = s->value; + s->q = Clamp(s->q + s->dq, 0.f, 100.f); + return s->q; +} + +//------------------------------------------------------------------------------ +// Tables for level coding + +const uint8_t VP8Cat3[] = { 173, 148, 140 }; +const uint8_t VP8Cat4[] = { 176, 155, 140, 135 }; +const uint8_t VP8Cat5[] = { 180, 157, 141, 134, 130 }; +const uint8_t VP8Cat6[] = + { 254, 254, 243, 230, 196, 177, 153, 140, 133, 130, 129 }; + +//------------------------------------------------------------------------------ +// Reset the statistics about: number of skips, token proba, level cost,... + +static void ResetStats(VP8Encoder* const enc) { + VP8EncProba* const proba = &enc->proba_; + VP8CalculateLevelCosts(proba); + proba->nb_skip_ = 0; +} + +//------------------------------------------------------------------------------ +// Skip decision probability + +#define SKIP_PROBA_THRESHOLD 250 // value below which using skip_proba is OK. + +static int CalcSkipProba(uint64_t nb, uint64_t total) { + return (int)(total ? (total - nb) * 255 / total : 255); +} + +// Returns the bit-cost for coding the skip probability. +static int FinalizeSkipProba(VP8Encoder* const enc) { + VP8EncProba* const proba = &enc->proba_; + const int nb_mbs = enc->mb_w_ * enc->mb_h_; + const int nb_events = proba->nb_skip_; + int size; + proba->skip_proba_ = CalcSkipProba(nb_events, nb_mbs); + proba->use_skip_proba_ = (proba->skip_proba_ < SKIP_PROBA_THRESHOLD); + size = 256; // 'use_skip_proba' bit + if (proba->use_skip_proba_) { + size += nb_events * VP8BitCost(1, proba->skip_proba_) + + (nb_mbs - nb_events) * VP8BitCost(0, proba->skip_proba_); + size += 8 * 256; // cost of signaling the skip_proba_ itself. + } + return size; +} + +// Collect statistics and deduce probabilities for next coding pass. +// Return the total bit-cost for coding the probability updates. +static int CalcTokenProba(int nb, int total) { + assert(nb <= total); + return nb ? (255 - nb * 255 / total) : 255; +} + +// Cost of coding 'nb' 1's and 'total-nb' 0's using 'proba' probability. +static int BranchCost(int nb, int total, int proba) { + return nb * VP8BitCost(1, proba) + (total - nb) * VP8BitCost(0, proba); +} + +static void ResetTokenStats(VP8Encoder* const enc) { + VP8EncProba* const proba = &enc->proba_; + memset(proba->stats_, 0, sizeof(proba->stats_)); +} + +static int FinalizeTokenProbas(VP8EncProba* const proba) { + int has_changed = 0; + int size = 0; + int t, b, c, p; + for (t = 0; t < NUM_TYPES; ++t) { + for (b = 0; b < NUM_BANDS; ++b) { + for (c = 0; c < NUM_CTX; ++c) { + for (p = 0; p < NUM_PROBAS; ++p) { + const proba_t stats = proba->stats_[t][b][c][p]; + const int nb = (stats >> 0) & 0xffff; + const int total = (stats >> 16) & 0xffff; + const int update_proba = VP8CoeffsUpdateProba[t][b][c][p]; + const int old_p = VP8CoeffsProba0[t][b][c][p]; + const int new_p = CalcTokenProba(nb, total); + const int old_cost = BranchCost(nb, total, old_p) + + VP8BitCost(0, update_proba); + const int new_cost = BranchCost(nb, total, new_p) + + VP8BitCost(1, update_proba) + + 8 * 256; + const int use_new_p = (old_cost > new_cost); + size += VP8BitCost(use_new_p, update_proba); + if (use_new_p) { // only use proba that seem meaningful enough. + proba->coeffs_[t][b][c][p] = new_p; + has_changed |= (new_p != old_p); + size += 8 * 256; + } else { + proba->coeffs_[t][b][c][p] = old_p; + } + } + } + } + } + proba->dirty_ = has_changed; + return size; +} + +//------------------------------------------------------------------------------ +// Finalize Segment probability based on the coding tree + +static int GetProba(int a, int b) { + const int total = a + b; + return (total == 0) ? 255 // that's the default probability. + : (255 * a + total / 2) / total; // rounded proba +} + +static void ResetSegments(VP8Encoder* const enc) { + int n; + for (n = 0; n < enc->mb_w_ * enc->mb_h_; ++n) { + enc->mb_info_[n].segment_ = 0; + } +} + +static void SetSegmentProbas(VP8Encoder* const enc) { + int p[NUM_MB_SEGMENTS] = { 0 }; + int n; + + for (n = 0; n < enc->mb_w_ * enc->mb_h_; ++n) { + const VP8MBInfo* const mb = &enc->mb_info_[n]; + p[mb->segment_]++; + } + if (enc->pic_->stats != NULL) { + for (n = 0; n < NUM_MB_SEGMENTS; ++n) { + enc->pic_->stats->segment_size[n] = p[n]; + } + } + if (enc->segment_hdr_.num_segments_ > 1) { + uint8_t* const probas = enc->proba_.segments_; + probas[0] = GetProba(p[0] + p[1], p[2] + p[3]); + probas[1] = GetProba(p[0], p[1]); + probas[2] = GetProba(p[2], p[3]); + + enc->segment_hdr_.update_map_ = + (probas[0] != 255) || (probas[1] != 255) || (probas[2] != 255); + if (!enc->segment_hdr_.update_map_) ResetSegments(enc); + enc->segment_hdr_.size_ = + p[0] * (VP8BitCost(0, probas[0]) + VP8BitCost(0, probas[1])) + + p[1] * (VP8BitCost(0, probas[0]) + VP8BitCost(1, probas[1])) + + p[2] * (VP8BitCost(1, probas[0]) + VP8BitCost(0, probas[2])) + + p[3] * (VP8BitCost(1, probas[0]) + VP8BitCost(1, probas[2])); + } else { + enc->segment_hdr_.update_map_ = 0; + enc->segment_hdr_.size_ = 0; + } +} + +//------------------------------------------------------------------------------ +// Coefficient coding + +static int PutCoeffs(VP8BitWriter* const bw, int ctx, const VP8Residual* res) { + int n = res->first; + // should be prob[VP8EncBands[n]], but it's equivalent for n=0 or 1 + const uint8_t* p = res->prob[n][ctx]; + if (!VP8PutBit(bw, res->last >= 0, p[0])) { + return 0; + } + + while (n < 16) { + const int c = res->coeffs[n++]; + const int sign = c < 0; + int v = sign ? -c : c; + if (!VP8PutBit(bw, v != 0, p[1])) { + p = res->prob[VP8EncBands[n]][0]; + continue; + } + if (!VP8PutBit(bw, v > 1, p[2])) { + p = res->prob[VP8EncBands[n]][1]; + } else { + if (!VP8PutBit(bw, v > 4, p[3])) { + if (VP8PutBit(bw, v != 2, p[4])) { + VP8PutBit(bw, v == 4, p[5]); + } + } else if (!VP8PutBit(bw, v > 10, p[6])) { + if (!VP8PutBit(bw, v > 6, p[7])) { + VP8PutBit(bw, v == 6, 159); + } else { + VP8PutBit(bw, v >= 9, 165); + VP8PutBit(bw, !(v & 1), 145); + } + } else { + int mask; + const uint8_t* tab; + if (v < 3 + (8 << 1)) { // VP8Cat3 (3b) + VP8PutBit(bw, 0, p[8]); + VP8PutBit(bw, 0, p[9]); + v -= 3 + (8 << 0); + mask = 1 << 2; + tab = VP8Cat3; + } else if (v < 3 + (8 << 2)) { // VP8Cat4 (4b) + VP8PutBit(bw, 0, p[8]); + VP8PutBit(bw, 1, p[9]); + v -= 3 + (8 << 1); + mask = 1 << 3; + tab = VP8Cat4; + } else if (v < 3 + (8 << 3)) { // VP8Cat5 (5b) + VP8PutBit(bw, 1, p[8]); + VP8PutBit(bw, 0, p[10]); + v -= 3 + (8 << 2); + mask = 1 << 4; + tab = VP8Cat5; + } else { // VP8Cat6 (11b) + VP8PutBit(bw, 1, p[8]); + VP8PutBit(bw, 1, p[10]); + v -= 3 + (8 << 3); + mask = 1 << 10; + tab = VP8Cat6; + } + while (mask) { + VP8PutBit(bw, !!(v & mask), *tab++); + mask >>= 1; + } + } + p = res->prob[VP8EncBands[n]][2]; + } + VP8PutBitUniform(bw, sign); + if (n == 16 || !VP8PutBit(bw, n <= res->last, p[0])) { + return 1; // EOB + } + } + return 1; +} + +static void CodeResiduals(VP8BitWriter* const bw, VP8EncIterator* const it, + const VP8ModeScore* const rd) { + int x, y, ch; + VP8Residual res; + uint64_t pos1, pos2, pos3; + const int i16 = (it->mb_->type_ == 1); + const int segment = it->mb_->segment_; + VP8Encoder* const enc = it->enc_; + + VP8IteratorNzToBytes(it); + + pos1 = VP8BitWriterPos(bw); + if (i16) { + VP8InitResidual(0, 1, enc, &res); + VP8SetResidualCoeffs(rd->y_dc_levels, &res); + it->top_nz_[8] = it->left_nz_[8] = + PutCoeffs(bw, it->top_nz_[8] + it->left_nz_[8], &res); + VP8InitResidual(1, 0, enc, &res); + } else { + VP8InitResidual(0, 3, enc, &res); + } + + // luma-AC + for (y = 0; y < 4; ++y) { + for (x = 0; x < 4; ++x) { + const int ctx = it->top_nz_[x] + it->left_nz_[y]; + VP8SetResidualCoeffs(rd->y_ac_levels[x + y * 4], &res); + it->top_nz_[x] = it->left_nz_[y] = PutCoeffs(bw, ctx, &res); + } + } + pos2 = VP8BitWriterPos(bw); + + // U/V + VP8InitResidual(0, 2, enc, &res); + for (ch = 0; ch <= 2; ch += 2) { + for (y = 0; y < 2; ++y) { + for (x = 0; x < 2; ++x) { + const int ctx = it->top_nz_[4 + ch + x] + it->left_nz_[4 + ch + y]; + VP8SetResidualCoeffs(rd->uv_levels[ch * 2 + x + y * 2], &res); + it->top_nz_[4 + ch + x] = it->left_nz_[4 + ch + y] = + PutCoeffs(bw, ctx, &res); + } + } + } + pos3 = VP8BitWriterPos(bw); + it->luma_bits_ = pos2 - pos1; + it->uv_bits_ = pos3 - pos2; + it->bit_count_[segment][i16] += it->luma_bits_; + it->bit_count_[segment][2] += it->uv_bits_; + VP8IteratorBytesToNz(it); +} + +// Same as CodeResiduals, but doesn't actually write anything. +// Instead, it just records the event distribution. +static void RecordResiduals(VP8EncIterator* const it, + const VP8ModeScore* const rd) { + int x, y, ch; + VP8Residual res; + VP8Encoder* const enc = it->enc_; + + VP8IteratorNzToBytes(it); + + if (it->mb_->type_ == 1) { // i16x16 + VP8InitResidual(0, 1, enc, &res); + VP8SetResidualCoeffs(rd->y_dc_levels, &res); + it->top_nz_[8] = it->left_nz_[8] = + VP8RecordCoeffs(it->top_nz_[8] + it->left_nz_[8], &res); + VP8InitResidual(1, 0, enc, &res); + } else { + VP8InitResidual(0, 3, enc, &res); + } + + // luma-AC + for (y = 0; y < 4; ++y) { + for (x = 0; x < 4; ++x) { + const int ctx = it->top_nz_[x] + it->left_nz_[y]; + VP8SetResidualCoeffs(rd->y_ac_levels[x + y * 4], &res); + it->top_nz_[x] = it->left_nz_[y] = VP8RecordCoeffs(ctx, &res); + } + } + + // U/V + VP8InitResidual(0, 2, enc, &res); + for (ch = 0; ch <= 2; ch += 2) { + for (y = 0; y < 2; ++y) { + for (x = 0; x < 2; ++x) { + const int ctx = it->top_nz_[4 + ch + x] + it->left_nz_[4 + ch + y]; + VP8SetResidualCoeffs(rd->uv_levels[ch * 2 + x + y * 2], &res); + it->top_nz_[4 + ch + x] = it->left_nz_[4 + ch + y] = + VP8RecordCoeffs(ctx, &res); + } + } + } + + VP8IteratorBytesToNz(it); +} + +//------------------------------------------------------------------------------ +// Token buffer + +#if !defined(DISABLE_TOKEN_BUFFER) + +static int RecordTokens(VP8EncIterator* const it, const VP8ModeScore* const rd, + VP8TBuffer* const tokens) { + int x, y, ch; + VP8Residual res; + VP8Encoder* const enc = it->enc_; + + VP8IteratorNzToBytes(it); + if (it->mb_->type_ == 1) { // i16x16 + const int ctx = it->top_nz_[8] + it->left_nz_[8]; + VP8InitResidual(0, 1, enc, &res); + VP8SetResidualCoeffs(rd->y_dc_levels, &res); + it->top_nz_[8] = it->left_nz_[8] = + VP8RecordCoeffTokens(ctx, &res, tokens); + VP8InitResidual(1, 0, enc, &res); + } else { + VP8InitResidual(0, 3, enc, &res); + } + + // luma-AC + for (y = 0; y < 4; ++y) { + for (x = 0; x < 4; ++x) { + const int ctx = it->top_nz_[x] + it->left_nz_[y]; + VP8SetResidualCoeffs(rd->y_ac_levels[x + y * 4], &res); + it->top_nz_[x] = it->left_nz_[y] = + VP8RecordCoeffTokens(ctx, &res, tokens); + } + } + + // U/V + VP8InitResidual(0, 2, enc, &res); + for (ch = 0; ch <= 2; ch += 2) { + for (y = 0; y < 2; ++y) { + for (x = 0; x < 2; ++x) { + const int ctx = it->top_nz_[4 + ch + x] + it->left_nz_[4 + ch + y]; + VP8SetResidualCoeffs(rd->uv_levels[ch * 2 + x + y * 2], &res); + it->top_nz_[4 + ch + x] = it->left_nz_[4 + ch + y] = + VP8RecordCoeffTokens(ctx, &res, tokens); + } + } + } + VP8IteratorBytesToNz(it); + return !tokens->error_; +} + +#endif // !DISABLE_TOKEN_BUFFER + +//------------------------------------------------------------------------------ +// ExtraInfo map / Debug function + +#if SEGMENT_VISU +static void SetBlock(uint8_t* p, int value, int size) { + int y; + for (y = 0; y < size; ++y) { + memset(p, value, size); + p += BPS; + } +} +#endif + +static void ResetSSE(VP8Encoder* const enc) { + enc->sse_[0] = 0; + enc->sse_[1] = 0; + enc->sse_[2] = 0; + // Note: enc->sse_[3] is managed by alpha.c + enc->sse_count_ = 0; +} + +static void StoreSSE(const VP8EncIterator* const it) { + VP8Encoder* const enc = it->enc_; + const uint8_t* const in = it->yuv_in_; + const uint8_t* const out = it->yuv_out_; + // Note: not totally accurate at boundary. And doesn't include in-loop filter. + enc->sse_[0] += VP8SSE16x16(in + Y_OFF_ENC, out + Y_OFF_ENC); + enc->sse_[1] += VP8SSE8x8(in + U_OFF_ENC, out + U_OFF_ENC); + enc->sse_[2] += VP8SSE8x8(in + V_OFF_ENC, out + V_OFF_ENC); + enc->sse_count_ += 16 * 16; +} + +static void StoreSideInfo(const VP8EncIterator* const it) { + VP8Encoder* const enc = it->enc_; + const VP8MBInfo* const mb = it->mb_; + WebPPicture* const pic = enc->pic_; + + if (pic->stats != NULL) { + StoreSSE(it); + enc->block_count_[0] += (mb->type_ == 0); + enc->block_count_[1] += (mb->type_ == 1); + enc->block_count_[2] += (mb->skip_ != 0); + } + + if (pic->extra_info != NULL) { + uint8_t* const info = &pic->extra_info[it->x_ + it->y_ * enc->mb_w_]; + switch (pic->extra_info_type) { + case 1: *info = mb->type_; break; + case 2: *info = mb->segment_; break; + case 3: *info = enc->dqm_[mb->segment_].quant_; break; + case 4: *info = (mb->type_ == 1) ? it->preds_[0] : 0xff; break; + case 5: *info = mb->uv_mode_; break; + case 6: { + const int b = (int)((it->luma_bits_ + it->uv_bits_ + 7) >> 3); + *info = (b > 255) ? 255 : b; break; + } + case 7: *info = mb->alpha_; break; + default: *info = 0; break; + } + } +#if SEGMENT_VISU // visualize segments and prediction modes + SetBlock(it->yuv_out_ + Y_OFF_ENC, mb->segment_ * 64, 16); + SetBlock(it->yuv_out_ + U_OFF_ENC, it->preds_[0] * 64, 8); + SetBlock(it->yuv_out_ + V_OFF_ENC, mb->uv_mode_ * 64, 8); +#endif +} + +static double GetPSNR(uint64_t mse, uint64_t size) { + return (mse > 0 && size > 0) ? 10. * log10(255. * 255. * size / mse) : 99; +} + +//------------------------------------------------------------------------------ +// StatLoop(): only collect statistics (number of skips, token usage, ...). +// This is used for deciding optimal probabilities. It also modifies the +// quantizer value if some target (size, PSNR) was specified. + +static void SetLoopParams(VP8Encoder* const enc, float q) { + // Make sure the quality parameter is inside valid bounds + q = Clamp(q, 0.f, 100.f); + + VP8SetSegmentParams(enc, q); // setup segment quantizations and filters + SetSegmentProbas(enc); // compute segment probabilities + + ResetStats(enc); + ResetSSE(enc); +} + +static uint64_t OneStatPass(VP8Encoder* const enc, VP8RDLevel rd_opt, + int nb_mbs, int percent_delta, + PassStats* const s) { + VP8EncIterator it; + uint64_t size = 0; + uint64_t size_p0 = 0; + uint64_t distortion = 0; + const uint64_t pixel_count = nb_mbs * 384; + + VP8IteratorInit(enc, &it); + SetLoopParams(enc, s->q); + do { + VP8ModeScore info; + VP8IteratorImport(&it, NULL); + if (VP8Decimate(&it, &info, rd_opt)) { + // Just record the number of skips and act like skip_proba is not used. + enc->proba_.nb_skip_++; + } + RecordResiduals(&it, &info); + size += info.R + info.H; + size_p0 += info.H; + distortion += info.D; + if (percent_delta && !VP8IteratorProgress(&it, percent_delta)) { + return 0; + } + VP8IteratorSaveBoundary(&it); + } while (VP8IteratorNext(&it) && --nb_mbs > 0); + + size_p0 += enc->segment_hdr_.size_; + if (s->do_size_search) { + size += FinalizeSkipProba(enc); + size += FinalizeTokenProbas(&enc->proba_); + size = ((size + size_p0 + 1024) >> 11) + HEADER_SIZE_ESTIMATE; + s->value = (double)size; + } else { + s->value = GetPSNR(distortion, pixel_count); + } + return size_p0; +} + +static int StatLoop(VP8Encoder* const enc) { + const int method = enc->method_; + const int do_search = enc->do_search_; + const int fast_probe = ((method == 0 || method == 3) && !do_search); + int num_pass_left = enc->config_->pass; + const int task_percent = 20; + const int percent_per_pass = + (task_percent + num_pass_left / 2) / num_pass_left; + const int final_percent = enc->percent_ + task_percent; + const VP8RDLevel rd_opt = + (method >= 3 || do_search) ? RD_OPT_BASIC : RD_OPT_NONE; + int nb_mbs = enc->mb_w_ * enc->mb_h_; + PassStats stats; + + InitPassStats(enc, &stats); + ResetTokenStats(enc); + + // Fast mode: quick analysis pass over few mbs. Better than nothing. + if (fast_probe) { + if (method == 3) { // we need more stats for method 3 to be reliable. + nb_mbs = (nb_mbs > 200) ? nb_mbs >> 1 : 100; + } else { + nb_mbs = (nb_mbs > 200) ? nb_mbs >> 2 : 50; + } + } + + while (num_pass_left-- > 0) { + const int is_last_pass = (fabs(stats.dq) <= DQ_LIMIT) || + (num_pass_left == 0) || + (enc->max_i4_header_bits_ == 0); + const uint64_t size_p0 = + OneStatPass(enc, rd_opt, nb_mbs, percent_per_pass, &stats); + if (size_p0 == 0) return 0; +#if (DEBUG_SEARCH > 0) + printf("#%d value:%.1lf -> %.1lf q:%.2f -> %.2f\n", + num_pass_left, stats.last_value, stats.value, stats.last_q, stats.q); +#endif + if (enc->max_i4_header_bits_ > 0 && size_p0 > PARTITION0_SIZE_LIMIT) { + ++num_pass_left; + enc->max_i4_header_bits_ >>= 1; // strengthen header bit limitation... + continue; // ...and start over + } + if (is_last_pass) { + break; + } + // If no target size: just do several pass without changing 'q' + if (do_search) { + ComputeNextQ(&stats); + if (fabs(stats.dq) <= DQ_LIMIT) break; + } + } + if (!do_search || !stats.do_size_search) { + // Need to finalize probas now, since it wasn't done during the search. + FinalizeSkipProba(enc); + FinalizeTokenProbas(&enc->proba_); + } + VP8CalculateLevelCosts(&enc->proba_); // finalize costs + return WebPReportProgress(enc->pic_, final_percent, &enc->percent_); +} + +//------------------------------------------------------------------------------ +// Main loops +// + +static const int kAverageBytesPerMB[8] = { 50, 24, 16, 9, 7, 5, 3, 2 }; + +static int PreLoopInitialize(VP8Encoder* const enc) { + int p; + int ok = 1; + const int average_bytes_per_MB = kAverageBytesPerMB[enc->base_quant_ >> 4]; + const int bytes_per_parts = + enc->mb_w_ * enc->mb_h_ * average_bytes_per_MB / enc->num_parts_; + // Initialize the bit-writers + for (p = 0; ok && p < enc->num_parts_; ++p) { + ok = VP8BitWriterInit(enc->parts_ + p, bytes_per_parts); + } + if (!ok) { + VP8EncFreeBitWriters(enc); // malloc error occurred + WebPEncodingSetError(enc->pic_, VP8_ENC_ERROR_OUT_OF_MEMORY); + } + return ok; +} + +static int PostLoopFinalize(VP8EncIterator* const it, int ok) { + VP8Encoder* const enc = it->enc_; + if (ok) { // Finalize the partitions, check for extra errors. + int p; + for (p = 0; p < enc->num_parts_; ++p) { + VP8BitWriterFinish(enc->parts_ + p); + ok &= !enc->parts_[p].error_; + } + } + + if (ok) { // All good. Finish up. + if (enc->pic_->stats != NULL) { // finalize byte counters... + int i, s; + for (i = 0; i <= 2; ++i) { + for (s = 0; s < NUM_MB_SEGMENTS; ++s) { + enc->residual_bytes_[i][s] = (int)((it->bit_count_[s][i] + 7) >> 3); + } + } + } + VP8AdjustFilterStrength(it); // ...and store filter stats. + } else { + // Something bad happened -> need to do some memory cleanup. + VP8EncFreeBitWriters(enc); + } + return ok; +} + +//------------------------------------------------------------------------------ +// VP8EncLoop(): does the final bitstream coding. + +static void ResetAfterSkip(VP8EncIterator* const it) { + if (it->mb_->type_ == 1) { + *it->nz_ = 0; // reset all predictors + it->left_nz_[8] = 0; + } else { + *it->nz_ &= (1 << 24); // preserve the dc_nz bit + } +} + +int VP8EncLoop(VP8Encoder* const enc) { + VP8EncIterator it; + int ok = PreLoopInitialize(enc); + if (!ok) return 0; + + StatLoop(enc); // stats-collection loop + + VP8IteratorInit(enc, &it); + VP8InitFilter(&it); + do { + VP8ModeScore info; + const int dont_use_skip = !enc->proba_.use_skip_proba_; + const VP8RDLevel rd_opt = enc->rd_opt_level_; + + VP8IteratorImport(&it, NULL); + // Warning! order is important: first call VP8Decimate() and + // *then* decide how to code the skip decision if there's one. + if (!VP8Decimate(&it, &info, rd_opt) || dont_use_skip) { + CodeResiduals(it.bw_, &it, &info); + } else { // reset predictors after a skip + ResetAfterSkip(&it); + } + StoreSideInfo(&it); + VP8StoreFilterStats(&it); + VP8IteratorExport(&it); + ok = VP8IteratorProgress(&it, 20); + VP8IteratorSaveBoundary(&it); + } while (ok && VP8IteratorNext(&it)); + + return PostLoopFinalize(&it, ok); +} + +//------------------------------------------------------------------------------ +// Single pass using Token Buffer. + +#if !defined(DISABLE_TOKEN_BUFFER) + +#define MIN_COUNT 96 // minimum number of macroblocks before updating stats + +int VP8EncTokenLoop(VP8Encoder* const enc) { + // Roughly refresh the proba eight times per pass + int max_count = (enc->mb_w_ * enc->mb_h_) >> 3; + int num_pass_left = enc->config_->pass; + const int do_search = enc->do_search_; + VP8EncIterator it; + VP8EncProba* const proba = &enc->proba_; + const VP8RDLevel rd_opt = enc->rd_opt_level_; + const uint64_t pixel_count = enc->mb_w_ * enc->mb_h_ * 384; + PassStats stats; + int ok; + + InitPassStats(enc, &stats); + ok = PreLoopInitialize(enc); + if (!ok) return 0; + + if (max_count < MIN_COUNT) max_count = MIN_COUNT; + + assert(enc->num_parts_ == 1); + assert(enc->use_tokens_); + assert(proba->use_skip_proba_ == 0); + assert(rd_opt >= RD_OPT_BASIC); // otherwise, token-buffer won't be useful + assert(num_pass_left > 0); + + while (ok && num_pass_left-- > 0) { + const int is_last_pass = (fabs(stats.dq) <= DQ_LIMIT) || + (num_pass_left == 0) || + (enc->max_i4_header_bits_ == 0); + uint64_t size_p0 = 0; + uint64_t distortion = 0; + int cnt = max_count; + VP8IteratorInit(enc, &it); + SetLoopParams(enc, stats.q); + if (is_last_pass) { + ResetTokenStats(enc); + VP8InitFilter(&it); // don't collect stats until last pass (too costly) + } + VP8TBufferClear(&enc->tokens_); + do { + VP8ModeScore info; + VP8IteratorImport(&it, NULL); + if (--cnt < 0) { + FinalizeTokenProbas(proba); + VP8CalculateLevelCosts(proba); // refresh cost tables for rd-opt + cnt = max_count; + } + VP8Decimate(&it, &info, rd_opt); + ok = RecordTokens(&it, &info, &enc->tokens_); + if (!ok) { + WebPEncodingSetError(enc->pic_, VP8_ENC_ERROR_OUT_OF_MEMORY); + break; + } + size_p0 += info.H; + distortion += info.D; + if (is_last_pass) { + StoreSideInfo(&it); + VP8StoreFilterStats(&it); + VP8IteratorExport(&it); + ok = VP8IteratorProgress(&it, 20); + } + VP8IteratorSaveBoundary(&it); + } while (ok && VP8IteratorNext(&it)); + if (!ok) break; + + size_p0 += enc->segment_hdr_.size_; + if (stats.do_size_search) { + uint64_t size = FinalizeTokenProbas(&enc->proba_); + size += VP8EstimateTokenSize(&enc->tokens_, + (const uint8_t*)proba->coeffs_); + size = (size + size_p0 + 1024) >> 11; // -> size in bytes + size += HEADER_SIZE_ESTIMATE; + stats.value = (double)size; + } else { // compute and store PSNR + stats.value = GetPSNR(distortion, pixel_count); + } + +#if (DEBUG_SEARCH > 0) + printf("#%2d metric:%.1lf -> %.1lf last_q=%.2lf q=%.2lf dq=%.2lf\n", + num_pass_left, stats.last_value, stats.value, + stats.last_q, stats.q, stats.dq); +#endif + if (enc->max_i4_header_bits_ > 0 && size_p0 > PARTITION0_SIZE_LIMIT) { + ++num_pass_left; + enc->max_i4_header_bits_ >>= 1; // strengthen header bit limitation... + continue; // ...and start over + } + if (is_last_pass) { + break; // done + } + if (do_search) { + ComputeNextQ(&stats); // Adjust q + } + } + if (ok) { + if (!stats.do_size_search) { + FinalizeTokenProbas(&enc->proba_); + } + ok = VP8EmitTokens(&enc->tokens_, enc->parts_ + 0, + (const uint8_t*)proba->coeffs_, 1); + } + ok = ok && WebPReportProgress(enc->pic_, enc->percent_ + 20, &enc->percent_); + return PostLoopFinalize(&it, ok); +} + +#else + +int VP8EncTokenLoop(VP8Encoder* const enc) { + (void)enc; + return 0; // we shouldn't be here. +} + +#endif // DISABLE_TOKEN_BUFFER + +//------------------------------------------------------------------------------ + diff --git a/thirdparty/libwebp/enc/histogram.c b/thirdparty/libwebp/enc/histogram.c deleted file mode 100644 index 36b7f22625..0000000000 --- a/thirdparty/libwebp/enc/histogram.c +++ /dev/null @@ -1,938 +0,0 @@ -// Copyright 2012 Google Inc. All Rights Reserved. -// -// Use of this source code is governed by a BSD-style license -// that can be found in the COPYING file in the root of the source -// tree. An additional intellectual property rights grant can be found -// in the file PATENTS. All contributing project authors may -// be found in the AUTHORS file in the root of the source tree. -// ----------------------------------------------------------------------------- -// -// Author: Jyrki Alakuijala (jyrki@google.com) -// -#ifdef HAVE_CONFIG_H -#include "../webp/config.h" -#endif - -#include - -#include "./backward_references.h" -#include "./histogram.h" -#include "../dsp/lossless.h" -#include "../utils/utils.h" - -#define MAX_COST 1.e38 - -// Number of partitions for the three dominant (literal, red and blue) symbol -// costs. -#define NUM_PARTITIONS 4 -// The size of the bin-hash corresponding to the three dominant costs. -#define BIN_SIZE (NUM_PARTITIONS * NUM_PARTITIONS * NUM_PARTITIONS) -// Maximum number of histograms allowed in greedy combining algorithm. -#define MAX_HISTO_GREEDY 100 - -static void HistogramClear(VP8LHistogram* const p) { - uint32_t* const literal = p->literal_; - const int cache_bits = p->palette_code_bits_; - const int histo_size = VP8LGetHistogramSize(cache_bits); - memset(p, 0, histo_size); - p->palette_code_bits_ = cache_bits; - p->literal_ = literal; -} - -// Swap two histogram pointers. -static void HistogramSwap(VP8LHistogram** const A, VP8LHistogram** const B) { - VP8LHistogram* const tmp = *A; - *A = *B; - *B = tmp; -} - -static void HistogramCopy(const VP8LHistogram* const src, - VP8LHistogram* const dst) { - uint32_t* const dst_literal = dst->literal_; - const int dst_cache_bits = dst->palette_code_bits_; - const int histo_size = VP8LGetHistogramSize(dst_cache_bits); - assert(src->palette_code_bits_ == dst_cache_bits); - memcpy(dst, src, histo_size); - dst->literal_ = dst_literal; -} - -int VP8LGetHistogramSize(int cache_bits) { - const int literal_size = VP8LHistogramNumCodes(cache_bits); - const size_t total_size = sizeof(VP8LHistogram) + sizeof(int) * literal_size; - assert(total_size <= (size_t)0x7fffffff); - return (int)total_size; -} - -void VP8LFreeHistogram(VP8LHistogram* const histo) { - WebPSafeFree(histo); -} - -void VP8LFreeHistogramSet(VP8LHistogramSet* const histo) { - WebPSafeFree(histo); -} - -void VP8LHistogramStoreRefs(const VP8LBackwardRefs* const refs, - VP8LHistogram* const histo) { - VP8LRefsCursor c = VP8LRefsCursorInit(refs); - while (VP8LRefsCursorOk(&c)) { - VP8LHistogramAddSinglePixOrCopy(histo, c.cur_pos); - VP8LRefsCursorNext(&c); - } -} - -void VP8LHistogramCreate(VP8LHistogram* const p, - const VP8LBackwardRefs* const refs, - int palette_code_bits) { - if (palette_code_bits >= 0) { - p->palette_code_bits_ = palette_code_bits; - } - HistogramClear(p); - VP8LHistogramStoreRefs(refs, p); -} - -void VP8LHistogramInit(VP8LHistogram* const p, int palette_code_bits) { - p->palette_code_bits_ = palette_code_bits; - HistogramClear(p); -} - -VP8LHistogram* VP8LAllocateHistogram(int cache_bits) { - VP8LHistogram* histo = NULL; - const int total_size = VP8LGetHistogramSize(cache_bits); - uint8_t* const memory = (uint8_t*)WebPSafeMalloc(total_size, sizeof(*memory)); - if (memory == NULL) return NULL; - histo = (VP8LHistogram*)memory; - // literal_ won't necessary be aligned. - histo->literal_ = (uint32_t*)(memory + sizeof(VP8LHistogram)); - VP8LHistogramInit(histo, cache_bits); - return histo; -} - -VP8LHistogramSet* VP8LAllocateHistogramSet(int size, int cache_bits) { - int i; - VP8LHistogramSet* set; - const int histo_size = VP8LGetHistogramSize(cache_bits); - const size_t total_size = - sizeof(*set) + size * (sizeof(*set->histograms) + - histo_size + WEBP_ALIGN_CST); - uint8_t* memory = (uint8_t*)WebPSafeMalloc(total_size, sizeof(*memory)); - if (memory == NULL) return NULL; - - set = (VP8LHistogramSet*)memory; - memory += sizeof(*set); - set->histograms = (VP8LHistogram**)memory; - memory += size * sizeof(*set->histograms); - set->max_size = size; - set->size = size; - for (i = 0; i < size; ++i) { - memory = (uint8_t*)WEBP_ALIGN(memory); - set->histograms[i] = (VP8LHistogram*)memory; - // literal_ won't necessary be aligned. - set->histograms[i]->literal_ = (uint32_t*)(memory + sizeof(VP8LHistogram)); - VP8LHistogramInit(set->histograms[i], cache_bits); - memory += histo_size; - } - return set; -} - -// ----------------------------------------------------------------------------- - -void VP8LHistogramAddSinglePixOrCopy(VP8LHistogram* const histo, - const PixOrCopy* const v) { - if (PixOrCopyIsLiteral(v)) { - ++histo->alpha_[PixOrCopyLiteral(v, 3)]; - ++histo->red_[PixOrCopyLiteral(v, 2)]; - ++histo->literal_[PixOrCopyLiteral(v, 1)]; - ++histo->blue_[PixOrCopyLiteral(v, 0)]; - } else if (PixOrCopyIsCacheIdx(v)) { - const int literal_ix = - NUM_LITERAL_CODES + NUM_LENGTH_CODES + PixOrCopyCacheIdx(v); - ++histo->literal_[literal_ix]; - } else { - int code, extra_bits; - VP8LPrefixEncodeBits(PixOrCopyLength(v), &code, &extra_bits); - ++histo->literal_[NUM_LITERAL_CODES + code]; - VP8LPrefixEncodeBits(PixOrCopyDistance(v), &code, &extra_bits); - ++histo->distance_[code]; - } -} - -// ----------------------------------------------------------------------------- -// Entropy-related functions. - -static WEBP_INLINE double BitsEntropyRefine(const VP8LBitEntropy* entropy) { - double mix; - if (entropy->nonzeros < 5) { - if (entropy->nonzeros <= 1) { - return 0; - } - // Two symbols, they will be 0 and 1 in a Huffman code. - // Let's mix in a bit of entropy to favor good clustering when - // distributions of these are combined. - if (entropy->nonzeros == 2) { - return 0.99 * entropy->sum + 0.01 * entropy->entropy; - } - // No matter what the entropy says, we cannot be better than min_limit - // with Huffman coding. I am mixing a bit of entropy into the - // min_limit since it produces much better (~0.5 %) compression results - // perhaps because of better entropy clustering. - if (entropy->nonzeros == 3) { - mix = 0.95; - } else { - mix = 0.7; // nonzeros == 4. - } - } else { - mix = 0.627; - } - - { - double min_limit = 2 * entropy->sum - entropy->max_val; - min_limit = mix * min_limit + (1.0 - mix) * entropy->entropy; - return (entropy->entropy < min_limit) ? min_limit : entropy->entropy; - } -} - -double VP8LBitsEntropy(const uint32_t* const array, int n, - uint32_t* const trivial_symbol) { - VP8LBitEntropy entropy; - VP8LBitsEntropyUnrefined(array, n, &entropy); - if (trivial_symbol != NULL) { - *trivial_symbol = - (entropy.nonzeros == 1) ? entropy.nonzero_code : VP8L_NON_TRIVIAL_SYM; - } - - return BitsEntropyRefine(&entropy); -} - -static double InitialHuffmanCost(void) { - // Small bias because Huffman code length is typically not stored in - // full length. - static const int kHuffmanCodeOfHuffmanCodeSize = CODE_LENGTH_CODES * 3; - static const double kSmallBias = 9.1; - return kHuffmanCodeOfHuffmanCodeSize - kSmallBias; -} - -// Finalize the Huffman cost based on streak numbers and length type (<3 or >=3) -static double FinalHuffmanCost(const VP8LStreaks* const stats) { - double retval = InitialHuffmanCost(); - retval += stats->counts[0] * 1.5625 + 0.234375 * stats->streaks[0][1]; - retval += stats->counts[1] * 2.578125 + 0.703125 * stats->streaks[1][1]; - retval += 1.796875 * stats->streaks[0][0]; - retval += 3.28125 * stats->streaks[1][0]; - return retval; -} - -// Get the symbol entropy for the distribution 'population'. -// Set 'trivial_sym', if there's only one symbol present in the distribution. -static double PopulationCost(const uint32_t* const population, int length, - uint32_t* const trivial_sym) { - VP8LBitEntropy bit_entropy; - VP8LStreaks stats; - VP8LGetEntropyUnrefined(population, length, &bit_entropy, &stats); - if (trivial_sym != NULL) { - *trivial_sym = (bit_entropy.nonzeros == 1) ? bit_entropy.nonzero_code - : VP8L_NON_TRIVIAL_SYM; - } - - return BitsEntropyRefine(&bit_entropy) + FinalHuffmanCost(&stats); -} - -static WEBP_INLINE double GetCombinedEntropy(const uint32_t* const X, - const uint32_t* const Y, - int length) { - VP8LBitEntropy bit_entropy; - VP8LStreaks stats; - VP8LGetCombinedEntropyUnrefined(X, Y, length, &bit_entropy, &stats); - - return BitsEntropyRefine(&bit_entropy) + FinalHuffmanCost(&stats); -} - -// Estimates the Entropy + Huffman + other block overhead size cost. -double VP8LHistogramEstimateBits(const VP8LHistogram* const p) { - return - PopulationCost( - p->literal_, VP8LHistogramNumCodes(p->palette_code_bits_), NULL) - + PopulationCost(p->red_, NUM_LITERAL_CODES, NULL) - + PopulationCost(p->blue_, NUM_LITERAL_CODES, NULL) - + PopulationCost(p->alpha_, NUM_LITERAL_CODES, NULL) - + PopulationCost(p->distance_, NUM_DISTANCE_CODES, NULL) - + VP8LExtraCost(p->literal_ + NUM_LITERAL_CODES, NUM_LENGTH_CODES) - + VP8LExtraCost(p->distance_, NUM_DISTANCE_CODES); -} - -// ----------------------------------------------------------------------------- -// Various histogram combine/cost-eval functions - -static int GetCombinedHistogramEntropy(const VP8LHistogram* const a, - const VP8LHistogram* const b, - double cost_threshold, - double* cost) { - const int palette_code_bits = a->palette_code_bits_; - assert(a->palette_code_bits_ == b->palette_code_bits_); - *cost += GetCombinedEntropy(a->literal_, b->literal_, - VP8LHistogramNumCodes(palette_code_bits)); - *cost += VP8LExtraCostCombined(a->literal_ + NUM_LITERAL_CODES, - b->literal_ + NUM_LITERAL_CODES, - NUM_LENGTH_CODES); - if (*cost > cost_threshold) return 0; - - *cost += GetCombinedEntropy(a->red_, b->red_, NUM_LITERAL_CODES); - if (*cost > cost_threshold) return 0; - - *cost += GetCombinedEntropy(a->blue_, b->blue_, NUM_LITERAL_CODES); - if (*cost > cost_threshold) return 0; - - *cost += GetCombinedEntropy(a->alpha_, b->alpha_, NUM_LITERAL_CODES); - if (*cost > cost_threshold) return 0; - - *cost += GetCombinedEntropy(a->distance_, b->distance_, NUM_DISTANCE_CODES); - *cost += - VP8LExtraCostCombined(a->distance_, b->distance_, NUM_DISTANCE_CODES); - if (*cost > cost_threshold) return 0; - - return 1; -} - -// Performs out = a + b, computing the cost C(a+b) - C(a) - C(b) while comparing -// to the threshold value 'cost_threshold'. The score returned is -// Score = C(a+b) - C(a) - C(b), where C(a) + C(b) is known and fixed. -// Since the previous score passed is 'cost_threshold', we only need to compare -// the partial cost against 'cost_threshold + C(a) + C(b)' to possibly bail-out -// early. -static double HistogramAddEval(const VP8LHistogram* const a, - const VP8LHistogram* const b, - VP8LHistogram* const out, - double cost_threshold) { - double cost = 0; - const double sum_cost = a->bit_cost_ + b->bit_cost_; - cost_threshold += sum_cost; - - if (GetCombinedHistogramEntropy(a, b, cost_threshold, &cost)) { - VP8LHistogramAdd(a, b, out); - out->bit_cost_ = cost; - out->palette_code_bits_ = a->palette_code_bits_; - out->trivial_symbol_ = (a->trivial_symbol_ == b->trivial_symbol_) ? - a->trivial_symbol_ : VP8L_NON_TRIVIAL_SYM; - } - - return cost - sum_cost; -} - -// Same as HistogramAddEval(), except that the resulting histogram -// is not stored. Only the cost C(a+b) - C(a) is evaluated. We omit -// the term C(b) which is constant over all the evaluations. -static double HistogramAddThresh(const VP8LHistogram* const a, - const VP8LHistogram* const b, - double cost_threshold) { - double cost = -a->bit_cost_; - GetCombinedHistogramEntropy(a, b, cost_threshold, &cost); - return cost; -} - -// ----------------------------------------------------------------------------- - -// The structure to keep track of cost range for the three dominant entropy -// symbols. -// TODO(skal): Evaluate if float can be used here instead of double for -// representing the entropy costs. -typedef struct { - double literal_max_; - double literal_min_; - double red_max_; - double red_min_; - double blue_max_; - double blue_min_; -} DominantCostRange; - -static void DominantCostRangeInit(DominantCostRange* const c) { - c->literal_max_ = 0.; - c->literal_min_ = MAX_COST; - c->red_max_ = 0.; - c->red_min_ = MAX_COST; - c->blue_max_ = 0.; - c->blue_min_ = MAX_COST; -} - -static void UpdateDominantCostRange( - const VP8LHistogram* const h, DominantCostRange* const c) { - if (c->literal_max_ < h->literal_cost_) c->literal_max_ = h->literal_cost_; - if (c->literal_min_ > h->literal_cost_) c->literal_min_ = h->literal_cost_; - if (c->red_max_ < h->red_cost_) c->red_max_ = h->red_cost_; - if (c->red_min_ > h->red_cost_) c->red_min_ = h->red_cost_; - if (c->blue_max_ < h->blue_cost_) c->blue_max_ = h->blue_cost_; - if (c->blue_min_ > h->blue_cost_) c->blue_min_ = h->blue_cost_; -} - -static void UpdateHistogramCost(VP8LHistogram* const h) { - uint32_t alpha_sym, red_sym, blue_sym; - const double alpha_cost = - PopulationCost(h->alpha_, NUM_LITERAL_CODES, &alpha_sym); - const double distance_cost = - PopulationCost(h->distance_, NUM_DISTANCE_CODES, NULL) + - VP8LExtraCost(h->distance_, NUM_DISTANCE_CODES); - const int num_codes = VP8LHistogramNumCodes(h->palette_code_bits_); - h->literal_cost_ = PopulationCost(h->literal_, num_codes, NULL) + - VP8LExtraCost(h->literal_ + NUM_LITERAL_CODES, - NUM_LENGTH_CODES); - h->red_cost_ = PopulationCost(h->red_, NUM_LITERAL_CODES, &red_sym); - h->blue_cost_ = PopulationCost(h->blue_, NUM_LITERAL_CODES, &blue_sym); - h->bit_cost_ = h->literal_cost_ + h->red_cost_ + h->blue_cost_ + - alpha_cost + distance_cost; - if ((alpha_sym | red_sym | blue_sym) == VP8L_NON_TRIVIAL_SYM) { - h->trivial_symbol_ = VP8L_NON_TRIVIAL_SYM; - } else { - h->trivial_symbol_ = - ((uint32_t)alpha_sym << 24) | (red_sym << 16) | (blue_sym << 0); - } -} - -static int GetBinIdForEntropy(double min, double max, double val) { - const double range = max - min; - if (range > 0.) { - const double delta = val - min; - return (int)((NUM_PARTITIONS - 1e-6) * delta / range); - } else { - return 0; - } -} - -static int GetHistoBinIndex(const VP8LHistogram* const h, - const DominantCostRange* const c, int low_effort) { - int bin_id = GetBinIdForEntropy(c->literal_min_, c->literal_max_, - h->literal_cost_); - assert(bin_id < NUM_PARTITIONS); - if (!low_effort) { - bin_id = bin_id * NUM_PARTITIONS - + GetBinIdForEntropy(c->red_min_, c->red_max_, h->red_cost_); - bin_id = bin_id * NUM_PARTITIONS - + GetBinIdForEntropy(c->blue_min_, c->blue_max_, h->blue_cost_); - assert(bin_id < BIN_SIZE); - } - return bin_id; -} - -// Construct the histograms from backward references. -static void HistogramBuild( - int xsize, int histo_bits, const VP8LBackwardRefs* const backward_refs, - VP8LHistogramSet* const image_histo) { - int x = 0, y = 0; - const int histo_xsize = VP8LSubSampleSize(xsize, histo_bits); - VP8LHistogram** const histograms = image_histo->histograms; - VP8LRefsCursor c = VP8LRefsCursorInit(backward_refs); - assert(histo_bits > 0); - while (VP8LRefsCursorOk(&c)) { - const PixOrCopy* const v = c.cur_pos; - const int ix = (y >> histo_bits) * histo_xsize + (x >> histo_bits); - VP8LHistogramAddSinglePixOrCopy(histograms[ix], v); - x += PixOrCopyLength(v); - while (x >= xsize) { - x -= xsize; - ++y; - } - VP8LRefsCursorNext(&c); - } -} - -// Copies the histograms and computes its bit_cost. -static void HistogramCopyAndAnalyze( - VP8LHistogramSet* const orig_histo, VP8LHistogramSet* const image_histo) { - int i; - const int histo_size = orig_histo->size; - VP8LHistogram** const orig_histograms = orig_histo->histograms; - VP8LHistogram** const histograms = image_histo->histograms; - for (i = 0; i < histo_size; ++i) { - VP8LHistogram* const histo = orig_histograms[i]; - UpdateHistogramCost(histo); - // Copy histograms from orig_histo[] to image_histo[]. - HistogramCopy(histo, histograms[i]); - } -} - -// Partition histograms to different entropy bins for three dominant (literal, -// red and blue) symbol costs and compute the histogram aggregate bit_cost. -static void HistogramAnalyzeEntropyBin(VP8LHistogramSet* const image_histo, - int16_t* const bin_map, int low_effort) { - int i; - VP8LHistogram** const histograms = image_histo->histograms; - const int histo_size = image_histo->size; - const int bin_depth = histo_size + 1; - DominantCostRange cost_range; - DominantCostRangeInit(&cost_range); - - // Analyze the dominant (literal, red and blue) entropy costs. - for (i = 0; i < histo_size; ++i) { - VP8LHistogram* const histo = histograms[i]; - UpdateDominantCostRange(histo, &cost_range); - } - - // bin-hash histograms on three of the dominant (literal, red and blue) - // symbol costs. - for (i = 0; i < histo_size; ++i) { - const VP8LHistogram* const histo = histograms[i]; - const int bin_id = GetHistoBinIndex(histo, &cost_range, low_effort); - const int bin_offset = bin_id * bin_depth; - // bin_map[n][0] for every bin 'n' maintains the counter for the number of - // histograms in that bin. - // Get and increment the num_histos in that bin. - const int num_histos = ++bin_map[bin_offset]; - assert(bin_offset + num_histos < bin_depth * BIN_SIZE); - // Add histogram i'th index at num_histos (last) position in the bin_map. - bin_map[bin_offset + num_histos] = i; - } -} - -// Compact the histogram set by removing unused entries. -static void HistogramCompactBins(VP8LHistogramSet* const image_histo) { - VP8LHistogram** const histograms = image_histo->histograms; - int i, j; - - for (i = 0, j = 0; i < image_histo->size; ++i) { - if (histograms[i] != NULL && histograms[i]->bit_cost_ != 0.) { - if (j < i) { - histograms[j] = histograms[i]; - histograms[i] = NULL; - } - ++j; - } - } - image_histo->size = j; -} - -static VP8LHistogram* HistogramCombineEntropyBin( - VP8LHistogramSet* const image_histo, - VP8LHistogram* cur_combo, - int16_t* const bin_map, int bin_depth, int num_bins, - double combine_cost_factor, int low_effort) { - int bin_id; - VP8LHistogram** const histograms = image_histo->histograms; - - for (bin_id = 0; bin_id < num_bins; ++bin_id) { - const int bin_offset = bin_id * bin_depth; - const int num_histos = bin_map[bin_offset]; - const int idx1 = bin_map[bin_offset + 1]; - int num_combine_failures = 0; - int n; - for (n = 2; n <= num_histos; ++n) { - const int idx2 = bin_map[bin_offset + n]; - if (low_effort) { - // Merge all histograms with the same bin index, irrespective of cost of - // the merged histograms. - VP8LHistogramAdd(histograms[idx1], histograms[idx2], histograms[idx1]); - histograms[idx2]->bit_cost_ = 0.; - } else { - const double bit_cost_idx2 = histograms[idx2]->bit_cost_; - if (bit_cost_idx2 > 0.) { - const double bit_cost_thresh = -bit_cost_idx2 * combine_cost_factor; - const double curr_cost_diff = - HistogramAddEval(histograms[idx1], histograms[idx2], - cur_combo, bit_cost_thresh); - if (curr_cost_diff < bit_cost_thresh) { - // Try to merge two histograms only if the combo is a trivial one or - // the two candidate histograms are already non-trivial. - // For some images, 'try_combine' turns out to be false for a lot of - // histogram pairs. In that case, we fallback to combining - // histograms as usual to avoid increasing the header size. - const int try_combine = - (cur_combo->trivial_symbol_ != VP8L_NON_TRIVIAL_SYM) || - ((histograms[idx1]->trivial_symbol_ == VP8L_NON_TRIVIAL_SYM) && - (histograms[idx2]->trivial_symbol_ == VP8L_NON_TRIVIAL_SYM)); - const int max_combine_failures = 32; - if (try_combine || (num_combine_failures >= max_combine_failures)) { - HistogramSwap(&cur_combo, &histograms[idx1]); - histograms[idx2]->bit_cost_ = 0.; - } else { - ++num_combine_failures; - } - } - } - } - } - if (low_effort) { - // Update the bit_cost for the merged histograms (per bin index). - UpdateHistogramCost(histograms[idx1]); - } - } - HistogramCompactBins(image_histo); - return cur_combo; -} - -static uint32_t MyRand(uint32_t *seed) { - *seed *= 16807U; - if (*seed == 0) { - *seed = 1; - } - return *seed; -} - -// ----------------------------------------------------------------------------- -// Histogram pairs priority queue - -// Pair of histograms. Negative idx1 value means that pair is out-of-date. -typedef struct { - int idx1; - int idx2; - double cost_diff; - double cost_combo; -} HistogramPair; - -typedef struct { - HistogramPair* queue; - int size; - int max_size; -} HistoQueue; - -static int HistoQueueInit(HistoQueue* const histo_queue, const int max_index) { - histo_queue->size = 0; - // max_index^2 for the queue size is safe. If you look at - // HistogramCombineGreedy, and imagine that UpdateQueueFront always pushes - // data to the queue, you insert at most: - // - max_index*(max_index-1)/2 (the first two for loops) - // - max_index - 1 in the last for loop at the first iteration of the while - // loop, max_index - 2 at the second iteration ... therefore - // max_index*(max_index-1)/2 overall too - histo_queue->max_size = max_index * max_index; - // We allocate max_size + 1 because the last element at index "size" is - // used as temporary data (and it could be up to max_size). - histo_queue->queue = (HistogramPair*)WebPSafeMalloc( - histo_queue->max_size + 1, sizeof(*histo_queue->queue)); - return histo_queue->queue != NULL; -} - -static void HistoQueueClear(HistoQueue* const histo_queue) { - assert(histo_queue != NULL); - WebPSafeFree(histo_queue->queue); -} - -static void SwapHistogramPairs(HistogramPair *p1, - HistogramPair *p2) { - const HistogramPair tmp = *p1; - *p1 = *p2; - *p2 = tmp; -} - -// Given a valid priority queue in range [0, queue_size) this function checks -// whether histo_queue[queue_size] should be accepted and swaps it with the -// front if it is smaller. Otherwise, it leaves it as is. -static void UpdateQueueFront(HistoQueue* const histo_queue) { - if (histo_queue->queue[histo_queue->size].cost_diff >= 0) return; - - if (histo_queue->queue[histo_queue->size].cost_diff < - histo_queue->queue[0].cost_diff) { - SwapHistogramPairs(histo_queue->queue, - histo_queue->queue + histo_queue->size); - } - ++histo_queue->size; - - // We cannot add more elements than the capacity. - // The allocation adds an extra element to the official capacity so that - // histo_queue->queue[histo_queue->max_size] is read/written within bound. - assert(histo_queue->size <= histo_queue->max_size); -} - -// ----------------------------------------------------------------------------- - -static void PreparePair(VP8LHistogram** histograms, int idx1, int idx2, - HistogramPair* const pair) { - VP8LHistogram* h1; - VP8LHistogram* h2; - double sum_cost; - - if (idx1 > idx2) { - const int tmp = idx2; - idx2 = idx1; - idx1 = tmp; - } - pair->idx1 = idx1; - pair->idx2 = idx2; - h1 = histograms[idx1]; - h2 = histograms[idx2]; - sum_cost = h1->bit_cost_ + h2->bit_cost_; - pair->cost_combo = 0.; - GetCombinedHistogramEntropy(h1, h2, sum_cost, &pair->cost_combo); - pair->cost_diff = pair->cost_combo - sum_cost; -} - -// Combines histograms by continuously choosing the one with the highest cost -// reduction. -static int HistogramCombineGreedy(VP8LHistogramSet* const image_histo) { - int ok = 0; - int image_histo_size = image_histo->size; - int i, j; - VP8LHistogram** const histograms = image_histo->histograms; - // Indexes of remaining histograms. - int* const clusters = - (int*)WebPSafeMalloc(image_histo_size, sizeof(*clusters)); - // Priority queue of histogram pairs. - HistoQueue histo_queue; - - if (!HistoQueueInit(&histo_queue, image_histo_size) || clusters == NULL) { - goto End; - } - - for (i = 0; i < image_histo_size; ++i) { - // Initialize clusters indexes. - clusters[i] = i; - for (j = i + 1; j < image_histo_size; ++j) { - // Initialize positions array. - PreparePair(histograms, i, j, &histo_queue.queue[histo_queue.size]); - UpdateQueueFront(&histo_queue); - } - } - - while (image_histo_size > 1 && histo_queue.size > 0) { - HistogramPair* copy_to; - const int idx1 = histo_queue.queue[0].idx1; - const int idx2 = histo_queue.queue[0].idx2; - VP8LHistogramAdd(histograms[idx2], histograms[idx1], histograms[idx1]); - histograms[idx1]->bit_cost_ = histo_queue.queue[0].cost_combo; - // Remove merged histogram. - for (i = 0; i + 1 < image_histo_size; ++i) { - if (clusters[i] >= idx2) { - clusters[i] = clusters[i + 1]; - } - } - --image_histo_size; - - // Remove pairs intersecting the just combined best pair. This will - // therefore pop the head of the queue. - copy_to = histo_queue.queue; - for (i = 0; i < histo_queue.size; ++i) { - HistogramPair* const p = histo_queue.queue + i; - if (p->idx1 == idx1 || p->idx2 == idx1 || - p->idx1 == idx2 || p->idx2 == idx2) { - // Do not copy the invalid pair. - continue; - } - if (p->cost_diff < histo_queue.queue[0].cost_diff) { - // Replace the top of the queue if we found better. - SwapHistogramPairs(histo_queue.queue, p); - } - SwapHistogramPairs(copy_to, p); - ++copy_to; - } - histo_queue.size = (int)(copy_to - histo_queue.queue); - - // Push new pairs formed with combined histogram to the queue. - for (i = 0; i < image_histo_size; ++i) { - if (clusters[i] != idx1) { - PreparePair(histograms, idx1, clusters[i], - &histo_queue.queue[histo_queue.size]); - UpdateQueueFront(&histo_queue); - } - } - } - // Move remaining histograms to the beginning of the array. - for (i = 0; i < image_histo_size; ++i) { - if (i != clusters[i]) { // swap the two histograms - HistogramSwap(&histograms[i], &histograms[clusters[i]]); - } - } - - image_histo->size = image_histo_size; - ok = 1; - - End: - WebPSafeFree(clusters); - HistoQueueClear(&histo_queue); - return ok; -} - -static void HistogramCombineStochastic(VP8LHistogramSet* const image_histo, - VP8LHistogram* tmp_histo, - VP8LHistogram* best_combo, - int quality, int min_cluster_size) { - int iter; - uint32_t seed = 0; - int tries_with_no_success = 0; - int image_histo_size = image_histo->size; - const int iter_mult = (quality < 25) ? 2 : 2 + (quality - 25) / 8; - const int outer_iters = image_histo_size * iter_mult; - const int num_pairs = image_histo_size / 2; - const int num_tries_no_success = outer_iters / 2; - VP8LHistogram** const histograms = image_histo->histograms; - - // Collapse similar histograms in 'image_histo'. - ++min_cluster_size; - for (iter = 0; - iter < outer_iters && image_histo_size >= min_cluster_size; - ++iter) { - double best_cost_diff = 0.; - int best_idx1 = -1, best_idx2 = 1; - int j; - const int num_tries = - (num_pairs < image_histo_size) ? num_pairs : image_histo_size; - seed += iter; - for (j = 0; j < num_tries; ++j) { - double curr_cost_diff; - // Choose two histograms at random and try to combine them. - const uint32_t idx1 = MyRand(&seed) % image_histo_size; - const uint32_t tmp = (j & 7) + 1; - const uint32_t diff = - (tmp < 3) ? tmp : MyRand(&seed) % (image_histo_size - 1); - const uint32_t idx2 = (idx1 + diff + 1) % image_histo_size; - if (idx1 == idx2) { - continue; - } - - // Calculate cost reduction on combining. - curr_cost_diff = HistogramAddEval(histograms[idx1], histograms[idx2], - tmp_histo, best_cost_diff); - if (curr_cost_diff < best_cost_diff) { // found a better pair? - HistogramSwap(&best_combo, &tmp_histo); - best_cost_diff = curr_cost_diff; - best_idx1 = idx1; - best_idx2 = idx2; - } - } - - if (best_idx1 >= 0) { - HistogramSwap(&best_combo, &histograms[best_idx1]); - // swap best_idx2 slot with last one (which is now unused) - --image_histo_size; - if (best_idx2 != image_histo_size) { - HistogramSwap(&histograms[image_histo_size], &histograms[best_idx2]); - histograms[image_histo_size] = NULL; - } - tries_with_no_success = 0; - } - if (++tries_with_no_success >= num_tries_no_success) { - break; - } - } - image_histo->size = image_histo_size; -} - -// ----------------------------------------------------------------------------- -// Histogram refinement - -// Find the best 'out' histogram for each of the 'in' histograms. -// Note: we assume that out[]->bit_cost_ is already up-to-date. -static void HistogramRemap(const VP8LHistogramSet* const in, - const VP8LHistogramSet* const out, - uint16_t* const symbols) { - int i; - VP8LHistogram** const in_histo = in->histograms; - VP8LHistogram** const out_histo = out->histograms; - const int in_size = in->size; - const int out_size = out->size; - if (out_size > 1) { - for (i = 0; i < in_size; ++i) { - int best_out = 0; - double best_bits = MAX_COST; - int k; - for (k = 0; k < out_size; ++k) { - const double cur_bits = - HistogramAddThresh(out_histo[k], in_histo[i], best_bits); - if (k == 0 || cur_bits < best_bits) { - best_bits = cur_bits; - best_out = k; - } - } - symbols[i] = best_out; - } - } else { - assert(out_size == 1); - for (i = 0; i < in_size; ++i) { - symbols[i] = 0; - } - } - - // Recompute each out based on raw and symbols. - for (i = 0; i < out_size; ++i) { - HistogramClear(out_histo[i]); - } - - for (i = 0; i < in_size; ++i) { - const int idx = symbols[i]; - VP8LHistogramAdd(in_histo[i], out_histo[idx], out_histo[idx]); - } -} - -static double GetCombineCostFactor(int histo_size, int quality) { - double combine_cost_factor = 0.16; - if (quality < 90) { - if (histo_size > 256) combine_cost_factor /= 2.; - if (histo_size > 512) combine_cost_factor /= 2.; - if (histo_size > 1024) combine_cost_factor /= 2.; - if (quality <= 50) combine_cost_factor /= 2.; - } - return combine_cost_factor; -} - -int VP8LGetHistoImageSymbols(int xsize, int ysize, - const VP8LBackwardRefs* const refs, - int quality, int low_effort, - int histo_bits, int cache_bits, - VP8LHistogramSet* const image_histo, - VP8LHistogramSet* const tmp_histos, - uint16_t* const histogram_symbols) { - int ok = 0; - const int histo_xsize = histo_bits ? VP8LSubSampleSize(xsize, histo_bits) : 1; - const int histo_ysize = histo_bits ? VP8LSubSampleSize(ysize, histo_bits) : 1; - const int image_histo_raw_size = histo_xsize * histo_ysize; - const int entropy_combine_num_bins = low_effort ? NUM_PARTITIONS : BIN_SIZE; - - // The bin_map for every bin follows following semantics: - // bin_map[n][0] = num_histo; // The number of histograms in that bin. - // bin_map[n][1] = index of first histogram in that bin; - // bin_map[n][num_histo] = index of last histogram in that bin; - // bin_map[n][num_histo + 1] ... bin_map[n][bin_depth - 1] = unused indices. - const int bin_depth = image_histo_raw_size + 1; - int16_t* bin_map = NULL; - VP8LHistogramSet* const orig_histo = - VP8LAllocateHistogramSet(image_histo_raw_size, cache_bits); - VP8LHistogram* cur_combo; - const int entropy_combine = - (orig_histo->size > entropy_combine_num_bins * 2) && (quality < 100); - - if (orig_histo == NULL) goto Error; - - // Don't attempt linear bin-partition heuristic for: - // histograms of small sizes, as bin_map will be very sparse and; - // Maximum quality (q==100), to preserve the compression gains at that level. - if (entropy_combine) { - const int bin_map_size = bin_depth * entropy_combine_num_bins; - bin_map = (int16_t*)WebPSafeCalloc(bin_map_size, sizeof(*bin_map)); - if (bin_map == NULL) goto Error; - } - - // Construct the histograms from backward references. - HistogramBuild(xsize, histo_bits, refs, orig_histo); - // Copies the histograms and computes its bit_cost. - HistogramCopyAndAnalyze(orig_histo, image_histo); - - cur_combo = tmp_histos->histograms[1]; // pick up working slot - if (entropy_combine) { - const double combine_cost_factor = - GetCombineCostFactor(image_histo_raw_size, quality); - HistogramAnalyzeEntropyBin(orig_histo, bin_map, low_effort); - // Collapse histograms with similar entropy. - cur_combo = HistogramCombineEntropyBin(image_histo, cur_combo, bin_map, - bin_depth, entropy_combine_num_bins, - combine_cost_factor, low_effort); - } - - // Don't combine the histograms using stochastic and greedy heuristics for - // low-effort compression mode. - if (!low_effort || !entropy_combine) { - const float x = quality / 100.f; - // cubic ramp between 1 and MAX_HISTO_GREEDY: - const int threshold_size = (int)(1 + (x * x * x) * (MAX_HISTO_GREEDY - 1)); - HistogramCombineStochastic(image_histo, tmp_histos->histograms[0], - cur_combo, quality, threshold_size); - if ((image_histo->size <= threshold_size) && - !HistogramCombineGreedy(image_histo)) { - goto Error; - } - } - - // TODO(vikasa): Optimize HistogramRemap for low-effort compression mode also. - // Find the optimal map from original histograms to the final ones. - HistogramRemap(orig_histo, image_histo, histogram_symbols); - - ok = 1; - - Error: - WebPSafeFree(bin_map); - VP8LFreeHistogramSet(orig_histo); - return ok; -} diff --git a/thirdparty/libwebp/enc/histogram.h b/thirdparty/libwebp/enc/histogram.h deleted file mode 100644 index d303d1d58b..0000000000 --- a/thirdparty/libwebp/enc/histogram.h +++ /dev/null @@ -1,123 +0,0 @@ -// Copyright 2012 Google Inc. All Rights Reserved. -// -// Use of this source code is governed by a BSD-style license -// that can be found in the COPYING file in the root of the source -// tree. An additional intellectual property rights grant can be found -// in the file PATENTS. All contributing project authors may -// be found in the AUTHORS file in the root of the source tree. -// ----------------------------------------------------------------------------- -// -// Author: Jyrki Alakuijala (jyrki@google.com) -// -// Models the histograms of literal and distance codes. - -#ifndef WEBP_ENC_HISTOGRAM_H_ -#define WEBP_ENC_HISTOGRAM_H_ - -#include - -#include "./backward_references.h" -#include "../webp/format_constants.h" -#include "../webp/types.h" - -#ifdef __cplusplus -extern "C" { -#endif - -// Not a trivial literal symbol. -#define VP8L_NON_TRIVIAL_SYM (0xffffffff) - -// A simple container for histograms of data. -typedef struct { - // literal_ contains green literal, palette-code and - // copy-length-prefix histogram - uint32_t* literal_; // Pointer to the allocated buffer for literal. - uint32_t red_[NUM_LITERAL_CODES]; - uint32_t blue_[NUM_LITERAL_CODES]; - uint32_t alpha_[NUM_LITERAL_CODES]; - // Backward reference prefix-code histogram. - uint32_t distance_[NUM_DISTANCE_CODES]; - int palette_code_bits_; - uint32_t trivial_symbol_; // True, if histograms for Red, Blue & Alpha - // literal symbols are single valued. - double bit_cost_; // cached value of bit cost. - double literal_cost_; // Cached values of dominant entropy costs: - double red_cost_; // literal, red & blue. - double blue_cost_; -} VP8LHistogram; - -// Collection of histograms with fixed capacity, allocated as one -// big memory chunk. Can be destroyed by calling WebPSafeFree(). -typedef struct { - int size; // number of slots currently in use - int max_size; // maximum capacity - VP8LHistogram** histograms; -} VP8LHistogramSet; - -// Create the histogram. -// -// The input data is the PixOrCopy data, which models the literals, stop -// codes and backward references (both distances and lengths). Also: if -// palette_code_bits is >= 0, initialize the histogram with this value. -void VP8LHistogramCreate(VP8LHistogram* const p, - const VP8LBackwardRefs* const refs, - int palette_code_bits); - -// Return the size of the histogram for a given palette_code_bits. -int VP8LGetHistogramSize(int palette_code_bits); - -// Set the palette_code_bits and reset the stats. -void VP8LHistogramInit(VP8LHistogram* const p, int palette_code_bits); - -// Collect all the references into a histogram (without reset) -void VP8LHistogramStoreRefs(const VP8LBackwardRefs* const refs, - VP8LHistogram* const histo); - -// Free the memory allocated for the histogram. -void VP8LFreeHistogram(VP8LHistogram* const histo); - -// Free the memory allocated for the histogram set. -void VP8LFreeHistogramSet(VP8LHistogramSet* const histo); - -// Allocate an array of pointer to histograms, allocated and initialized -// using 'cache_bits'. Return NULL in case of memory error. -VP8LHistogramSet* VP8LAllocateHistogramSet(int size, int cache_bits); - -// Allocate and initialize histogram object with specified 'cache_bits'. -// Returns NULL in case of memory error. -// Special case of VP8LAllocateHistogramSet, with size equals 1. -VP8LHistogram* VP8LAllocateHistogram(int cache_bits); - -// Accumulate a token 'v' into a histogram. -void VP8LHistogramAddSinglePixOrCopy(VP8LHistogram* const histo, - const PixOrCopy* const v); - -static WEBP_INLINE int VP8LHistogramNumCodes(int palette_code_bits) { - return NUM_LITERAL_CODES + NUM_LENGTH_CODES + - ((palette_code_bits > 0) ? (1 << palette_code_bits) : 0); -} - -// Builds the histogram image. -int VP8LGetHistoImageSymbols(int xsize, int ysize, - const VP8LBackwardRefs* const refs, - int quality, int low_effort, - int histogram_bits, int cache_bits, - VP8LHistogramSet* const image_in, - VP8LHistogramSet* const tmp_histos, - uint16_t* const histogram_symbols); - -// Returns the entropy for the symbols in the input array. -// Also sets trivial_symbol to the code value, if the array has only one code -// value. Otherwise, set it to VP8L_NON_TRIVIAL_SYM. -double VP8LBitsEntropy(const uint32_t* const array, int n, - uint32_t* const trivial_symbol); - -// Estimate how many bits the combined entropy of literals and distance -// approximately maps to. -double VP8LHistogramEstimateBits(const VP8LHistogram* const p); - -#ifdef __cplusplus -} -#endif - -#endif // WEBP_ENC_HISTOGRAM_H_ diff --git a/thirdparty/libwebp/enc/histogram_enc.c b/thirdparty/libwebp/enc/histogram_enc.c new file mode 100644 index 0000000000..808b6f78ab --- /dev/null +++ b/thirdparty/libwebp/enc/histogram_enc.c @@ -0,0 +1,990 @@ +// Copyright 2012 Google Inc. All Rights Reserved. +// +// Use of this source code is governed by a BSD-style license +// that can be found in the COPYING file in the root of the source +// tree. An additional intellectual property rights grant can be found +// in the file PATENTS. All contributing project authors may +// be found in the AUTHORS file in the root of the source tree. +// ----------------------------------------------------------------------------- +// +// Author: Jyrki Alakuijala (jyrki@google.com) +// +#ifdef HAVE_CONFIG_H +#include "../webp/config.h" +#endif + +#include + +#include "./backward_references_enc.h" +#include "./histogram_enc.h" +#include "../dsp/lossless.h" +#include "../dsp/lossless_common.h" +#include "../utils/utils.h" + +#define MAX_COST 1.e38 + +// Number of partitions for the three dominant (literal, red and blue) symbol +// costs. +#define NUM_PARTITIONS 4 +// The size of the bin-hash corresponding to the three dominant costs. +#define BIN_SIZE (NUM_PARTITIONS * NUM_PARTITIONS * NUM_PARTITIONS) +// Maximum number of histograms allowed in greedy combining algorithm. +#define MAX_HISTO_GREEDY 100 + +static void HistogramClear(VP8LHistogram* const p) { + uint32_t* const literal = p->literal_; + const int cache_bits = p->palette_code_bits_; + const int histo_size = VP8LGetHistogramSize(cache_bits); + memset(p, 0, histo_size); + p->palette_code_bits_ = cache_bits; + p->literal_ = literal; +} + +// Swap two histogram pointers. +static void HistogramSwap(VP8LHistogram** const A, VP8LHistogram** const B) { + VP8LHistogram* const tmp = *A; + *A = *B; + *B = tmp; +} + +static void HistogramCopy(const VP8LHistogram* const src, + VP8LHistogram* const dst) { + uint32_t* const dst_literal = dst->literal_; + const int dst_cache_bits = dst->palette_code_bits_; + const int histo_size = VP8LGetHistogramSize(dst_cache_bits); + assert(src->palette_code_bits_ == dst_cache_bits); + memcpy(dst, src, histo_size); + dst->literal_ = dst_literal; +} + +int VP8LGetHistogramSize(int cache_bits) { + const int literal_size = VP8LHistogramNumCodes(cache_bits); + const size_t total_size = sizeof(VP8LHistogram) + sizeof(int) * literal_size; + assert(total_size <= (size_t)0x7fffffff); + return (int)total_size; +} + +void VP8LFreeHistogram(VP8LHistogram* const histo) { + WebPSafeFree(histo); +} + +void VP8LFreeHistogramSet(VP8LHistogramSet* const histo) { + WebPSafeFree(histo); +} + +void VP8LHistogramStoreRefs(const VP8LBackwardRefs* const refs, + VP8LHistogram* const histo) { + VP8LRefsCursor c = VP8LRefsCursorInit(refs); + while (VP8LRefsCursorOk(&c)) { + VP8LHistogramAddSinglePixOrCopy(histo, c.cur_pos); + VP8LRefsCursorNext(&c); + } +} + +void VP8LHistogramCreate(VP8LHistogram* const p, + const VP8LBackwardRefs* const refs, + int palette_code_bits) { + if (palette_code_bits >= 0) { + p->palette_code_bits_ = palette_code_bits; + } + HistogramClear(p); + VP8LHistogramStoreRefs(refs, p); +} + +void VP8LHistogramInit(VP8LHistogram* const p, int palette_code_bits) { + p->palette_code_bits_ = palette_code_bits; + HistogramClear(p); +} + +VP8LHistogram* VP8LAllocateHistogram(int cache_bits) { + VP8LHistogram* histo = NULL; + const int total_size = VP8LGetHistogramSize(cache_bits); + uint8_t* const memory = (uint8_t*)WebPSafeMalloc(total_size, sizeof(*memory)); + if (memory == NULL) return NULL; + histo = (VP8LHistogram*)memory; + // literal_ won't necessary be aligned. + histo->literal_ = (uint32_t*)(memory + sizeof(VP8LHistogram)); + VP8LHistogramInit(histo, cache_bits); + return histo; +} + +VP8LHistogramSet* VP8LAllocateHistogramSet(int size, int cache_bits) { + int i; + VP8LHistogramSet* set; + const int histo_size = VP8LGetHistogramSize(cache_bits); + const size_t total_size = + sizeof(*set) + size * (sizeof(*set->histograms) + + histo_size + WEBP_ALIGN_CST); + uint8_t* memory = (uint8_t*)WebPSafeMalloc(total_size, sizeof(*memory)); + if (memory == NULL) return NULL; + + set = (VP8LHistogramSet*)memory; + memory += sizeof(*set); + set->histograms = (VP8LHistogram**)memory; + memory += size * sizeof(*set->histograms); + set->max_size = size; + set->size = size; + for (i = 0; i < size; ++i) { + memory = (uint8_t*)WEBP_ALIGN(memory); + set->histograms[i] = (VP8LHistogram*)memory; + // literal_ won't necessary be aligned. + set->histograms[i]->literal_ = (uint32_t*)(memory + sizeof(VP8LHistogram)); + VP8LHistogramInit(set->histograms[i], cache_bits); + memory += histo_size; + } + return set; +} + +// ----------------------------------------------------------------------------- + +void VP8LHistogramAddSinglePixOrCopy(VP8LHistogram* const histo, + const PixOrCopy* const v) { + if (PixOrCopyIsLiteral(v)) { + ++histo->alpha_[PixOrCopyLiteral(v, 3)]; + ++histo->red_[PixOrCopyLiteral(v, 2)]; + ++histo->literal_[PixOrCopyLiteral(v, 1)]; + ++histo->blue_[PixOrCopyLiteral(v, 0)]; + } else if (PixOrCopyIsCacheIdx(v)) { + const int literal_ix = + NUM_LITERAL_CODES + NUM_LENGTH_CODES + PixOrCopyCacheIdx(v); + ++histo->literal_[literal_ix]; + } else { + int code, extra_bits; + VP8LPrefixEncodeBits(PixOrCopyLength(v), &code, &extra_bits); + ++histo->literal_[NUM_LITERAL_CODES + code]; + VP8LPrefixEncodeBits(PixOrCopyDistance(v), &code, &extra_bits); + ++histo->distance_[code]; + } +} + +// ----------------------------------------------------------------------------- +// Entropy-related functions. + +static WEBP_INLINE double BitsEntropyRefine(const VP8LBitEntropy* entropy) { + double mix; + if (entropy->nonzeros < 5) { + if (entropy->nonzeros <= 1) { + return 0; + } + // Two symbols, they will be 0 and 1 in a Huffman code. + // Let's mix in a bit of entropy to favor good clustering when + // distributions of these are combined. + if (entropy->nonzeros == 2) { + return 0.99 * entropy->sum + 0.01 * entropy->entropy; + } + // No matter what the entropy says, we cannot be better than min_limit + // with Huffman coding. I am mixing a bit of entropy into the + // min_limit since it produces much better (~0.5 %) compression results + // perhaps because of better entropy clustering. + if (entropy->nonzeros == 3) { + mix = 0.95; + } else { + mix = 0.7; // nonzeros == 4. + } + } else { + mix = 0.627; + } + + { + double min_limit = 2 * entropy->sum - entropy->max_val; + min_limit = mix * min_limit + (1.0 - mix) * entropy->entropy; + return (entropy->entropy < min_limit) ? min_limit : entropy->entropy; + } +} + +double VP8LBitsEntropy(const uint32_t* const array, int n, + uint32_t* const trivial_symbol) { + VP8LBitEntropy entropy; + VP8LBitsEntropyUnrefined(array, n, &entropy); + if (trivial_symbol != NULL) { + *trivial_symbol = + (entropy.nonzeros == 1) ? entropy.nonzero_code : VP8L_NON_TRIVIAL_SYM; + } + + return BitsEntropyRefine(&entropy); +} + +static double InitialHuffmanCost(void) { + // Small bias because Huffman code length is typically not stored in + // full length. + static const int kHuffmanCodeOfHuffmanCodeSize = CODE_LENGTH_CODES * 3; + static const double kSmallBias = 9.1; + return kHuffmanCodeOfHuffmanCodeSize - kSmallBias; +} + +// Finalize the Huffman cost based on streak numbers and length type (<3 or >=3) +static double FinalHuffmanCost(const VP8LStreaks* const stats) { + // The constants in this function are experimental and got rounded from + // their original values in 1/8 when switched to 1/1024. + double retval = InitialHuffmanCost(); + // Second coefficient: Many zeros in the histogram are covered efficiently + // by a run-length encode. Originally 2/8. + retval += stats->counts[0] * 1.5625 + 0.234375 * stats->streaks[0][1]; + // Second coefficient: Constant values are encoded less efficiently, but still + // RLE'ed. Originally 6/8. + retval += stats->counts[1] * 2.578125 + 0.703125 * stats->streaks[1][1]; + // 0s are usually encoded more efficiently than non-0s. + // Originally 15/8. + retval += 1.796875 * stats->streaks[0][0]; + // Originally 26/8. + retval += 3.28125 * stats->streaks[1][0]; + return retval; +} + +// Get the symbol entropy for the distribution 'population'. +// Set 'trivial_sym', if there's only one symbol present in the distribution. +static double PopulationCost(const uint32_t* const population, int length, + uint32_t* const trivial_sym) { + VP8LBitEntropy bit_entropy; + VP8LStreaks stats; + VP8LGetEntropyUnrefined(population, length, &bit_entropy, &stats); + if (trivial_sym != NULL) { + *trivial_sym = (bit_entropy.nonzeros == 1) ? bit_entropy.nonzero_code + : VP8L_NON_TRIVIAL_SYM; + } + + return BitsEntropyRefine(&bit_entropy) + FinalHuffmanCost(&stats); +} + +// trivial_at_end is 1 if the two histograms only have one element that is +// non-zero: both the zero-th one, or both the last one. +static WEBP_INLINE double GetCombinedEntropy(const uint32_t* const X, + const uint32_t* const Y, + int length, int trivial_at_end) { + VP8LStreaks stats; + if (trivial_at_end) { + // This configuration is due to palettization that transforms an indexed + // pixel into 0xff000000 | (pixel << 8) in VP8LBundleColorMap. + // BitsEntropyRefine is 0 for histograms with only one non-zero value. + // Only FinalHuffmanCost needs to be evaluated. + memset(&stats, 0, sizeof(stats)); + // Deal with the non-zero value at index 0 or length-1. + stats.streaks[1][0] += 1; + // Deal with the following/previous zero streak. + stats.counts[0] += 1; + stats.streaks[0][1] += length - 1; + return FinalHuffmanCost(&stats); + } else { + VP8LBitEntropy bit_entropy; + VP8LGetCombinedEntropyUnrefined(X, Y, length, &bit_entropy, &stats); + + return BitsEntropyRefine(&bit_entropy) + FinalHuffmanCost(&stats); + } +} + +// Estimates the Entropy + Huffman + other block overhead size cost. +double VP8LHistogramEstimateBits(const VP8LHistogram* const p) { + return + PopulationCost( + p->literal_, VP8LHistogramNumCodes(p->palette_code_bits_), NULL) + + PopulationCost(p->red_, NUM_LITERAL_CODES, NULL) + + PopulationCost(p->blue_, NUM_LITERAL_CODES, NULL) + + PopulationCost(p->alpha_, NUM_LITERAL_CODES, NULL) + + PopulationCost(p->distance_, NUM_DISTANCE_CODES, NULL) + + VP8LExtraCost(p->literal_ + NUM_LITERAL_CODES, NUM_LENGTH_CODES) + + VP8LExtraCost(p->distance_, NUM_DISTANCE_CODES); +} + +// ----------------------------------------------------------------------------- +// Various histogram combine/cost-eval functions + +static int GetCombinedHistogramEntropy(const VP8LHistogram* const a, + const VP8LHistogram* const b, + double cost_threshold, + double* cost) { + const int palette_code_bits = a->palette_code_bits_; + int trivial_at_end = 0; + assert(a->palette_code_bits_ == b->palette_code_bits_); + *cost += GetCombinedEntropy(a->literal_, b->literal_, + VP8LHistogramNumCodes(palette_code_bits), 0); + *cost += VP8LExtraCostCombined(a->literal_ + NUM_LITERAL_CODES, + b->literal_ + NUM_LITERAL_CODES, + NUM_LENGTH_CODES); + if (*cost > cost_threshold) return 0; + + if (a->trivial_symbol_ != VP8L_NON_TRIVIAL_SYM && + a->trivial_symbol_ == b->trivial_symbol_) { + // A, R and B are all 0 or 0xff. + const uint32_t color_a = (a->trivial_symbol_ >> 24) & 0xff; + const uint32_t color_r = (a->trivial_symbol_ >> 16) & 0xff; + const uint32_t color_b = (a->trivial_symbol_ >> 0) & 0xff; + if ((color_a == 0 || color_a == 0xff) && + (color_r == 0 || color_r == 0xff) && + (color_b == 0 || color_b == 0xff)) { + trivial_at_end = 1; + } + } + + *cost += + GetCombinedEntropy(a->red_, b->red_, NUM_LITERAL_CODES, trivial_at_end); + if (*cost > cost_threshold) return 0; + + *cost += + GetCombinedEntropy(a->blue_, b->blue_, NUM_LITERAL_CODES, trivial_at_end); + if (*cost > cost_threshold) return 0; + + *cost += GetCombinedEntropy(a->alpha_, b->alpha_, NUM_LITERAL_CODES, + trivial_at_end); + if (*cost > cost_threshold) return 0; + + *cost += + GetCombinedEntropy(a->distance_, b->distance_, NUM_DISTANCE_CODES, 0); + *cost += + VP8LExtraCostCombined(a->distance_, b->distance_, NUM_DISTANCE_CODES); + if (*cost > cost_threshold) return 0; + + return 1; +} + +static WEBP_INLINE void HistogramAdd(const VP8LHistogram* const a, + const VP8LHistogram* const b, + VP8LHistogram* const out) { + VP8LHistogramAdd(a, b, out); + out->trivial_symbol_ = (a->trivial_symbol_ == b->trivial_symbol_) + ? a->trivial_symbol_ + : VP8L_NON_TRIVIAL_SYM; +} + +// Performs out = a + b, computing the cost C(a+b) - C(a) - C(b) while comparing +// to the threshold value 'cost_threshold'. The score returned is +// Score = C(a+b) - C(a) - C(b), where C(a) + C(b) is known and fixed. +// Since the previous score passed is 'cost_threshold', we only need to compare +// the partial cost against 'cost_threshold + C(a) + C(b)' to possibly bail-out +// early. +static double HistogramAddEval(const VP8LHistogram* const a, + const VP8LHistogram* const b, + VP8LHistogram* const out, + double cost_threshold) { + double cost = 0; + const double sum_cost = a->bit_cost_ + b->bit_cost_; + cost_threshold += sum_cost; + + if (GetCombinedHistogramEntropy(a, b, cost_threshold, &cost)) { + HistogramAdd(a, b, out); + out->bit_cost_ = cost; + out->palette_code_bits_ = a->palette_code_bits_; + } + + return cost - sum_cost; +} + +// Same as HistogramAddEval(), except that the resulting histogram +// is not stored. Only the cost C(a+b) - C(a) is evaluated. We omit +// the term C(b) which is constant over all the evaluations. +static double HistogramAddThresh(const VP8LHistogram* const a, + const VP8LHistogram* const b, + double cost_threshold) { + double cost = -a->bit_cost_; + GetCombinedHistogramEntropy(a, b, cost_threshold, &cost); + return cost; +} + +// ----------------------------------------------------------------------------- + +// The structure to keep track of cost range for the three dominant entropy +// symbols. +// TODO(skal): Evaluate if float can be used here instead of double for +// representing the entropy costs. +typedef struct { + double literal_max_; + double literal_min_; + double red_max_; + double red_min_; + double blue_max_; + double blue_min_; +} DominantCostRange; + +static void DominantCostRangeInit(DominantCostRange* const c) { + c->literal_max_ = 0.; + c->literal_min_ = MAX_COST; + c->red_max_ = 0.; + c->red_min_ = MAX_COST; + c->blue_max_ = 0.; + c->blue_min_ = MAX_COST; +} + +static void UpdateDominantCostRange( + const VP8LHistogram* const h, DominantCostRange* const c) { + if (c->literal_max_ < h->literal_cost_) c->literal_max_ = h->literal_cost_; + if (c->literal_min_ > h->literal_cost_) c->literal_min_ = h->literal_cost_; + if (c->red_max_ < h->red_cost_) c->red_max_ = h->red_cost_; + if (c->red_min_ > h->red_cost_) c->red_min_ = h->red_cost_; + if (c->blue_max_ < h->blue_cost_) c->blue_max_ = h->blue_cost_; + if (c->blue_min_ > h->blue_cost_) c->blue_min_ = h->blue_cost_; +} + +static void UpdateHistogramCost(VP8LHistogram* const h) { + uint32_t alpha_sym, red_sym, blue_sym; + const double alpha_cost = + PopulationCost(h->alpha_, NUM_LITERAL_CODES, &alpha_sym); + const double distance_cost = + PopulationCost(h->distance_, NUM_DISTANCE_CODES, NULL) + + VP8LExtraCost(h->distance_, NUM_DISTANCE_CODES); + const int num_codes = VP8LHistogramNumCodes(h->palette_code_bits_); + h->literal_cost_ = PopulationCost(h->literal_, num_codes, NULL) + + VP8LExtraCost(h->literal_ + NUM_LITERAL_CODES, + NUM_LENGTH_CODES); + h->red_cost_ = PopulationCost(h->red_, NUM_LITERAL_CODES, &red_sym); + h->blue_cost_ = PopulationCost(h->blue_, NUM_LITERAL_CODES, &blue_sym); + h->bit_cost_ = h->literal_cost_ + h->red_cost_ + h->blue_cost_ + + alpha_cost + distance_cost; + if ((alpha_sym | red_sym | blue_sym) == VP8L_NON_TRIVIAL_SYM) { + h->trivial_symbol_ = VP8L_NON_TRIVIAL_SYM; + } else { + h->trivial_symbol_ = + ((uint32_t)alpha_sym << 24) | (red_sym << 16) | (blue_sym << 0); + } +} + +static int GetBinIdForEntropy(double min, double max, double val) { + const double range = max - min; + if (range > 0.) { + const double delta = val - min; + return (int)((NUM_PARTITIONS - 1e-6) * delta / range); + } else { + return 0; + } +} + +static int GetHistoBinIndex(const VP8LHistogram* const h, + const DominantCostRange* const c, int low_effort) { + int bin_id = GetBinIdForEntropy(c->literal_min_, c->literal_max_, + h->literal_cost_); + assert(bin_id < NUM_PARTITIONS); + if (!low_effort) { + bin_id = bin_id * NUM_PARTITIONS + + GetBinIdForEntropy(c->red_min_, c->red_max_, h->red_cost_); + bin_id = bin_id * NUM_PARTITIONS + + GetBinIdForEntropy(c->blue_min_, c->blue_max_, h->blue_cost_); + assert(bin_id < BIN_SIZE); + } + return bin_id; +} + +// Construct the histograms from backward references. +static void HistogramBuild( + int xsize, int histo_bits, const VP8LBackwardRefs* const backward_refs, + VP8LHistogramSet* const image_histo) { + int x = 0, y = 0; + const int histo_xsize = VP8LSubSampleSize(xsize, histo_bits); + VP8LHistogram** const histograms = image_histo->histograms; + VP8LRefsCursor c = VP8LRefsCursorInit(backward_refs); + assert(histo_bits > 0); + while (VP8LRefsCursorOk(&c)) { + const PixOrCopy* const v = c.cur_pos; + const int ix = (y >> histo_bits) * histo_xsize + (x >> histo_bits); + VP8LHistogramAddSinglePixOrCopy(histograms[ix], v); + x += PixOrCopyLength(v); + while (x >= xsize) { + x -= xsize; + ++y; + } + VP8LRefsCursorNext(&c); + } +} + +// Copies the histograms and computes its bit_cost. +static void HistogramCopyAndAnalyze( + VP8LHistogramSet* const orig_histo, VP8LHistogramSet* const image_histo) { + int i; + const int histo_size = orig_histo->size; + VP8LHistogram** const orig_histograms = orig_histo->histograms; + VP8LHistogram** const histograms = image_histo->histograms; + for (i = 0; i < histo_size; ++i) { + VP8LHistogram* const histo = orig_histograms[i]; + UpdateHistogramCost(histo); + // Copy histograms from orig_histo[] to image_histo[]. + HistogramCopy(histo, histograms[i]); + } +} + +// Partition histograms to different entropy bins for three dominant (literal, +// red and blue) symbol costs and compute the histogram aggregate bit_cost. +static void HistogramAnalyzeEntropyBin(VP8LHistogramSet* const image_histo, + uint16_t* const bin_map, + int low_effort) { + int i; + VP8LHistogram** const histograms = image_histo->histograms; + const int histo_size = image_histo->size; + DominantCostRange cost_range; + DominantCostRangeInit(&cost_range); + + // Analyze the dominant (literal, red and blue) entropy costs. + for (i = 0; i < histo_size; ++i) { + UpdateDominantCostRange(histograms[i], &cost_range); + } + + // bin-hash histograms on three of the dominant (literal, red and blue) + // symbol costs and store the resulting bin_id for each histogram. + for (i = 0; i < histo_size; ++i) { + bin_map[i] = GetHistoBinIndex(histograms[i], &cost_range, low_effort); + } +} + +// Compact image_histo[] by merging some histograms with same bin_id together if +// it's advantageous. +static VP8LHistogram* HistogramCombineEntropyBin( + VP8LHistogramSet* const image_histo, + VP8LHistogram* cur_combo, + const uint16_t* const bin_map, int bin_map_size, int num_bins, + double combine_cost_factor, int low_effort) { + VP8LHistogram** const histograms = image_histo->histograms; + int idx; + // Work in-place: processed histograms are put at the beginning of + // image_histo[]. At the end, we just have to truncate the array. + int size = 0; + struct { + int16_t first; // position of the histogram that accumulates all + // histograms with the same bin_id + uint16_t num_combine_failures; // number of combine failures per bin_id + } bin_info[BIN_SIZE]; + + assert(num_bins <= BIN_SIZE); + for (idx = 0; idx < num_bins; ++idx) { + bin_info[idx].first = -1; + bin_info[idx].num_combine_failures = 0; + } + + for (idx = 0; idx < bin_map_size; ++idx) { + const int bin_id = bin_map[idx]; + const int first = bin_info[bin_id].first; + assert(size <= idx); + if (first == -1) { + // just move histogram #idx to its final position + histograms[size] = histograms[idx]; + bin_info[bin_id].first = size++; + } else if (low_effort) { + HistogramAdd(histograms[idx], histograms[first], histograms[first]); + } else { + // try to merge #idx into #first (both share the same bin_id) + const double bit_cost = histograms[idx]->bit_cost_; + const double bit_cost_thresh = -bit_cost * combine_cost_factor; + const double curr_cost_diff = + HistogramAddEval(histograms[first], histograms[idx], + cur_combo, bit_cost_thresh); + if (curr_cost_diff < bit_cost_thresh) { + // Try to merge two histograms only if the combo is a trivial one or + // the two candidate histograms are already non-trivial. + // For some images, 'try_combine' turns out to be false for a lot of + // histogram pairs. In that case, we fallback to combining + // histograms as usual to avoid increasing the header size. + const int try_combine = + (cur_combo->trivial_symbol_ != VP8L_NON_TRIVIAL_SYM) || + ((histograms[idx]->trivial_symbol_ == VP8L_NON_TRIVIAL_SYM) && + (histograms[first]->trivial_symbol_ == VP8L_NON_TRIVIAL_SYM)); + const int max_combine_failures = 32; + if (try_combine || + bin_info[bin_id].num_combine_failures >= max_combine_failures) { + // move the (better) merged histogram to its final slot + HistogramSwap(&cur_combo, &histograms[first]); + } else { + histograms[size++] = histograms[idx]; + ++bin_info[bin_id].num_combine_failures; + } + } else { + histograms[size++] = histograms[idx]; + } + } + } + image_histo->size = size; + if (low_effort) { + // for low_effort case, update the final cost when everything is merged + for (idx = 0; idx < size; ++idx) { + UpdateHistogramCost(histograms[idx]); + } + } + return cur_combo; +} + +static uint32_t MyRand(uint32_t* const seed) { + *seed = (*seed * 16807ull) & 0xffffffffu; + if (*seed == 0) { + *seed = 1; + } + return *seed; +} + +// ----------------------------------------------------------------------------- +// Histogram pairs priority queue + +// Pair of histograms. Negative idx1 value means that pair is out-of-date. +typedef struct { + int idx1; + int idx2; + double cost_diff; + double cost_combo; +} HistogramPair; + +typedef struct { + HistogramPair* queue; + int size; + int max_size; +} HistoQueue; + +static int HistoQueueInit(HistoQueue* const histo_queue, const int max_index) { + histo_queue->size = 0; + // max_index^2 for the queue size is safe. If you look at + // HistogramCombineGreedy, and imagine that UpdateQueueFront always pushes + // data to the queue, you insert at most: + // - max_index*(max_index-1)/2 (the first two for loops) + // - max_index - 1 in the last for loop at the first iteration of the while + // loop, max_index - 2 at the second iteration ... therefore + // max_index*(max_index-1)/2 overall too + histo_queue->max_size = max_index * max_index; + // We allocate max_size + 1 because the last element at index "size" is + // used as temporary data (and it could be up to max_size). + histo_queue->queue = (HistogramPair*)WebPSafeMalloc( + histo_queue->max_size + 1, sizeof(*histo_queue->queue)); + return histo_queue->queue != NULL; +} + +static void HistoQueueClear(HistoQueue* const histo_queue) { + assert(histo_queue != NULL); + WebPSafeFree(histo_queue->queue); +} + +static void SwapHistogramPairs(HistogramPair *p1, + HistogramPair *p2) { + const HistogramPair tmp = *p1; + *p1 = *p2; + *p2 = tmp; +} + +// Given a valid priority queue in range [0, queue_size) this function checks +// whether histo_queue[queue_size] should be accepted and swaps it with the +// front if it is smaller. Otherwise, it leaves it as is. +static void UpdateQueueFront(HistoQueue* const histo_queue) { + if (histo_queue->queue[histo_queue->size].cost_diff >= 0) return; + + if (histo_queue->queue[histo_queue->size].cost_diff < + histo_queue->queue[0].cost_diff) { + SwapHistogramPairs(histo_queue->queue, + histo_queue->queue + histo_queue->size); + } + ++histo_queue->size; + + // We cannot add more elements than the capacity. + // The allocation adds an extra element to the official capacity so that + // histo_queue->queue[histo_queue->max_size] is read/written within bound. + assert(histo_queue->size <= histo_queue->max_size); +} + +// ----------------------------------------------------------------------------- + +static void PreparePair(VP8LHistogram** histograms, int idx1, int idx2, + HistogramPair* const pair) { + VP8LHistogram* h1; + VP8LHistogram* h2; + double sum_cost; + + if (idx1 > idx2) { + const int tmp = idx2; + idx2 = idx1; + idx1 = tmp; + } + pair->idx1 = idx1; + pair->idx2 = idx2; + h1 = histograms[idx1]; + h2 = histograms[idx2]; + sum_cost = h1->bit_cost_ + h2->bit_cost_; + pair->cost_combo = 0.; + GetCombinedHistogramEntropy(h1, h2, sum_cost, &pair->cost_combo); + pair->cost_diff = pair->cost_combo - sum_cost; +} + +// Combines histograms by continuously choosing the one with the highest cost +// reduction. +static int HistogramCombineGreedy(VP8LHistogramSet* const image_histo) { + int ok = 0; + int image_histo_size = image_histo->size; + int i, j; + VP8LHistogram** const histograms = image_histo->histograms; + // Indexes of remaining histograms. + int* const clusters = + (int*)WebPSafeMalloc(image_histo_size, sizeof(*clusters)); + // Priority queue of histogram pairs. + HistoQueue histo_queue; + + if (!HistoQueueInit(&histo_queue, image_histo_size) || clusters == NULL) { + goto End; + } + + for (i = 0; i < image_histo_size; ++i) { + // Initialize clusters indexes. + clusters[i] = i; + for (j = i + 1; j < image_histo_size; ++j) { + // Initialize positions array. + PreparePair(histograms, i, j, &histo_queue.queue[histo_queue.size]); + UpdateQueueFront(&histo_queue); + } + } + + while (image_histo_size > 1 && histo_queue.size > 0) { + HistogramPair* copy_to; + const int idx1 = histo_queue.queue[0].idx1; + const int idx2 = histo_queue.queue[0].idx2; + HistogramAdd(histograms[idx2], histograms[idx1], histograms[idx1]); + histograms[idx1]->bit_cost_ = histo_queue.queue[0].cost_combo; + // Remove merged histogram. + for (i = 0; i + 1 < image_histo_size; ++i) { + if (clusters[i] >= idx2) { + clusters[i] = clusters[i + 1]; + } + } + --image_histo_size; + + // Remove pairs intersecting the just combined best pair. This will + // therefore pop the head of the queue. + copy_to = histo_queue.queue; + for (i = 0; i < histo_queue.size; ++i) { + HistogramPair* const p = histo_queue.queue + i; + if (p->idx1 == idx1 || p->idx2 == idx1 || + p->idx1 == idx2 || p->idx2 == idx2) { + // Do not copy the invalid pair. + continue; + } + if (p->cost_diff < histo_queue.queue[0].cost_diff) { + // Replace the top of the queue if we found better. + SwapHistogramPairs(histo_queue.queue, p); + } + SwapHistogramPairs(copy_to, p); + ++copy_to; + } + histo_queue.size = (int)(copy_to - histo_queue.queue); + + // Push new pairs formed with combined histogram to the queue. + for (i = 0; i < image_histo_size; ++i) { + if (clusters[i] != idx1) { + PreparePair(histograms, idx1, clusters[i], + &histo_queue.queue[histo_queue.size]); + UpdateQueueFront(&histo_queue); + } + } + } + // Move remaining histograms to the beginning of the array. + for (i = 0; i < image_histo_size; ++i) { + if (i != clusters[i]) { // swap the two histograms + HistogramSwap(&histograms[i], &histograms[clusters[i]]); + } + } + + image_histo->size = image_histo_size; + ok = 1; + + End: + WebPSafeFree(clusters); + HistoQueueClear(&histo_queue); + return ok; +} + +static void HistogramCombineStochastic(VP8LHistogramSet* const image_histo, + VP8LHistogram* tmp_histo, + VP8LHistogram* best_combo, + int quality, int min_cluster_size) { + int iter; + uint32_t seed = 0; + int tries_with_no_success = 0; + int image_histo_size = image_histo->size; + const int iter_mult = (quality < 25) ? 2 : 2 + (quality - 25) / 8; + const int outer_iters = image_histo_size * iter_mult; + const int num_pairs = image_histo_size / 2; + const int num_tries_no_success = outer_iters / 2; + int idx2_max = image_histo_size - 1; + int do_brute_dorce = 0; + VP8LHistogram** const histograms = image_histo->histograms; + + // Collapse similar histograms in 'image_histo'. + ++min_cluster_size; + for (iter = 0; + iter < outer_iters && image_histo_size >= min_cluster_size; + ++iter) { + double best_cost_diff = 0.; + int best_idx1 = -1, best_idx2 = 1; + int j; + int num_tries = + (num_pairs < image_histo_size) ? num_pairs : image_histo_size; + // Use a brute force approach if: + // - stochastic has not worked for a while and + // - if the number of iterations for brute force is less than the number of + // iterations if we never find a match ever again stochastically (hence + // num_tries times the number of remaining outer iterations). + do_brute_dorce = + (tries_with_no_success > 10) && + (idx2_max * (idx2_max + 1) < 2 * num_tries * (outer_iters - iter)); + if (do_brute_dorce) num_tries = idx2_max; + + seed += iter; + for (j = 0; j < num_tries; ++j) { + double curr_cost_diff; + // Choose two histograms at random and try to combine them. + uint32_t idx1, idx2; + if (do_brute_dorce) { + // Use a brute force approach. + idx1 = (uint32_t)j; + idx2 = (uint32_t)idx2_max; + } else { + const uint32_t tmp = (j & 7) + 1; + const uint32_t diff = + (tmp < 3) ? tmp : MyRand(&seed) % (image_histo_size - 1); + idx1 = MyRand(&seed) % image_histo_size; + idx2 = (idx1 + diff + 1) % image_histo_size; + if (idx1 == idx2) { + continue; + } + } + + // Calculate cost reduction on combining. + curr_cost_diff = HistogramAddEval(histograms[idx1], histograms[idx2], + tmp_histo, best_cost_diff); + if (curr_cost_diff < best_cost_diff) { // found a better pair? + HistogramSwap(&best_combo, &tmp_histo); + best_cost_diff = curr_cost_diff; + best_idx1 = idx1; + best_idx2 = idx2; + } + } + if (do_brute_dorce) --idx2_max; + + if (best_idx1 >= 0) { + HistogramSwap(&best_combo, &histograms[best_idx1]); + // swap best_idx2 slot with last one (which is now unused) + --image_histo_size; + if (idx2_max >= image_histo_size) idx2_max = image_histo_size - 1; + if (best_idx2 != image_histo_size) { + HistogramSwap(&histograms[image_histo_size], &histograms[best_idx2]); + histograms[image_histo_size] = NULL; + } + tries_with_no_success = 0; + } + if (++tries_with_no_success >= num_tries_no_success || idx2_max == 0) { + break; + } + } + image_histo->size = image_histo_size; +} + +// ----------------------------------------------------------------------------- +// Histogram refinement + +// Find the best 'out' histogram for each of the 'in' histograms. +// Note: we assume that out[]->bit_cost_ is already up-to-date. +static void HistogramRemap(const VP8LHistogramSet* const in, + const VP8LHistogramSet* const out, + uint16_t* const symbols) { + int i; + VP8LHistogram** const in_histo = in->histograms; + VP8LHistogram** const out_histo = out->histograms; + const int in_size = in->size; + const int out_size = out->size; + if (out_size > 1) { + for (i = 0; i < in_size; ++i) { + int best_out = 0; + double best_bits = MAX_COST; + int k; + for (k = 0; k < out_size; ++k) { + const double cur_bits = + HistogramAddThresh(out_histo[k], in_histo[i], best_bits); + if (k == 0 || cur_bits < best_bits) { + best_bits = cur_bits; + best_out = k; + } + } + symbols[i] = best_out; + } + } else { + assert(out_size == 1); + for (i = 0; i < in_size; ++i) { + symbols[i] = 0; + } + } + + // Recompute each out based on raw and symbols. + for (i = 0; i < out_size; ++i) { + HistogramClear(out_histo[i]); + } + + for (i = 0; i < in_size; ++i) { + const int idx = symbols[i]; + HistogramAdd(in_histo[i], out_histo[idx], out_histo[idx]); + } +} + +static double GetCombineCostFactor(int histo_size, int quality) { + double combine_cost_factor = 0.16; + if (quality < 90) { + if (histo_size > 256) combine_cost_factor /= 2.; + if (histo_size > 512) combine_cost_factor /= 2.; + if (histo_size > 1024) combine_cost_factor /= 2.; + if (quality <= 50) combine_cost_factor /= 2.; + } + return combine_cost_factor; +} + +int VP8LGetHistoImageSymbols(int xsize, int ysize, + const VP8LBackwardRefs* const refs, + int quality, int low_effort, + int histo_bits, int cache_bits, + VP8LHistogramSet* const image_histo, + VP8LHistogramSet* const tmp_histos, + uint16_t* const histogram_symbols) { + int ok = 0; + const int histo_xsize = histo_bits ? VP8LSubSampleSize(xsize, histo_bits) : 1; + const int histo_ysize = histo_bits ? VP8LSubSampleSize(ysize, histo_bits) : 1; + const int image_histo_raw_size = histo_xsize * histo_ysize; + VP8LHistogramSet* const orig_histo = + VP8LAllocateHistogramSet(image_histo_raw_size, cache_bits); + VP8LHistogram* cur_combo; + // Don't attempt linear bin-partition heuristic for + // histograms of small sizes (as bin_map will be very sparse) and + // maximum quality q==100 (to preserve the compression gains at that level). + const int entropy_combine_num_bins = low_effort ? NUM_PARTITIONS : BIN_SIZE; + const int entropy_combine = + (orig_histo->size > entropy_combine_num_bins * 2) && (quality < 100); + + if (orig_histo == NULL) goto Error; + + // Construct the histograms from backward references. + HistogramBuild(xsize, histo_bits, refs, orig_histo); + // Copies the histograms and computes its bit_cost. + HistogramCopyAndAnalyze(orig_histo, image_histo); + + cur_combo = tmp_histos->histograms[1]; // pick up working slot + if (entropy_combine) { + const int bin_map_size = orig_histo->size; + // Reuse histogram_symbols storage. By definition, it's guaranteed to be ok. + uint16_t* const bin_map = histogram_symbols; + const double combine_cost_factor = + GetCombineCostFactor(image_histo_raw_size, quality); + + HistogramAnalyzeEntropyBin(orig_histo, bin_map, low_effort); + // Collapse histograms with similar entropy. + cur_combo = HistogramCombineEntropyBin(image_histo, cur_combo, + bin_map, bin_map_size, + entropy_combine_num_bins, + combine_cost_factor, low_effort); + } + + // Don't combine the histograms using stochastic and greedy heuristics for + // low-effort compression mode. + if (!low_effort || !entropy_combine) { + const float x = quality / 100.f; + // cubic ramp between 1 and MAX_HISTO_GREEDY: + const int threshold_size = (int)(1 + (x * x * x) * (MAX_HISTO_GREEDY - 1)); + HistogramCombineStochastic(image_histo, tmp_histos->histograms[0], + cur_combo, quality, threshold_size); + if ((image_histo->size <= threshold_size) && + !HistogramCombineGreedy(image_histo)) { + goto Error; + } + } + + // TODO(vikasa): Optimize HistogramRemap for low-effort compression mode also. + // Find the optimal map from original histograms to the final ones. + HistogramRemap(orig_histo, image_histo, histogram_symbols); + + ok = 1; + + Error: + VP8LFreeHistogramSet(orig_histo); + return ok; +} diff --git a/thirdparty/libwebp/enc/histogram_enc.h b/thirdparty/libwebp/enc/histogram_enc.h new file mode 100644 index 0000000000..a9d258a166 --- /dev/null +++ b/thirdparty/libwebp/enc/histogram_enc.h @@ -0,0 +1,123 @@ +// Copyright 2012 Google Inc. All Rights Reserved. +// +// Use of this source code is governed by a BSD-style license +// that can be found in the COPYING file in the root of the source +// tree. An additional intellectual property rights grant can be found +// in the file PATENTS. All contributing project authors may +// be found in the AUTHORS file in the root of the source tree. +// ----------------------------------------------------------------------------- +// +// Author: Jyrki Alakuijala (jyrki@google.com) +// +// Models the histograms of literal and distance codes. + +#ifndef WEBP_ENC_HISTOGRAM_H_ +#define WEBP_ENC_HISTOGRAM_H_ + +#include + +#include "./backward_references_enc.h" +#include "../webp/format_constants.h" +#include "../webp/types.h" + +#ifdef __cplusplus +extern "C" { +#endif + +// Not a trivial literal symbol. +#define VP8L_NON_TRIVIAL_SYM (0xffffffff) + +// A simple container for histograms of data. +typedef struct { + // literal_ contains green literal, palette-code and + // copy-length-prefix histogram + uint32_t* literal_; // Pointer to the allocated buffer for literal. + uint32_t red_[NUM_LITERAL_CODES]; + uint32_t blue_[NUM_LITERAL_CODES]; + uint32_t alpha_[NUM_LITERAL_CODES]; + // Backward reference prefix-code histogram. + uint32_t distance_[NUM_DISTANCE_CODES]; + int palette_code_bits_; + uint32_t trivial_symbol_; // True, if histograms for Red, Blue & Alpha + // literal symbols are single valued. + double bit_cost_; // cached value of bit cost. + double literal_cost_; // Cached values of dominant entropy costs: + double red_cost_; // literal, red & blue. + double blue_cost_; +} VP8LHistogram; + +// Collection of histograms with fixed capacity, allocated as one +// big memory chunk. Can be destroyed by calling WebPSafeFree(). +typedef struct { + int size; // number of slots currently in use + int max_size; // maximum capacity + VP8LHistogram** histograms; +} VP8LHistogramSet; + +// Create the histogram. +// +// The input data is the PixOrCopy data, which models the literals, stop +// codes and backward references (both distances and lengths). Also: if +// palette_code_bits is >= 0, initialize the histogram with this value. +void VP8LHistogramCreate(VP8LHistogram* const p, + const VP8LBackwardRefs* const refs, + int palette_code_bits); + +// Return the size of the histogram for a given palette_code_bits. +int VP8LGetHistogramSize(int palette_code_bits); + +// Set the palette_code_bits and reset the stats. +void VP8LHistogramInit(VP8LHistogram* const p, int palette_code_bits); + +// Collect all the references into a histogram (without reset) +void VP8LHistogramStoreRefs(const VP8LBackwardRefs* const refs, + VP8LHistogram* const histo); + +// Free the memory allocated for the histogram. +void VP8LFreeHistogram(VP8LHistogram* const histo); + +// Free the memory allocated for the histogram set. +void VP8LFreeHistogramSet(VP8LHistogramSet* const histo); + +// Allocate an array of pointer to histograms, allocated and initialized +// using 'cache_bits'. Return NULL in case of memory error. +VP8LHistogramSet* VP8LAllocateHistogramSet(int size, int cache_bits); + +// Allocate and initialize histogram object with specified 'cache_bits'. +// Returns NULL in case of memory error. +// Special case of VP8LAllocateHistogramSet, with size equals 1. +VP8LHistogram* VP8LAllocateHistogram(int cache_bits); + +// Accumulate a token 'v' into a histogram. +void VP8LHistogramAddSinglePixOrCopy(VP8LHistogram* const histo, + const PixOrCopy* const v); + +static WEBP_INLINE int VP8LHistogramNumCodes(int palette_code_bits) { + return NUM_LITERAL_CODES + NUM_LENGTH_CODES + + ((palette_code_bits > 0) ? (1 << palette_code_bits) : 0); +} + +// Builds the histogram image. +int VP8LGetHistoImageSymbols(int xsize, int ysize, + const VP8LBackwardRefs* const refs, + int quality, int low_effort, + int histogram_bits, int cache_bits, + VP8LHistogramSet* const image_in, + VP8LHistogramSet* const tmp_histos, + uint16_t* const histogram_symbols); + +// Returns the entropy for the symbols in the input array. +// Also sets trivial_symbol to the code value, if the array has only one code +// value. Otherwise, set it to VP8L_NON_TRIVIAL_SYM. +double VP8LBitsEntropy(const uint32_t* const array, int n, + uint32_t* const trivial_symbol); + +// Estimate how many bits the combined entropy of literals and distance +// approximately maps to. +double VP8LHistogramEstimateBits(const VP8LHistogram* const p); + +#ifdef __cplusplus +} +#endif + +#endif // WEBP_ENC_HISTOGRAM_H_ diff --git a/thirdparty/libwebp/enc/iterator.c b/thirdparty/libwebp/enc/iterator.c deleted file mode 100644 index 99d960a547..0000000000 --- a/thirdparty/libwebp/enc/iterator.c +++ /dev/null @@ -1,456 +0,0 @@ -// Copyright 2011 Google Inc. All Rights Reserved. -// -// Use of this source code is governed by a BSD-style license -// that can be found in the COPYING file in the root of the source -// tree. An additional intellectual property rights grant can be found -// in the file PATENTS. All contributing project authors may -// be found in the AUTHORS file in the root of the source tree. -// ----------------------------------------------------------------------------- -// -// VP8Iterator: block iterator -// -// Author: Skal (pascal.massimino@gmail.com) - -#include - -#include "./vp8enci.h" - -//------------------------------------------------------------------------------ -// VP8Iterator -//------------------------------------------------------------------------------ - -static void InitLeft(VP8EncIterator* const it) { - it->y_left_[-1] = it->u_left_[-1] = it->v_left_[-1] = - (it->y_ > 0) ? 129 : 127; - memset(it->y_left_, 129, 16); - memset(it->u_left_, 129, 8); - memset(it->v_left_, 129, 8); - it->left_nz_[8] = 0; -} - -static void InitTop(VP8EncIterator* const it) { - const VP8Encoder* const enc = it->enc_; - const size_t top_size = enc->mb_w_ * 16; - memset(enc->y_top_, 127, 2 * top_size); - memset(enc->nz_, 0, enc->mb_w_ * sizeof(*enc->nz_)); -} - -void VP8IteratorSetRow(VP8EncIterator* const it, int y) { - VP8Encoder* const enc = it->enc_; - it->x_ = 0; - it->y_ = y; - it->bw_ = &enc->parts_[y & (enc->num_parts_ - 1)]; - it->preds_ = enc->preds_ + y * 4 * enc->preds_w_; - it->nz_ = enc->nz_; - it->mb_ = enc->mb_info_ + y * enc->mb_w_; - it->y_top_ = enc->y_top_; - it->uv_top_ = enc->uv_top_; - InitLeft(it); -} - -void VP8IteratorReset(VP8EncIterator* const it) { - VP8Encoder* const enc = it->enc_; - VP8IteratorSetRow(it, 0); - VP8IteratorSetCountDown(it, enc->mb_w_ * enc->mb_h_); // default - InitTop(it); - InitLeft(it); - memset(it->bit_count_, 0, sizeof(it->bit_count_)); - it->do_trellis_ = 0; -} - -void VP8IteratorSetCountDown(VP8EncIterator* const it, int count_down) { - it->count_down_ = it->count_down0_ = count_down; -} - -int VP8IteratorIsDone(const VP8EncIterator* const it) { - return (it->count_down_ <= 0); -} - -void VP8IteratorInit(VP8Encoder* const enc, VP8EncIterator* const it) { - it->enc_ = enc; - it->y_stride_ = enc->pic_->y_stride; - it->uv_stride_ = enc->pic_->uv_stride; - it->yuv_in_ = (uint8_t*)WEBP_ALIGN(it->yuv_mem_); - it->yuv_out_ = it->yuv_in_ + YUV_SIZE_ENC; - it->yuv_out2_ = it->yuv_out_ + YUV_SIZE_ENC; - it->yuv_p_ = it->yuv_out2_ + YUV_SIZE_ENC; - it->lf_stats_ = enc->lf_stats_; - it->percent0_ = enc->percent_; - it->y_left_ = (uint8_t*)WEBP_ALIGN(it->yuv_left_mem_ + 1); - it->u_left_ = it->y_left_ + 16 + 16; - it->v_left_ = it->u_left_ + 16; - VP8IteratorReset(it); -} - -int VP8IteratorProgress(const VP8EncIterator* const it, int delta) { - VP8Encoder* const enc = it->enc_; - if (delta && enc->pic_->progress_hook != NULL) { - const int done = it->count_down0_ - it->count_down_; - const int percent = (it->count_down0_ <= 0) - ? it->percent0_ - : it->percent0_ + delta * done / it->count_down0_; - return WebPReportProgress(enc->pic_, percent, &enc->percent_); - } - return 1; -} - -//------------------------------------------------------------------------------ -// Import the source samples into the cache. Takes care of replicating -// boundary pixels if necessary. - -static WEBP_INLINE int MinSize(int a, int b) { return (a < b) ? a : b; } - -static void ImportBlock(const uint8_t* src, int src_stride, - uint8_t* dst, int w, int h, int size) { - int i; - for (i = 0; i < h; ++i) { - memcpy(dst, src, w); - if (w < size) { - memset(dst + w, dst[w - 1], size - w); - } - dst += BPS; - src += src_stride; - } - for (i = h; i < size; ++i) { - memcpy(dst, dst - BPS, size); - dst += BPS; - } -} - -static void ImportLine(const uint8_t* src, int src_stride, - uint8_t* dst, int len, int total_len) { - int i; - for (i = 0; i < len; ++i, src += src_stride) dst[i] = *src; - for (; i < total_len; ++i) dst[i] = dst[len - 1]; -} - -void VP8IteratorImport(VP8EncIterator* const it, uint8_t* tmp_32) { - const VP8Encoder* const enc = it->enc_; - const int x = it->x_, y = it->y_; - const WebPPicture* const pic = enc->pic_; - const uint8_t* const ysrc = pic->y + (y * pic->y_stride + x) * 16; - const uint8_t* const usrc = pic->u + (y * pic->uv_stride + x) * 8; - const uint8_t* const vsrc = pic->v + (y * pic->uv_stride + x) * 8; - const int w = MinSize(pic->width - x * 16, 16); - const int h = MinSize(pic->height - y * 16, 16); - const int uv_w = (w + 1) >> 1; - const int uv_h = (h + 1) >> 1; - - ImportBlock(ysrc, pic->y_stride, it->yuv_in_ + Y_OFF_ENC, w, h, 16); - ImportBlock(usrc, pic->uv_stride, it->yuv_in_ + U_OFF_ENC, uv_w, uv_h, 8); - ImportBlock(vsrc, pic->uv_stride, it->yuv_in_ + V_OFF_ENC, uv_w, uv_h, 8); - - if (tmp_32 == NULL) return; - - // Import source (uncompressed) samples into boundary. - if (x == 0) { - InitLeft(it); - } else { - if (y == 0) { - it->y_left_[-1] = it->u_left_[-1] = it->v_left_[-1] = 127; - } else { - it->y_left_[-1] = ysrc[- 1 - pic->y_stride]; - it->u_left_[-1] = usrc[- 1 - pic->uv_stride]; - it->v_left_[-1] = vsrc[- 1 - pic->uv_stride]; - } - ImportLine(ysrc - 1, pic->y_stride, it->y_left_, h, 16); - ImportLine(usrc - 1, pic->uv_stride, it->u_left_, uv_h, 8); - ImportLine(vsrc - 1, pic->uv_stride, it->v_left_, uv_h, 8); - } - - it->y_top_ = tmp_32 + 0; - it->uv_top_ = tmp_32 + 16; - if (y == 0) { - memset(tmp_32, 127, 32 * sizeof(*tmp_32)); - } else { - ImportLine(ysrc - pic->y_stride, 1, tmp_32, w, 16); - ImportLine(usrc - pic->uv_stride, 1, tmp_32 + 16, uv_w, 8); - ImportLine(vsrc - pic->uv_stride, 1, tmp_32 + 16 + 8, uv_w, 8); - } -} - -//------------------------------------------------------------------------------ -// Copy back the compressed samples into user space if requested. - -static void ExportBlock(const uint8_t* src, uint8_t* dst, int dst_stride, - int w, int h) { - while (h-- > 0) { - memcpy(dst, src, w); - dst += dst_stride; - src += BPS; - } -} - -void VP8IteratorExport(const VP8EncIterator* const it) { - const VP8Encoder* const enc = it->enc_; - if (enc->config_->show_compressed) { - const int x = it->x_, y = it->y_; - const uint8_t* const ysrc = it->yuv_out_ + Y_OFF_ENC; - const uint8_t* const usrc = it->yuv_out_ + U_OFF_ENC; - const uint8_t* const vsrc = it->yuv_out_ + V_OFF_ENC; - const WebPPicture* const pic = enc->pic_; - uint8_t* const ydst = pic->y + (y * pic->y_stride + x) * 16; - uint8_t* const udst = pic->u + (y * pic->uv_stride + x) * 8; - uint8_t* const vdst = pic->v + (y * pic->uv_stride + x) * 8; - int w = (pic->width - x * 16); - int h = (pic->height - y * 16); - - if (w > 16) w = 16; - if (h > 16) h = 16; - - // Luma plane - ExportBlock(ysrc, ydst, pic->y_stride, w, h); - - { // U/V planes - const int uv_w = (w + 1) >> 1; - const int uv_h = (h + 1) >> 1; - ExportBlock(usrc, udst, pic->uv_stride, uv_w, uv_h); - ExportBlock(vsrc, vdst, pic->uv_stride, uv_w, uv_h); - } - } -} - -//------------------------------------------------------------------------------ -// Non-zero contexts setup/teardown - -// Nz bits: -// 0 1 2 3 Y -// 4 5 6 7 -// 8 9 10 11 -// 12 13 14 15 -// 16 17 U -// 18 19 -// 20 21 V -// 22 23 -// 24 DC-intra16 - -// Convert packed context to byte array -#define BIT(nz, n) (!!((nz) & (1 << (n)))) - -void VP8IteratorNzToBytes(VP8EncIterator* const it) { - const int tnz = it->nz_[0], lnz = it->nz_[-1]; - int* const top_nz = it->top_nz_; - int* const left_nz = it->left_nz_; - - // Top-Y - top_nz[0] = BIT(tnz, 12); - top_nz[1] = BIT(tnz, 13); - top_nz[2] = BIT(tnz, 14); - top_nz[3] = BIT(tnz, 15); - // Top-U - top_nz[4] = BIT(tnz, 18); - top_nz[5] = BIT(tnz, 19); - // Top-V - top_nz[6] = BIT(tnz, 22); - top_nz[7] = BIT(tnz, 23); - // DC - top_nz[8] = BIT(tnz, 24); - - // left-Y - left_nz[0] = BIT(lnz, 3); - left_nz[1] = BIT(lnz, 7); - left_nz[2] = BIT(lnz, 11); - left_nz[3] = BIT(lnz, 15); - // left-U - left_nz[4] = BIT(lnz, 17); - left_nz[5] = BIT(lnz, 19); - // left-V - left_nz[6] = BIT(lnz, 21); - left_nz[7] = BIT(lnz, 23); - // left-DC is special, iterated separately -} - -void VP8IteratorBytesToNz(VP8EncIterator* const it) { - uint32_t nz = 0; - const int* const top_nz = it->top_nz_; - const int* const left_nz = it->left_nz_; - // top - nz |= (top_nz[0] << 12) | (top_nz[1] << 13); - nz |= (top_nz[2] << 14) | (top_nz[3] << 15); - nz |= (top_nz[4] << 18) | (top_nz[5] << 19); - nz |= (top_nz[6] << 22) | (top_nz[7] << 23); - nz |= (top_nz[8] << 24); // we propagate the _top_ bit, esp. for intra4 - // left - nz |= (left_nz[0] << 3) | (left_nz[1] << 7); - nz |= (left_nz[2] << 11); - nz |= (left_nz[4] << 17) | (left_nz[6] << 21); - - *it->nz_ = nz; -} - -#undef BIT - -//------------------------------------------------------------------------------ -// Advance to the next position, doing the bookkeeping. - -void VP8IteratorSaveBoundary(VP8EncIterator* const it) { - VP8Encoder* const enc = it->enc_; - const int x = it->x_, y = it->y_; - const uint8_t* const ysrc = it->yuv_out_ + Y_OFF_ENC; - const uint8_t* const uvsrc = it->yuv_out_ + U_OFF_ENC; - if (x < enc->mb_w_ - 1) { // left - int i; - for (i = 0; i < 16; ++i) { - it->y_left_[i] = ysrc[15 + i * BPS]; - } - for (i = 0; i < 8; ++i) { - it->u_left_[i] = uvsrc[7 + i * BPS]; - it->v_left_[i] = uvsrc[15 + i * BPS]; - } - // top-left (before 'top'!) - it->y_left_[-1] = it->y_top_[15]; - it->u_left_[-1] = it->uv_top_[0 + 7]; - it->v_left_[-1] = it->uv_top_[8 + 7]; - } - if (y < enc->mb_h_ - 1) { // top - memcpy(it->y_top_, ysrc + 15 * BPS, 16); - memcpy(it->uv_top_, uvsrc + 7 * BPS, 8 + 8); - } -} - -int VP8IteratorNext(VP8EncIterator* const it) { - it->preds_ += 4; - it->mb_ += 1; - it->nz_ += 1; - it->y_top_ += 16; - it->uv_top_ += 16; - it->x_ += 1; - if (it->x_ == it->enc_->mb_w_) { - VP8IteratorSetRow(it, ++it->y_); - } - return (0 < --it->count_down_); -} - -//------------------------------------------------------------------------------ -// Helper function to set mode properties - -void VP8SetIntra16Mode(const VP8EncIterator* const it, int mode) { - uint8_t* preds = it->preds_; - int y; - for (y = 0; y < 4; ++y) { - memset(preds, mode, 4); - preds += it->enc_->preds_w_; - } - it->mb_->type_ = 1; -} - -void VP8SetIntra4Mode(const VP8EncIterator* const it, const uint8_t* modes) { - uint8_t* preds = it->preds_; - int y; - for (y = 4; y > 0; --y) { - memcpy(preds, modes, 4 * sizeof(*modes)); - preds += it->enc_->preds_w_; - modes += 4; - } - it->mb_->type_ = 0; -} - -void VP8SetIntraUVMode(const VP8EncIterator* const it, int mode) { - it->mb_->uv_mode_ = mode; -} - -void VP8SetSkip(const VP8EncIterator* const it, int skip) { - it->mb_->skip_ = skip; -} - -void VP8SetSegment(const VP8EncIterator* const it, int segment) { - it->mb_->segment_ = segment; -} - -//------------------------------------------------------------------------------ -// Intra4x4 sub-blocks iteration -// -// We store and update the boundary samples into an array of 37 pixels. They -// are updated as we iterate and reconstructs each intra4x4 blocks in turn. -// The position of the samples has the following snake pattern: -// -// 16|17 18 19 20|21 22 23 24|25 26 27 28|29 30 31 32|33 34 35 36 <- Top-right -// --+-----------+-----------+-----------+-----------+ -// 15| 19| 23| 27| 31| -// 14| 18| 22| 26| 30| -// 13| 17| 21| 25| 29| -// 12|13 14 15 16|17 18 19 20|21 22 23 24|25 26 27 28| -// --+-----------+-----------+-----------+-----------+ -// 11| 15| 19| 23| 27| -// 10| 14| 18| 22| 26| -// 9| 13| 17| 21| 25| -// 8| 9 10 11 12|13 14 15 16|17 18 19 20|21 22 23 24| -// --+-----------+-----------+-----------+-----------+ -// 7| 11| 15| 19| 23| -// 6| 10| 14| 18| 22| -// 5| 9| 13| 17| 21| -// 4| 5 6 7 8| 9 10 11 12|13 14 15 16|17 18 19 20| -// --+-----------+-----------+-----------+-----------+ -// 3| 7| 11| 15| 19| -// 2| 6| 10| 14| 18| -// 1| 5| 9| 13| 17| -// 0| 1 2 3 4| 5 6 7 8| 9 10 11 12|13 14 15 16| -// --+-----------+-----------+-----------+-----------+ - -// Array to record the position of the top sample to pass to the prediction -// functions in dsp.c. -static const uint8_t VP8TopLeftI4[16] = { - 17, 21, 25, 29, - 13, 17, 21, 25, - 9, 13, 17, 21, - 5, 9, 13, 17 -}; - -void VP8IteratorStartI4(VP8EncIterator* const it) { - const VP8Encoder* const enc = it->enc_; - int i; - - it->i4_ = 0; // first 4x4 sub-block - it->i4_top_ = it->i4_boundary_ + VP8TopLeftI4[0]; - - // Import the boundary samples - for (i = 0; i < 17; ++i) { // left - it->i4_boundary_[i] = it->y_left_[15 - i]; - } - for (i = 0; i < 16; ++i) { // top - it->i4_boundary_[17 + i] = it->y_top_[i]; - } - // top-right samples have a special case on the far right of the picture - if (it->x_ < enc->mb_w_ - 1) { - for (i = 16; i < 16 + 4; ++i) { - it->i4_boundary_[17 + i] = it->y_top_[i]; - } - } else { // else, replicate the last valid pixel four times - for (i = 16; i < 16 + 4; ++i) { - it->i4_boundary_[17 + i] = it->i4_boundary_[17 + 15]; - } - } - VP8IteratorNzToBytes(it); // import the non-zero context -} - -int VP8IteratorRotateI4(VP8EncIterator* const it, - const uint8_t* const yuv_out) { - const uint8_t* const blk = yuv_out + VP8Scan[it->i4_]; - uint8_t* const top = it->i4_top_; - int i; - - // Update the cache with 7 fresh samples - for (i = 0; i <= 3; ++i) { - top[-4 + i] = blk[i + 3 * BPS]; // store future top samples - } - if ((it->i4_ & 3) != 3) { // if not on the right sub-blocks #3, #7, #11, #15 - for (i = 0; i <= 2; ++i) { // store future left samples - top[i] = blk[3 + (2 - i) * BPS]; - } - } else { // else replicate top-right samples, as says the specs. - for (i = 0; i <= 3; ++i) { - top[i] = top[i + 4]; - } - } - // move pointers to next sub-block - ++it->i4_; - if (it->i4_ == 16) { // we're done - return 0; - } - - it->i4_top_ = it->i4_boundary_ + VP8TopLeftI4[it->i4_]; - return 1; -} - -//------------------------------------------------------------------------------ - diff --git a/thirdparty/libwebp/enc/iterator_enc.c b/thirdparty/libwebp/enc/iterator_enc.c new file mode 100644 index 0000000000..e48d30bd31 --- /dev/null +++ b/thirdparty/libwebp/enc/iterator_enc.c @@ -0,0 +1,453 @@ +// Copyright 2011 Google Inc. All Rights Reserved. +// +// Use of this source code is governed by a BSD-style license +// that can be found in the COPYING file in the root of the source +// tree. An additional intellectual property rights grant can be found +// in the file PATENTS. All contributing project authors may +// be found in the AUTHORS file in the root of the source tree. +// ----------------------------------------------------------------------------- +// +// VP8Iterator: block iterator +// +// Author: Skal (pascal.massimino@gmail.com) + +#include + +#include "./vp8i_enc.h" + +//------------------------------------------------------------------------------ +// VP8Iterator +//------------------------------------------------------------------------------ + +static void InitLeft(VP8EncIterator* const it) { + it->y_left_[-1] = it->u_left_[-1] = it->v_left_[-1] = + (it->y_ > 0) ? 129 : 127; + memset(it->y_left_, 129, 16); + memset(it->u_left_, 129, 8); + memset(it->v_left_, 129, 8); + it->left_nz_[8] = 0; +} + +static void InitTop(VP8EncIterator* const it) { + const VP8Encoder* const enc = it->enc_; + const size_t top_size = enc->mb_w_ * 16; + memset(enc->y_top_, 127, 2 * top_size); + memset(enc->nz_, 0, enc->mb_w_ * sizeof(*enc->nz_)); +} + +void VP8IteratorSetRow(VP8EncIterator* const it, int y) { + VP8Encoder* const enc = it->enc_; + it->x_ = 0; + it->y_ = y; + it->bw_ = &enc->parts_[y & (enc->num_parts_ - 1)]; + it->preds_ = enc->preds_ + y * 4 * enc->preds_w_; + it->nz_ = enc->nz_; + it->mb_ = enc->mb_info_ + y * enc->mb_w_; + it->y_top_ = enc->y_top_; + it->uv_top_ = enc->uv_top_; + InitLeft(it); +} + +void VP8IteratorReset(VP8EncIterator* const it) { + VP8Encoder* const enc = it->enc_; + VP8IteratorSetRow(it, 0); + VP8IteratorSetCountDown(it, enc->mb_w_ * enc->mb_h_); // default + InitTop(it); + memset(it->bit_count_, 0, sizeof(it->bit_count_)); + it->do_trellis_ = 0; +} + +void VP8IteratorSetCountDown(VP8EncIterator* const it, int count_down) { + it->count_down_ = it->count_down0_ = count_down; +} + +int VP8IteratorIsDone(const VP8EncIterator* const it) { + return (it->count_down_ <= 0); +} + +void VP8IteratorInit(VP8Encoder* const enc, VP8EncIterator* const it) { + it->enc_ = enc; + it->yuv_in_ = (uint8_t*)WEBP_ALIGN(it->yuv_mem_); + it->yuv_out_ = it->yuv_in_ + YUV_SIZE_ENC; + it->yuv_out2_ = it->yuv_out_ + YUV_SIZE_ENC; + it->yuv_p_ = it->yuv_out2_ + YUV_SIZE_ENC; + it->lf_stats_ = enc->lf_stats_; + it->percent0_ = enc->percent_; + it->y_left_ = (uint8_t*)WEBP_ALIGN(it->yuv_left_mem_ + 1); + it->u_left_ = it->y_left_ + 16 + 16; + it->v_left_ = it->u_left_ + 16; + VP8IteratorReset(it); +} + +int VP8IteratorProgress(const VP8EncIterator* const it, int delta) { + VP8Encoder* const enc = it->enc_; + if (delta && enc->pic_->progress_hook != NULL) { + const int done = it->count_down0_ - it->count_down_; + const int percent = (it->count_down0_ <= 0) + ? it->percent0_ + : it->percent0_ + delta * done / it->count_down0_; + return WebPReportProgress(enc->pic_, percent, &enc->percent_); + } + return 1; +} + +//------------------------------------------------------------------------------ +// Import the source samples into the cache. Takes care of replicating +// boundary pixels if necessary. + +static WEBP_INLINE int MinSize(int a, int b) { return (a < b) ? a : b; } + +static void ImportBlock(const uint8_t* src, int src_stride, + uint8_t* dst, int w, int h, int size) { + int i; + for (i = 0; i < h; ++i) { + memcpy(dst, src, w); + if (w < size) { + memset(dst + w, dst[w - 1], size - w); + } + dst += BPS; + src += src_stride; + } + for (i = h; i < size; ++i) { + memcpy(dst, dst - BPS, size); + dst += BPS; + } +} + +static void ImportLine(const uint8_t* src, int src_stride, + uint8_t* dst, int len, int total_len) { + int i; + for (i = 0; i < len; ++i, src += src_stride) dst[i] = *src; + for (; i < total_len; ++i) dst[i] = dst[len - 1]; +} + +void VP8IteratorImport(VP8EncIterator* const it, uint8_t* tmp_32) { + const VP8Encoder* const enc = it->enc_; + const int x = it->x_, y = it->y_; + const WebPPicture* const pic = enc->pic_; + const uint8_t* const ysrc = pic->y + (y * pic->y_stride + x) * 16; + const uint8_t* const usrc = pic->u + (y * pic->uv_stride + x) * 8; + const uint8_t* const vsrc = pic->v + (y * pic->uv_stride + x) * 8; + const int w = MinSize(pic->width - x * 16, 16); + const int h = MinSize(pic->height - y * 16, 16); + const int uv_w = (w + 1) >> 1; + const int uv_h = (h + 1) >> 1; + + ImportBlock(ysrc, pic->y_stride, it->yuv_in_ + Y_OFF_ENC, w, h, 16); + ImportBlock(usrc, pic->uv_stride, it->yuv_in_ + U_OFF_ENC, uv_w, uv_h, 8); + ImportBlock(vsrc, pic->uv_stride, it->yuv_in_ + V_OFF_ENC, uv_w, uv_h, 8); + + if (tmp_32 == NULL) return; + + // Import source (uncompressed) samples into boundary. + if (x == 0) { + InitLeft(it); + } else { + if (y == 0) { + it->y_left_[-1] = it->u_left_[-1] = it->v_left_[-1] = 127; + } else { + it->y_left_[-1] = ysrc[- 1 - pic->y_stride]; + it->u_left_[-1] = usrc[- 1 - pic->uv_stride]; + it->v_left_[-1] = vsrc[- 1 - pic->uv_stride]; + } + ImportLine(ysrc - 1, pic->y_stride, it->y_left_, h, 16); + ImportLine(usrc - 1, pic->uv_stride, it->u_left_, uv_h, 8); + ImportLine(vsrc - 1, pic->uv_stride, it->v_left_, uv_h, 8); + } + + it->y_top_ = tmp_32 + 0; + it->uv_top_ = tmp_32 + 16; + if (y == 0) { + memset(tmp_32, 127, 32 * sizeof(*tmp_32)); + } else { + ImportLine(ysrc - pic->y_stride, 1, tmp_32, w, 16); + ImportLine(usrc - pic->uv_stride, 1, tmp_32 + 16, uv_w, 8); + ImportLine(vsrc - pic->uv_stride, 1, tmp_32 + 16 + 8, uv_w, 8); + } +} + +//------------------------------------------------------------------------------ +// Copy back the compressed samples into user space if requested. + +static void ExportBlock(const uint8_t* src, uint8_t* dst, int dst_stride, + int w, int h) { + while (h-- > 0) { + memcpy(dst, src, w); + dst += dst_stride; + src += BPS; + } +} + +void VP8IteratorExport(const VP8EncIterator* const it) { + const VP8Encoder* const enc = it->enc_; + if (enc->config_->show_compressed) { + const int x = it->x_, y = it->y_; + const uint8_t* const ysrc = it->yuv_out_ + Y_OFF_ENC; + const uint8_t* const usrc = it->yuv_out_ + U_OFF_ENC; + const uint8_t* const vsrc = it->yuv_out_ + V_OFF_ENC; + const WebPPicture* const pic = enc->pic_; + uint8_t* const ydst = pic->y + (y * pic->y_stride + x) * 16; + uint8_t* const udst = pic->u + (y * pic->uv_stride + x) * 8; + uint8_t* const vdst = pic->v + (y * pic->uv_stride + x) * 8; + int w = (pic->width - x * 16); + int h = (pic->height - y * 16); + + if (w > 16) w = 16; + if (h > 16) h = 16; + + // Luma plane + ExportBlock(ysrc, ydst, pic->y_stride, w, h); + + { // U/V planes + const int uv_w = (w + 1) >> 1; + const int uv_h = (h + 1) >> 1; + ExportBlock(usrc, udst, pic->uv_stride, uv_w, uv_h); + ExportBlock(vsrc, vdst, pic->uv_stride, uv_w, uv_h); + } + } +} + +//------------------------------------------------------------------------------ +// Non-zero contexts setup/teardown + +// Nz bits: +// 0 1 2 3 Y +// 4 5 6 7 +// 8 9 10 11 +// 12 13 14 15 +// 16 17 U +// 18 19 +// 20 21 V +// 22 23 +// 24 DC-intra16 + +// Convert packed context to byte array +#define BIT(nz, n) (!!((nz) & (1 << (n)))) + +void VP8IteratorNzToBytes(VP8EncIterator* const it) { + const int tnz = it->nz_[0], lnz = it->nz_[-1]; + int* const top_nz = it->top_nz_; + int* const left_nz = it->left_nz_; + + // Top-Y + top_nz[0] = BIT(tnz, 12); + top_nz[1] = BIT(tnz, 13); + top_nz[2] = BIT(tnz, 14); + top_nz[3] = BIT(tnz, 15); + // Top-U + top_nz[4] = BIT(tnz, 18); + top_nz[5] = BIT(tnz, 19); + // Top-V + top_nz[6] = BIT(tnz, 22); + top_nz[7] = BIT(tnz, 23); + // DC + top_nz[8] = BIT(tnz, 24); + + // left-Y + left_nz[0] = BIT(lnz, 3); + left_nz[1] = BIT(lnz, 7); + left_nz[2] = BIT(lnz, 11); + left_nz[3] = BIT(lnz, 15); + // left-U + left_nz[4] = BIT(lnz, 17); + left_nz[5] = BIT(lnz, 19); + // left-V + left_nz[6] = BIT(lnz, 21); + left_nz[7] = BIT(lnz, 23); + // left-DC is special, iterated separately +} + +void VP8IteratorBytesToNz(VP8EncIterator* const it) { + uint32_t nz = 0; + const int* const top_nz = it->top_nz_; + const int* const left_nz = it->left_nz_; + // top + nz |= (top_nz[0] << 12) | (top_nz[1] << 13); + nz |= (top_nz[2] << 14) | (top_nz[3] << 15); + nz |= (top_nz[4] << 18) | (top_nz[5] << 19); + nz |= (top_nz[6] << 22) | (top_nz[7] << 23); + nz |= (top_nz[8] << 24); // we propagate the _top_ bit, esp. for intra4 + // left + nz |= (left_nz[0] << 3) | (left_nz[1] << 7); + nz |= (left_nz[2] << 11); + nz |= (left_nz[4] << 17) | (left_nz[6] << 21); + + *it->nz_ = nz; +} + +#undef BIT + +//------------------------------------------------------------------------------ +// Advance to the next position, doing the bookkeeping. + +void VP8IteratorSaveBoundary(VP8EncIterator* const it) { + VP8Encoder* const enc = it->enc_; + const int x = it->x_, y = it->y_; + const uint8_t* const ysrc = it->yuv_out_ + Y_OFF_ENC; + const uint8_t* const uvsrc = it->yuv_out_ + U_OFF_ENC; + if (x < enc->mb_w_ - 1) { // left + int i; + for (i = 0; i < 16; ++i) { + it->y_left_[i] = ysrc[15 + i * BPS]; + } + for (i = 0; i < 8; ++i) { + it->u_left_[i] = uvsrc[7 + i * BPS]; + it->v_left_[i] = uvsrc[15 + i * BPS]; + } + // top-left (before 'top'!) + it->y_left_[-1] = it->y_top_[15]; + it->u_left_[-1] = it->uv_top_[0 + 7]; + it->v_left_[-1] = it->uv_top_[8 + 7]; + } + if (y < enc->mb_h_ - 1) { // top + memcpy(it->y_top_, ysrc + 15 * BPS, 16); + memcpy(it->uv_top_, uvsrc + 7 * BPS, 8 + 8); + } +} + +int VP8IteratorNext(VP8EncIterator* const it) { + if (++it->x_ == it->enc_->mb_w_) { + VP8IteratorSetRow(it, ++it->y_); + } else { + it->preds_ += 4; + it->mb_ += 1; + it->nz_ += 1; + it->y_top_ += 16; + it->uv_top_ += 16; + } + return (0 < --it->count_down_); +} + +//------------------------------------------------------------------------------ +// Helper function to set mode properties + +void VP8SetIntra16Mode(const VP8EncIterator* const it, int mode) { + uint8_t* preds = it->preds_; + int y; + for (y = 0; y < 4; ++y) { + memset(preds, mode, 4); + preds += it->enc_->preds_w_; + } + it->mb_->type_ = 1; +} + +void VP8SetIntra4Mode(const VP8EncIterator* const it, const uint8_t* modes) { + uint8_t* preds = it->preds_; + int y; + for (y = 4; y > 0; --y) { + memcpy(preds, modes, 4 * sizeof(*modes)); + preds += it->enc_->preds_w_; + modes += 4; + } + it->mb_->type_ = 0; +} + +void VP8SetIntraUVMode(const VP8EncIterator* const it, int mode) { + it->mb_->uv_mode_ = mode; +} + +void VP8SetSkip(const VP8EncIterator* const it, int skip) { + it->mb_->skip_ = skip; +} + +void VP8SetSegment(const VP8EncIterator* const it, int segment) { + it->mb_->segment_ = segment; +} + +//------------------------------------------------------------------------------ +// Intra4x4 sub-blocks iteration +// +// We store and update the boundary samples into an array of 37 pixels. They +// are updated as we iterate and reconstructs each intra4x4 blocks in turn. +// The position of the samples has the following snake pattern: +// +// 16|17 18 19 20|21 22 23 24|25 26 27 28|29 30 31 32|33 34 35 36 <- Top-right +// --+-----------+-----------+-----------+-----------+ +// 15| 19| 23| 27| 31| +// 14| 18| 22| 26| 30| +// 13| 17| 21| 25| 29| +// 12|13 14 15 16|17 18 19 20|21 22 23 24|25 26 27 28| +// --+-----------+-----------+-----------+-----------+ +// 11| 15| 19| 23| 27| +// 10| 14| 18| 22| 26| +// 9| 13| 17| 21| 25| +// 8| 9 10 11 12|13 14 15 16|17 18 19 20|21 22 23 24| +// --+-----------+-----------+-----------+-----------+ +// 7| 11| 15| 19| 23| +// 6| 10| 14| 18| 22| +// 5| 9| 13| 17| 21| +// 4| 5 6 7 8| 9 10 11 12|13 14 15 16|17 18 19 20| +// --+-----------+-----------+-----------+-----------+ +// 3| 7| 11| 15| 19| +// 2| 6| 10| 14| 18| +// 1| 5| 9| 13| 17| +// 0| 1 2 3 4| 5 6 7 8| 9 10 11 12|13 14 15 16| +// --+-----------+-----------+-----------+-----------+ + +// Array to record the position of the top sample to pass to the prediction +// functions in dsp.c. +static const uint8_t VP8TopLeftI4[16] = { + 17, 21, 25, 29, + 13, 17, 21, 25, + 9, 13, 17, 21, + 5, 9, 13, 17 +}; + +void VP8IteratorStartI4(VP8EncIterator* const it) { + const VP8Encoder* const enc = it->enc_; + int i; + + it->i4_ = 0; // first 4x4 sub-block + it->i4_top_ = it->i4_boundary_ + VP8TopLeftI4[0]; + + // Import the boundary samples + for (i = 0; i < 17; ++i) { // left + it->i4_boundary_[i] = it->y_left_[15 - i]; + } + for (i = 0; i < 16; ++i) { // top + it->i4_boundary_[17 + i] = it->y_top_[i]; + } + // top-right samples have a special case on the far right of the picture + if (it->x_ < enc->mb_w_ - 1) { + for (i = 16; i < 16 + 4; ++i) { + it->i4_boundary_[17 + i] = it->y_top_[i]; + } + } else { // else, replicate the last valid pixel four times + for (i = 16; i < 16 + 4; ++i) { + it->i4_boundary_[17 + i] = it->i4_boundary_[17 + 15]; + } + } + VP8IteratorNzToBytes(it); // import the non-zero context +} + +int VP8IteratorRotateI4(VP8EncIterator* const it, + const uint8_t* const yuv_out) { + const uint8_t* const blk = yuv_out + VP8Scan[it->i4_]; + uint8_t* const top = it->i4_top_; + int i; + + // Update the cache with 7 fresh samples + for (i = 0; i <= 3; ++i) { + top[-4 + i] = blk[i + 3 * BPS]; // store future top samples + } + if ((it->i4_ & 3) != 3) { // if not on the right sub-blocks #3, #7, #11, #15 + for (i = 0; i <= 2; ++i) { // store future left samples + top[i] = blk[3 + (2 - i) * BPS]; + } + } else { // else replicate top-right samples, as says the specs. + for (i = 0; i <= 3; ++i) { + top[i] = top[i + 4]; + } + } + // move pointers to next sub-block + ++it->i4_; + if (it->i4_ == 16) { // we're done + return 0; + } + + it->i4_top_ = it->i4_boundary_ + VP8TopLeftI4[it->i4_]; + return 1; +} + +//------------------------------------------------------------------------------ + diff --git a/thirdparty/libwebp/enc/near_lossless.c b/thirdparty/libwebp/enc/near_lossless.c deleted file mode 100644 index f4ab91f571..0000000000 --- a/thirdparty/libwebp/enc/near_lossless.c +++ /dev/null @@ -1,122 +0,0 @@ -// Copyright 2014 Google Inc. All Rights Reserved. -// -// Use of this source code is governed by a BSD-style license -// that can be found in the COPYING file in the root of the source -// tree. An additional intellectual property rights grant can be found -// in the file PATENTS. All contributing project authors may -// be found in the AUTHORS file in the root of the source tree. -// ----------------------------------------------------------------------------- -// -// Near-lossless image preprocessing adjusts pixel values to help -// compressibility with a guarantee of maximum deviation between original and -// resulting pixel values. -// -// Author: Jyrki Alakuijala (jyrki@google.com) -// Converted to C by Aleksander Kramarz (akramarz@google.com) - -#include -#include - -#include "../dsp/lossless.h" -#include "../utils/utils.h" -#include "./vp8enci.h" - -#define MIN_DIM_FOR_NEAR_LOSSLESS 64 -#define MAX_LIMIT_BITS 5 - -// Quantizes the value up or down to a multiple of 1<> 1) + ((a >> bits) & 1); - assert(bits > 0); - if (biased > 0xff) return 0xff; - return biased & ~mask; -} - -// Applies FindClosestDiscretized to all channels of pixel. -static uint32_t ClosestDiscretizedArgb(uint32_t a, int bits) { - return - (FindClosestDiscretized(a >> 24, bits) << 24) | - (FindClosestDiscretized((a >> 16) & 0xff, bits) << 16) | - (FindClosestDiscretized((a >> 8) & 0xff, bits) << 8) | - (FindClosestDiscretized(a & 0xff, bits)); -} - -// Checks if distance between corresponding channel values of pixels a and b -// is within the given limit. -static int IsNear(uint32_t a, uint32_t b, int limit) { - int k; - for (k = 0; k < 4; ++k) { - const int delta = - (int)((a >> (k * 8)) & 0xff) - (int)((b >> (k * 8)) & 0xff); - if (delta >= limit || delta <= -limit) { - return 0; - } - } - return 1; -} - -static int IsSmooth(const uint32_t* const prev_row, - const uint32_t* const curr_row, - const uint32_t* const next_row, - int ix, int limit) { - // Check that all pixels in 4-connected neighborhood are smooth. - return (IsNear(curr_row[ix], curr_row[ix - 1], limit) && - IsNear(curr_row[ix], curr_row[ix + 1], limit) && - IsNear(curr_row[ix], prev_row[ix], limit) && - IsNear(curr_row[ix], next_row[ix], limit)); -} - -// Adjusts pixel values of image with given maximum error. -static void NearLossless(int xsize, int ysize, uint32_t* argb, - int limit_bits, uint32_t* copy_buffer) { - int x, y; - const int limit = 1 << limit_bits; - uint32_t* prev_row = copy_buffer; - uint32_t* curr_row = prev_row + xsize; - uint32_t* next_row = curr_row + xsize; - memcpy(copy_buffer, argb, xsize * 2 * sizeof(argb[0])); - - for (y = 1; y < ysize - 1; ++y) { - uint32_t* const curr_argb_row = argb + y * xsize; - uint32_t* const next_argb_row = curr_argb_row + xsize; - memcpy(next_row, next_argb_row, xsize * sizeof(argb[0])); - for (x = 1; x < xsize - 1; ++x) { - if (!IsSmooth(prev_row, curr_row, next_row, x, limit)) { - curr_argb_row[x] = ClosestDiscretizedArgb(curr_row[x], limit_bits); - } - } - { - // Three-way swap. - uint32_t* const temp = prev_row; - prev_row = curr_row; - curr_row = next_row; - next_row = temp; - } - } -} - -int VP8ApplyNearLossless(int xsize, int ysize, uint32_t* argb, int quality) { - int i; - uint32_t* const copy_buffer = - (uint32_t*)WebPSafeMalloc(xsize * 3, sizeof(*copy_buffer)); - const int limit_bits = VP8LNearLosslessBits(quality); - assert(argb != NULL); - assert(limit_bits >= 0); - assert(limit_bits <= MAX_LIMIT_BITS); - if (copy_buffer == NULL) { - return 0; - } - // For small icon images, don't attempt to apply near-lossless compression. - if (xsize < MIN_DIM_FOR_NEAR_LOSSLESS && ysize < MIN_DIM_FOR_NEAR_LOSSLESS) { - WebPSafeFree(copy_buffer); - return 1; - } - - for (i = limit_bits; i != 0; --i) { - NearLossless(xsize, ysize, argb, i, copy_buffer); - } - WebPSafeFree(copy_buffer); - return 1; -} diff --git a/thirdparty/libwebp/enc/near_lossless_enc.c b/thirdparty/libwebp/enc/near_lossless_enc.c new file mode 100644 index 0000000000..2bd03ab20d --- /dev/null +++ b/thirdparty/libwebp/enc/near_lossless_enc.c @@ -0,0 +1,122 @@ +// Copyright 2014 Google Inc. All Rights Reserved. +// +// Use of this source code is governed by a BSD-style license +// that can be found in the COPYING file in the root of the source +// tree. An additional intellectual property rights grant can be found +// in the file PATENTS. All contributing project authors may +// be found in the AUTHORS file in the root of the source tree. +// ----------------------------------------------------------------------------- +// +// Near-lossless image preprocessing adjusts pixel values to help +// compressibility with a guarantee of maximum deviation between original and +// resulting pixel values. +// +// Author: Jyrki Alakuijala (jyrki@google.com) +// Converted to C by Aleksander Kramarz (akramarz@google.com) + +#include +#include + +#include "../dsp/lossless_common.h" +#include "../utils/utils.h" +#include "./vp8i_enc.h" + +#define MIN_DIM_FOR_NEAR_LOSSLESS 64 +#define MAX_LIMIT_BITS 5 + +// Quantizes the value up or down to a multiple of 1<> 1) + ((a >> bits) & 1); + assert(bits > 0); + if (biased > 0xff) return 0xff; + return biased & ~mask; +} + +// Applies FindClosestDiscretized to all channels of pixel. +static uint32_t ClosestDiscretizedArgb(uint32_t a, int bits) { + return + (FindClosestDiscretized(a >> 24, bits) << 24) | + (FindClosestDiscretized((a >> 16) & 0xff, bits) << 16) | + (FindClosestDiscretized((a >> 8) & 0xff, bits) << 8) | + (FindClosestDiscretized(a & 0xff, bits)); +} + +// Checks if distance between corresponding channel values of pixels a and b +// is within the given limit. +static int IsNear(uint32_t a, uint32_t b, int limit) { + int k; + for (k = 0; k < 4; ++k) { + const int delta = + (int)((a >> (k * 8)) & 0xff) - (int)((b >> (k * 8)) & 0xff); + if (delta >= limit || delta <= -limit) { + return 0; + } + } + return 1; +} + +static int IsSmooth(const uint32_t* const prev_row, + const uint32_t* const curr_row, + const uint32_t* const next_row, + int ix, int limit) { + // Check that all pixels in 4-connected neighborhood are smooth. + return (IsNear(curr_row[ix], curr_row[ix - 1], limit) && + IsNear(curr_row[ix], curr_row[ix + 1], limit) && + IsNear(curr_row[ix], prev_row[ix], limit) && + IsNear(curr_row[ix], next_row[ix], limit)); +} + +// Adjusts pixel values of image with given maximum error. +static void NearLossless(int xsize, int ysize, uint32_t* argb, + int limit_bits, uint32_t* copy_buffer) { + int x, y; + const int limit = 1 << limit_bits; + uint32_t* prev_row = copy_buffer; + uint32_t* curr_row = prev_row + xsize; + uint32_t* next_row = curr_row + xsize; + memcpy(copy_buffer, argb, xsize * 2 * sizeof(argb[0])); + + for (y = 1; y < ysize - 1; ++y) { + uint32_t* const curr_argb_row = argb + y * xsize; + uint32_t* const next_argb_row = curr_argb_row + xsize; + memcpy(next_row, next_argb_row, xsize * sizeof(argb[0])); + for (x = 1; x < xsize - 1; ++x) { + if (!IsSmooth(prev_row, curr_row, next_row, x, limit)) { + curr_argb_row[x] = ClosestDiscretizedArgb(curr_row[x], limit_bits); + } + } + { + // Three-way swap. + uint32_t* const temp = prev_row; + prev_row = curr_row; + curr_row = next_row; + next_row = temp; + } + } +} + +int VP8ApplyNearLossless(int xsize, int ysize, uint32_t* argb, int quality) { + int i; + uint32_t* const copy_buffer = + (uint32_t*)WebPSafeMalloc(xsize * 3, sizeof(*copy_buffer)); + const int limit_bits = VP8LNearLosslessBits(quality); + assert(argb != NULL); + assert(limit_bits >= 0); + assert(limit_bits <= MAX_LIMIT_BITS); + if (copy_buffer == NULL) { + return 0; + } + // For small icon images, don't attempt to apply near-lossless compression. + if (xsize < MIN_DIM_FOR_NEAR_LOSSLESS && ysize < MIN_DIM_FOR_NEAR_LOSSLESS) { + WebPSafeFree(copy_buffer); + return 1; + } + + for (i = limit_bits; i != 0; --i) { + NearLossless(xsize, ysize, argb, i, copy_buffer); + } + WebPSafeFree(copy_buffer); + return 1; +} diff --git a/thirdparty/libwebp/enc/picture.c b/thirdparty/libwebp/enc/picture.c deleted file mode 100644 index 28c56cd6e5..0000000000 --- a/thirdparty/libwebp/enc/picture.c +++ /dev/null @@ -1,293 +0,0 @@ -// Copyright 2011 Google Inc. All Rights Reserved. -// -// Use of this source code is governed by a BSD-style license -// that can be found in the COPYING file in the root of the source -// tree. An additional intellectual property rights grant can be found -// in the file PATENTS. All contributing project authors may -// be found in the AUTHORS file in the root of the source tree. -// ----------------------------------------------------------------------------- -// -// WebPPicture class basis -// -// Author: Skal (pascal.massimino@gmail.com) - -#include -#include - -#include "./vp8enci.h" -#include "../dsp/dsp.h" -#include "../utils/utils.h" - -//------------------------------------------------------------------------------ -// WebPPicture -//------------------------------------------------------------------------------ - -static int DummyWriter(const uint8_t* data, size_t data_size, - const WebPPicture* const picture) { - // The following are to prevent 'unused variable' error message. - (void)data; - (void)data_size; - (void)picture; - return 1; -} - -int WebPPictureInitInternal(WebPPicture* picture, int version) { - if (WEBP_ABI_IS_INCOMPATIBLE(version, WEBP_ENCODER_ABI_VERSION)) { - return 0; // caller/system version mismatch! - } - if (picture != NULL) { - memset(picture, 0, sizeof(*picture)); - picture->writer = DummyWriter; - WebPEncodingSetError(picture, VP8_ENC_OK); - } - return 1; -} - -//------------------------------------------------------------------------------ - -static void WebPPictureResetBufferARGB(WebPPicture* const picture) { - picture->memory_argb_ = NULL; - picture->argb = NULL; - picture->argb_stride = 0; -} - -static void WebPPictureResetBufferYUVA(WebPPicture* const picture) { - picture->memory_ = NULL; - picture->y = picture->u = picture->v = picture->a = NULL; - picture->y_stride = picture->uv_stride = 0; - picture->a_stride = 0; -} - -void WebPPictureResetBuffers(WebPPicture* const picture) { - WebPPictureResetBufferARGB(picture); - WebPPictureResetBufferYUVA(picture); -} - -int WebPPictureAllocARGB(WebPPicture* const picture, int width, int height) { - void* memory; - const uint64_t argb_size = (uint64_t)width * height; - - assert(picture != NULL); - - WebPSafeFree(picture->memory_argb_); - WebPPictureResetBufferARGB(picture); - - if (width <= 0 || height <= 0) { - return WebPEncodingSetError(picture, VP8_ENC_ERROR_BAD_DIMENSION); - } - // allocate a new buffer. - memory = WebPSafeMalloc(argb_size, sizeof(*picture->argb)); - if (memory == NULL) { - return WebPEncodingSetError(picture, VP8_ENC_ERROR_OUT_OF_MEMORY); - } - // TODO(skal): align plane to cache line? - picture->memory_argb_ = memory; - picture->argb = (uint32_t*)memory; - picture->argb_stride = width; - return 1; -} - -int WebPPictureAllocYUVA(WebPPicture* const picture, int width, int height) { - const WebPEncCSP uv_csp = - (WebPEncCSP)((int)picture->colorspace & WEBP_CSP_UV_MASK); - const int has_alpha = (int)picture->colorspace & WEBP_CSP_ALPHA_BIT; - const int y_stride = width; - const int uv_width = (width + 1) >> 1; - const int uv_height = (height + 1) >> 1; - const int uv_stride = uv_width; - int a_width, a_stride; - uint64_t y_size, uv_size, a_size, total_size; - uint8_t* mem; - - assert(picture != NULL); - - WebPSafeFree(picture->memory_); - WebPPictureResetBufferYUVA(picture); - - if (uv_csp != WEBP_YUV420) { - return WebPEncodingSetError(picture, VP8_ENC_ERROR_INVALID_CONFIGURATION); - } - - // alpha - a_width = has_alpha ? width : 0; - a_stride = a_width; - y_size = (uint64_t)y_stride * height; - uv_size = (uint64_t)uv_stride * uv_height; - a_size = (uint64_t)a_stride * height; - - total_size = y_size + a_size + 2 * uv_size; - - // Security and validation checks - if (width <= 0 || height <= 0 || // luma/alpha param error - uv_width < 0 || uv_height < 0) { // u/v param error - return WebPEncodingSetError(picture, VP8_ENC_ERROR_BAD_DIMENSION); - } - // allocate a new buffer. - mem = (uint8_t*)WebPSafeMalloc(total_size, sizeof(*mem)); - if (mem == NULL) { - return WebPEncodingSetError(picture, VP8_ENC_ERROR_OUT_OF_MEMORY); - } - - // From now on, we're in the clear, we can no longer fail... - picture->memory_ = (void*)mem; - picture->y_stride = y_stride; - picture->uv_stride = uv_stride; - picture->a_stride = a_stride; - - // TODO(skal): we could align the y/u/v planes and adjust stride. - picture->y = mem; - mem += y_size; - - picture->u = mem; - mem += uv_size; - picture->v = mem; - mem += uv_size; - - if (a_size > 0) { - picture->a = mem; - mem += a_size; - } - (void)mem; // makes the static analyzer happy - return 1; -} - -int WebPPictureAlloc(WebPPicture* picture) { - if (picture != NULL) { - const int width = picture->width; - const int height = picture->height; - - WebPPictureFree(picture); // erase previous buffer - - if (!picture->use_argb) { - return WebPPictureAllocYUVA(picture, width, height); - } else { - return WebPPictureAllocARGB(picture, width, height); - } - } - return 1; -} - -void WebPPictureFree(WebPPicture* picture) { - if (picture != NULL) { - WebPSafeFree(picture->memory_); - WebPSafeFree(picture->memory_argb_); - WebPPictureResetBuffers(picture); - } -} - -//------------------------------------------------------------------------------ -// WebPMemoryWriter: Write-to-memory - -void WebPMemoryWriterInit(WebPMemoryWriter* writer) { - writer->mem = NULL; - writer->size = 0; - writer->max_size = 0; -} - -int WebPMemoryWrite(const uint8_t* data, size_t data_size, - const WebPPicture* picture) { - WebPMemoryWriter* const w = (WebPMemoryWriter*)picture->custom_ptr; - uint64_t next_size; - if (w == NULL) { - return 1; - } - next_size = (uint64_t)w->size + data_size; - if (next_size > w->max_size) { - uint8_t* new_mem; - uint64_t next_max_size = 2ULL * w->max_size; - if (next_max_size < next_size) next_max_size = next_size; - if (next_max_size < 8192ULL) next_max_size = 8192ULL; - new_mem = (uint8_t*)WebPSafeMalloc(next_max_size, 1); - if (new_mem == NULL) { - return 0; - } - if (w->size > 0) { - memcpy(new_mem, w->mem, w->size); - } - WebPSafeFree(w->mem); - w->mem = new_mem; - // down-cast is ok, thanks to WebPSafeMalloc - w->max_size = (size_t)next_max_size; - } - if (data_size > 0) { - memcpy(w->mem + w->size, data, data_size); - w->size += data_size; - } - return 1; -} - -void WebPMemoryWriterClear(WebPMemoryWriter* writer) { - if (writer != NULL) { - WebPSafeFree(writer->mem); - writer->mem = NULL; - writer->size = 0; - writer->max_size = 0; - } -} - -//------------------------------------------------------------------------------ -// Simplest high-level calls: - -typedef int (*Importer)(WebPPicture* const, const uint8_t* const, int); - -static size_t Encode(const uint8_t* rgba, int width, int height, int stride, - Importer import, float quality_factor, int lossless, - uint8_t** output) { - WebPPicture pic; - WebPConfig config; - WebPMemoryWriter wrt; - int ok; - - if (output == NULL) return 0; - - if (!WebPConfigPreset(&config, WEBP_PRESET_DEFAULT, quality_factor) || - !WebPPictureInit(&pic)) { - return 0; // shouldn't happen, except if system installation is broken - } - - config.lossless = !!lossless; - pic.use_argb = !!lossless; - pic.width = width; - pic.height = height; - pic.writer = WebPMemoryWrite; - pic.custom_ptr = &wrt; - WebPMemoryWriterInit(&wrt); - - ok = import(&pic, rgba, stride) && WebPEncode(&config, &pic); - WebPPictureFree(&pic); - if (!ok) { - WebPMemoryWriterClear(&wrt); - *output = NULL; - return 0; - } - *output = wrt.mem; - return wrt.size; -} - -#define ENCODE_FUNC(NAME, IMPORTER) \ -size_t NAME(const uint8_t* in, int w, int h, int bps, float q, \ - uint8_t** out) { \ - return Encode(in, w, h, bps, IMPORTER, q, 0, out); \ -} - -ENCODE_FUNC(WebPEncodeRGB, WebPPictureImportRGB) -ENCODE_FUNC(WebPEncodeBGR, WebPPictureImportBGR) -ENCODE_FUNC(WebPEncodeRGBA, WebPPictureImportRGBA) -ENCODE_FUNC(WebPEncodeBGRA, WebPPictureImportBGRA) - -#undef ENCODE_FUNC - -#define LOSSLESS_DEFAULT_QUALITY 70. -#define LOSSLESS_ENCODE_FUNC(NAME, IMPORTER) \ -size_t NAME(const uint8_t* in, int w, int h, int bps, uint8_t** out) { \ - return Encode(in, w, h, bps, IMPORTER, LOSSLESS_DEFAULT_QUALITY, 1, out); \ -} - -LOSSLESS_ENCODE_FUNC(WebPEncodeLosslessRGB, WebPPictureImportRGB) -LOSSLESS_ENCODE_FUNC(WebPEncodeLosslessBGR, WebPPictureImportBGR) -LOSSLESS_ENCODE_FUNC(WebPEncodeLosslessRGBA, WebPPictureImportRGBA) -LOSSLESS_ENCODE_FUNC(WebPEncodeLosslessBGRA, WebPPictureImportBGRA) - -#undef LOSSLESS_ENCODE_FUNC - -//------------------------------------------------------------------------------ diff --git a/thirdparty/libwebp/enc/picture_csp.c b/thirdparty/libwebp/enc/picture_csp.c deleted file mode 100644 index 188a3ca55b..0000000000 --- a/thirdparty/libwebp/enc/picture_csp.c +++ /dev/null @@ -1,1192 +0,0 @@ -// Copyright 2014 Google Inc. All Rights Reserved. -// -// Use of this source code is governed by a BSD-style license -// that can be found in the COPYING file in the root of the source -// tree. An additional intellectual property rights grant can be found -// in the file PATENTS. All contributing project authors may -// be found in the AUTHORS file in the root of the source tree. -// ----------------------------------------------------------------------------- -// -// WebPPicture utils for colorspace conversion -// -// Author: Skal (pascal.massimino@gmail.com) - -#include -#include -#include - -#include "./vp8enci.h" -#include "../utils/random.h" -#include "../utils/utils.h" -#include "../dsp/yuv.h" - -// Uncomment to disable gamma-compression during RGB->U/V averaging -#define USE_GAMMA_COMPRESSION - -// If defined, use table to compute x / alpha. -#define USE_INVERSE_ALPHA_TABLE - -static const union { - uint32_t argb; - uint8_t bytes[4]; -} test_endian = { 0xff000000u }; -#define ALPHA_IS_LAST (test_endian.bytes[3] == 0xff) - -//------------------------------------------------------------------------------ -// Detection of non-trivial transparency - -// Returns true if alpha[] has non-0xff values. -static int CheckNonOpaque(const uint8_t* alpha, int width, int height, - int x_step, int y_step) { - if (alpha == NULL) return 0; - while (height-- > 0) { - int x; - for (x = 0; x < width * x_step; x += x_step) { - if (alpha[x] != 0xff) return 1; // TODO(skal): check 4/8 bytes at a time. - } - alpha += y_step; - } - return 0; -} - -// Checking for the presence of non-opaque alpha. -int WebPPictureHasTransparency(const WebPPicture* picture) { - if (picture == NULL) return 0; - if (!picture->use_argb) { - return CheckNonOpaque(picture->a, picture->width, picture->height, - 1, picture->a_stride); - } else { - int x, y; - const uint32_t* argb = picture->argb; - if (argb == NULL) return 0; - for (y = 0; y < picture->height; ++y) { - for (x = 0; x < picture->width; ++x) { - if (argb[x] < 0xff000000u) return 1; // test any alpha values != 0xff - } - argb += picture->argb_stride; - } - } - return 0; -} - -//------------------------------------------------------------------------------ -// Code for gamma correction - -#if defined(USE_GAMMA_COMPRESSION) - -// gamma-compensates loss of resolution during chroma subsampling -#define kGamma 0.80 // for now we use a different gamma value than kGammaF -#define kGammaFix 12 // fixed-point precision for linear values -#define kGammaScale ((1 << kGammaFix) - 1) -#define kGammaTabFix 7 // fixed-point fractional bits precision -#define kGammaTabScale (1 << kGammaTabFix) -#define kGammaTabRounder (kGammaTabScale >> 1) -#define kGammaTabSize (1 << (kGammaFix - kGammaTabFix)) - -static int kLinearToGammaTab[kGammaTabSize + 1]; -static uint16_t kGammaToLinearTab[256]; -static volatile int kGammaTablesOk = 0; - -static WEBP_TSAN_IGNORE_FUNCTION void InitGammaTables(void) { - if (!kGammaTablesOk) { - int v; - const double scale = (double)(1 << kGammaTabFix) / kGammaScale; - const double norm = 1. / 255.; - for (v = 0; v <= 255; ++v) { - kGammaToLinearTab[v] = - (uint16_t)(pow(norm * v, kGamma) * kGammaScale + .5); - } - for (v = 0; v <= kGammaTabSize; ++v) { - kLinearToGammaTab[v] = (int)(255. * pow(scale * v, 1. / kGamma) + .5); - } - kGammaTablesOk = 1; - } -} - -static WEBP_INLINE uint32_t GammaToLinear(uint8_t v) { - return kGammaToLinearTab[v]; -} - -static WEBP_INLINE int Interpolate(int v) { - const int tab_pos = v >> (kGammaTabFix + 2); // integer part - const int x = v & ((kGammaTabScale << 2) - 1); // fractional part - const int v0 = kLinearToGammaTab[tab_pos]; - const int v1 = kLinearToGammaTab[tab_pos + 1]; - const int y = v1 * x + v0 * ((kGammaTabScale << 2) - x); // interpolate - assert(tab_pos + 1 < kGammaTabSize + 1); - return y; -} - -// Convert a linear value 'v' to YUV_FIX+2 fixed-point precision -// U/V value, suitable for RGBToU/V calls. -static WEBP_INLINE int LinearToGamma(uint32_t base_value, int shift) { - const int y = Interpolate(base_value << shift); // final uplifted value - return (y + kGammaTabRounder) >> kGammaTabFix; // descale -} - -#else - -static WEBP_TSAN_IGNORE_FUNCTION void InitGammaTables(void) {} -static WEBP_INLINE uint32_t GammaToLinear(uint8_t v) { return v; } -static WEBP_INLINE int LinearToGamma(uint32_t base_value, int shift) { - return (int)(base_value << shift); -} - -#endif // USE_GAMMA_COMPRESSION - -//------------------------------------------------------------------------------ -// RGB -> YUV conversion - -static int RGBToY(int r, int g, int b, VP8Random* const rg) { - return (rg == NULL) ? VP8RGBToY(r, g, b, YUV_HALF) - : VP8RGBToY(r, g, b, VP8RandomBits(rg, YUV_FIX)); -} - -static int RGBToU(int r, int g, int b, VP8Random* const rg) { - return (rg == NULL) ? VP8RGBToU(r, g, b, YUV_HALF << 2) - : VP8RGBToU(r, g, b, VP8RandomBits(rg, YUV_FIX + 2)); -} - -static int RGBToV(int r, int g, int b, VP8Random* const rg) { - return (rg == NULL) ? VP8RGBToV(r, g, b, YUV_HALF << 2) - : VP8RGBToV(r, g, b, VP8RandomBits(rg, YUV_FIX + 2)); -} - -//------------------------------------------------------------------------------ -// Smart RGB->YUV conversion - -static const int kNumIterations = 6; -static const int kMinDimensionIterativeConversion = 4; - -// We could use SFIX=0 and only uint8_t for fixed_y_t, but it produces some -// banding sometimes. Better use extra precision. -#define SFIX 2 // fixed-point precision of RGB and Y/W -typedef int16_t fixed_t; // signed type with extra SFIX precision for UV -typedef uint16_t fixed_y_t; // unsigned type with extra SFIX precision for W - -#define SHALF (1 << SFIX >> 1) -#define MAX_Y_T ((256 << SFIX) - 1) -#define SROUNDER (1 << (YUV_FIX + SFIX - 1)) - -#if defined(USE_GAMMA_COMPRESSION) - -// float variant of gamma-correction -// We use tables of different size and precision, along with a 'real-world' -// Gamma value close to ~2. -#define kGammaF 2.2 -static float kGammaToLinearTabF[MAX_Y_T + 1]; // size scales with Y_FIX -static float kLinearToGammaTabF[kGammaTabSize + 2]; -static volatile int kGammaTablesFOk = 0; - -static WEBP_TSAN_IGNORE_FUNCTION void InitGammaTablesF(void) { - if (!kGammaTablesFOk) { - int v; - const double norm = 1. / MAX_Y_T; - const double scale = 1. / kGammaTabSize; - for (v = 0; v <= MAX_Y_T; ++v) { - kGammaToLinearTabF[v] = (float)pow(norm * v, kGammaF); - } - for (v = 0; v <= kGammaTabSize; ++v) { - kLinearToGammaTabF[v] = (float)(MAX_Y_T * pow(scale * v, 1. / kGammaF)); - } - // to prevent small rounding errors to cause read-overflow: - kLinearToGammaTabF[kGammaTabSize + 1] = kLinearToGammaTabF[kGammaTabSize]; - kGammaTablesFOk = 1; - } -} - -static WEBP_INLINE float GammaToLinearF(int v) { - return kGammaToLinearTabF[v]; -} - -static WEBP_INLINE int LinearToGammaF(float value) { - const float v = value * kGammaTabSize; - const int tab_pos = (int)v; - const float x = v - (float)tab_pos; // fractional part - const float v0 = kLinearToGammaTabF[tab_pos + 0]; - const float v1 = kLinearToGammaTabF[tab_pos + 1]; - const float y = v1 * x + v0 * (1.f - x); // interpolate - return (int)(y + .5); -} - -#else - -static WEBP_TSAN_IGNORE_FUNCTION void InitGammaTablesF(void) {} -static WEBP_INLINE float GammaToLinearF(int v) { - const float norm = 1.f / MAX_Y_T; - return norm * v; -} -static WEBP_INLINE int LinearToGammaF(float value) { - return (int)(MAX_Y_T * value + .5); -} - -#endif // USE_GAMMA_COMPRESSION - -//------------------------------------------------------------------------------ - -static uint8_t clip_8b(fixed_t v) { - return (!(v & ~0xff)) ? (uint8_t)v : (v < 0) ? 0u : 255u; -} - -static fixed_y_t clip_y(int y) { - return (!(y & ~MAX_Y_T)) ? (fixed_y_t)y : (y < 0) ? 0 : MAX_Y_T; -} - -//------------------------------------------------------------------------------ - -static int RGBToGray(int r, int g, int b) { - const int luma = 19595 * r + 38470 * g + 7471 * b + YUV_HALF; - return (luma >> YUV_FIX); -} - -static float RGBToGrayF(float r, float g, float b) { - return 0.299f * r + 0.587f * g + 0.114f * b; -} - -static int ScaleDown(int a, int b, int c, int d) { - const float A = GammaToLinearF(a); - const float B = GammaToLinearF(b); - const float C = GammaToLinearF(c); - const float D = GammaToLinearF(d); - return LinearToGammaF(0.25f * (A + B + C + D)); -} - -static WEBP_INLINE void UpdateW(const fixed_y_t* src, fixed_y_t* dst, int len) { - while (len-- > 0) { - const float R = GammaToLinearF(src[0]); - const float G = GammaToLinearF(src[1]); - const float B = GammaToLinearF(src[2]); - const float Y = RGBToGrayF(R, G, B); - *dst++ = (fixed_y_t)LinearToGammaF(Y); - src += 3; - } -} - -static int UpdateChroma(const fixed_y_t* src1, - const fixed_y_t* src2, - fixed_t* dst, fixed_y_t* tmp, int len) { - int diff = 0; - while (len--> 0) { - const int r = ScaleDown(src1[0], src1[3], src2[0], src2[3]); - const int g = ScaleDown(src1[1], src1[4], src2[1], src2[4]); - const int b = ScaleDown(src1[2], src1[5], src2[2], src2[5]); - const int W = RGBToGray(r, g, b); - const int r_avg = (src1[0] + src1[3] + src2[0] + src2[3] + 2) >> 2; - const int g_avg = (src1[1] + src1[4] + src2[1] + src2[4] + 2) >> 2; - const int b_avg = (src1[2] + src1[5] + src2[2] + src2[5] + 2) >> 2; - dst[0] = (fixed_t)(r - W); - dst[1] = (fixed_t)(g - W); - dst[2] = (fixed_t)(b - W); - dst += 3; - src1 += 6; - src2 += 6; - if (tmp != NULL) { - tmp[0] = tmp[1] = clip_y(W); - tmp += 2; - } - diff += abs(RGBToGray(r_avg, g_avg, b_avg) - W); - } - return diff; -} - -//------------------------------------------------------------------------------ - -static WEBP_INLINE int Filter(const fixed_t* const A, const fixed_t* const B, - int rightwise) { - int v; - if (!rightwise) { - v = (A[0] * 9 + A[-3] * 3 + B[0] * 3 + B[-3]); - } else { - v = (A[0] * 9 + A[+3] * 3 + B[0] * 3 + B[+3]); - } - return (v + 8) >> 4; -} - -static WEBP_INLINE int Filter2(int A, int B) { return (A * 3 + B + 2) >> 2; } - -//------------------------------------------------------------------------------ - -static WEBP_INLINE fixed_y_t UpLift(uint8_t a) { // 8bit -> SFIX - return ((fixed_y_t)a << SFIX) | SHALF; -} - -static void ImportOneRow(const uint8_t* const r_ptr, - const uint8_t* const g_ptr, - const uint8_t* const b_ptr, - int step, - int pic_width, - fixed_y_t* const dst) { - int i; - for (i = 0; i < pic_width; ++i) { - const int off = i * step; - dst[3 * i + 0] = UpLift(r_ptr[off]); - dst[3 * i + 1] = UpLift(g_ptr[off]); - dst[3 * i + 2] = UpLift(b_ptr[off]); - } - if (pic_width & 1) { // replicate rightmost pixel - memcpy(dst + 3 * pic_width, dst + 3 * (pic_width - 1), 3 * sizeof(*dst)); - } -} - -static void InterpolateTwoRows(const fixed_y_t* const best_y, - const fixed_t* const prev_uv, - const fixed_t* const cur_uv, - const fixed_t* const next_uv, - int w, - fixed_y_t* const out1, - fixed_y_t* const out2) { - int i, k; - { // special boundary case for i==0 - const int W0 = best_y[0]; - const int W1 = best_y[w]; - for (k = 0; k <= 2; ++k) { - out1[k] = clip_y(Filter2(cur_uv[k], prev_uv[k]) + W0); - out2[k] = clip_y(Filter2(cur_uv[k], next_uv[k]) + W1); - } - } - for (i = 1; i < w - 1; ++i) { - const int W0 = best_y[i + 0]; - const int W1 = best_y[i + w]; - const int off = 3 * (i >> 1); - for (k = 0; k <= 2; ++k) { - const int tmp0 = Filter(cur_uv + off + k, prev_uv + off + k, i & 1); - const int tmp1 = Filter(cur_uv + off + k, next_uv + off + k, i & 1); - out1[3 * i + k] = clip_y(tmp0 + W0); - out2[3 * i + k] = clip_y(tmp1 + W1); - } - } - { // special boundary case for i == w - 1 - const int W0 = best_y[i + 0]; - const int W1 = best_y[i + w]; - const int off = 3 * (i >> 1); - for (k = 0; k <= 2; ++k) { - out1[3 * i + k] = clip_y(Filter2(cur_uv[off + k], prev_uv[off + k]) + W0); - out2[3 * i + k] = clip_y(Filter2(cur_uv[off + k], next_uv[off + k]) + W1); - } - } -} - -static WEBP_INLINE uint8_t ConvertRGBToY(int r, int g, int b) { - const int luma = 16839 * r + 33059 * g + 6420 * b + SROUNDER; - return clip_8b(16 + (luma >> (YUV_FIX + SFIX))); -} - -static WEBP_INLINE uint8_t ConvertRGBToU(int r, int g, int b) { - const int u = -9719 * r - 19081 * g + 28800 * b + SROUNDER; - return clip_8b(128 + (u >> (YUV_FIX + SFIX))); -} - -static WEBP_INLINE uint8_t ConvertRGBToV(int r, int g, int b) { - const int v = +28800 * r - 24116 * g - 4684 * b + SROUNDER; - return clip_8b(128 + (v >> (YUV_FIX + SFIX))); -} - -static int ConvertWRGBToYUV(const fixed_y_t* best_y, const fixed_t* best_uv, - WebPPicture* const picture) { - int i, j; - uint8_t* dst_y = picture->y; - uint8_t* dst_u = picture->u; - uint8_t* dst_v = picture->v; - const fixed_t* const best_uv_base = best_uv; - const int w = (picture->width + 1) & ~1; - const int h = (picture->height + 1) & ~1; - const int uv_w = w >> 1; - const int uv_h = h >> 1; - for (best_uv = best_uv_base, j = 0; j < picture->height; ++j) { - for (i = 0; i < picture->width; ++i) { - const int off = 3 * (i >> 1); - const int W = best_y[i]; - const int r = best_uv[off + 0] + W; - const int g = best_uv[off + 1] + W; - const int b = best_uv[off + 2] + W; - dst_y[i] = ConvertRGBToY(r, g, b); - } - best_y += w; - best_uv += (j & 1) * 3 * uv_w; - dst_y += picture->y_stride; - } - for (best_uv = best_uv_base, j = 0; j < uv_h; ++j) { - for (i = 0; i < uv_w; ++i) { - const int off = 3 * i; - const int r = best_uv[off + 0]; - const int g = best_uv[off + 1]; - const int b = best_uv[off + 2]; - dst_u[i] = ConvertRGBToU(r, g, b); - dst_v[i] = ConvertRGBToV(r, g, b); - } - best_uv += 3 * uv_w; - dst_u += picture->uv_stride; - dst_v += picture->uv_stride; - } - return 1; -} - -//------------------------------------------------------------------------------ -// Main function - -#define SAFE_ALLOC(W, H, T) ((T*)WebPSafeMalloc((W) * (H), sizeof(T))) - -static int PreprocessARGB(const uint8_t* r_ptr, - const uint8_t* g_ptr, - const uint8_t* b_ptr, - int step, int rgb_stride, - WebPPicture* const picture) { - // we expand the right/bottom border if needed - const int w = (picture->width + 1) & ~1; - const int h = (picture->height + 1) & ~1; - const int uv_w = w >> 1; - const int uv_h = h >> 1; - int i, j, iter; - - // TODO(skal): allocate one big memory chunk. But for now, it's easier - // for valgrind debugging to have several chunks. - fixed_y_t* const tmp_buffer = SAFE_ALLOC(w * 3, 2, fixed_y_t); // scratch - fixed_y_t* const best_y_base = SAFE_ALLOC(w, h, fixed_y_t); - fixed_y_t* const target_y_base = SAFE_ALLOC(w, h, fixed_y_t); - fixed_y_t* const best_rgb_y = SAFE_ALLOC(w, 2, fixed_y_t); - fixed_t* const best_uv_base = SAFE_ALLOC(uv_w * 3, uv_h, fixed_t); - fixed_t* const target_uv_base = SAFE_ALLOC(uv_w * 3, uv_h, fixed_t); - fixed_t* const best_rgb_uv = SAFE_ALLOC(uv_w * 3, 1, fixed_t); - fixed_y_t* best_y = best_y_base; - fixed_y_t* target_y = target_y_base; - fixed_t* best_uv = best_uv_base; - fixed_t* target_uv = target_uv_base; - int ok; - int diff_sum = 0; - const int first_diff_threshold = (int)(2.5 * w * h); - const int min_improvement = 5; // stop if improvement is below this % - const int min_first_improvement = 80; - - if (best_y_base == NULL || best_uv_base == NULL || - target_y_base == NULL || target_uv_base == NULL || - best_rgb_y == NULL || best_rgb_uv == NULL || - tmp_buffer == NULL) { - ok = WebPEncodingSetError(picture, VP8_ENC_ERROR_OUT_OF_MEMORY); - goto End; - } - assert(picture->width >= kMinDimensionIterativeConversion); - assert(picture->height >= kMinDimensionIterativeConversion); - - // Import RGB samples to W/RGB representation. - for (j = 0; j < picture->height; j += 2) { - const int is_last_row = (j == picture->height - 1); - fixed_y_t* const src1 = tmp_buffer; - fixed_y_t* const src2 = tmp_buffer + 3 * w; - - // prepare two rows of input - ImportOneRow(r_ptr, g_ptr, b_ptr, step, picture->width, src1); - if (!is_last_row) { - ImportOneRow(r_ptr + rgb_stride, g_ptr + rgb_stride, b_ptr + rgb_stride, - step, picture->width, src2); - } else { - memcpy(src2, src1, 3 * w * sizeof(*src2)); - } - UpdateW(src1, target_y, w); - UpdateW(src2, target_y + w, w); - diff_sum += UpdateChroma(src1, src2, target_uv, best_y, uv_w); - memcpy(best_uv, target_uv, 3 * uv_w * sizeof(*best_uv)); - memcpy(best_y + w, best_y, w * sizeof(*best_y)); - best_y += 2 * w; - best_uv += 3 * uv_w; - target_y += 2 * w; - target_uv += 3 * uv_w; - r_ptr += 2 * rgb_stride; - g_ptr += 2 * rgb_stride; - b_ptr += 2 * rgb_stride; - } - - // Iterate and resolve clipping conflicts. - for (iter = 0; iter < kNumIterations; ++iter) { - int k; - const fixed_t* cur_uv = best_uv_base; - const fixed_t* prev_uv = best_uv_base; - const int old_diff_sum = diff_sum; - diff_sum = 0; - - best_y = best_y_base; - best_uv = best_uv_base; - target_y = target_y_base; - target_uv = target_uv_base; - for (j = 0; j < h; j += 2) { - fixed_y_t* const src1 = tmp_buffer; - fixed_y_t* const src2 = tmp_buffer + 3 * w; - { - const fixed_t* const next_uv = cur_uv + ((j < h - 2) ? 3 * uv_w : 0); - InterpolateTwoRows(best_y, prev_uv, cur_uv, next_uv, w, src1, src2); - prev_uv = cur_uv; - cur_uv = next_uv; - } - - UpdateW(src1, best_rgb_y + 0 * w, w); - UpdateW(src2, best_rgb_y + 1 * w, w); - diff_sum += UpdateChroma(src1, src2, best_rgb_uv, NULL, uv_w); - - // update two rows of Y and one row of RGB - for (i = 0; i < 2 * w; ++i) { - const int diff_y = target_y[i] - best_rgb_y[i]; - const int new_y = (int)best_y[i] + diff_y; - best_y[i] = clip_y(new_y); - } - for (i = 0; i < uv_w; ++i) { - const int off = 3 * i; - int W; - for (k = 0; k <= 2; ++k) { - const int diff_uv = (int)target_uv[off + k] - best_rgb_uv[off + k]; - best_uv[off + k] += diff_uv; - } - W = RGBToGray(best_uv[off + 0], best_uv[off + 1], best_uv[off + 2]); - for (k = 0; k <= 2; ++k) { - best_uv[off + k] -= W; - } - } - best_y += 2 * w; - best_uv += 3 * uv_w; - target_y += 2 * w; - target_uv += 3 * uv_w; - } - // test exit condition - if (diff_sum > 0) { - const int improvement = 100 * abs(diff_sum - old_diff_sum) / diff_sum; - // Check if first iteration gave good result already, without a large - // jump of improvement (otherwise it means we need to try few extra - // iterations, just to be sure). - if (iter == 0 && diff_sum < first_diff_threshold && - improvement < min_first_improvement) { - break; - } - // then, check if improvement is stalling. - if (improvement < min_improvement) { - break; - } - } else { - break; - } - } - - // final reconstruction - ok = ConvertWRGBToYUV(best_y_base, best_uv_base, picture); - - End: - WebPSafeFree(best_y_base); - WebPSafeFree(best_uv_base); - WebPSafeFree(target_y_base); - WebPSafeFree(target_uv_base); - WebPSafeFree(best_rgb_y); - WebPSafeFree(best_rgb_uv); - WebPSafeFree(tmp_buffer); - return ok; -} -#undef SAFE_ALLOC - -//------------------------------------------------------------------------------ -// "Fast" regular RGB->YUV - -#define SUM4(ptr, step) LinearToGamma( \ - GammaToLinear((ptr)[0]) + \ - GammaToLinear((ptr)[(step)]) + \ - GammaToLinear((ptr)[rgb_stride]) + \ - GammaToLinear((ptr)[rgb_stride + (step)]), 0) \ - -#define SUM2(ptr) \ - LinearToGamma(GammaToLinear((ptr)[0]) + GammaToLinear((ptr)[rgb_stride]), 1) - -#define SUM2ALPHA(ptr) ((ptr)[0] + (ptr)[rgb_stride]) -#define SUM4ALPHA(ptr) (SUM2ALPHA(ptr) + SUM2ALPHA((ptr) + 4)) - -#if defined(USE_INVERSE_ALPHA_TABLE) - -static const int kAlphaFix = 19; -// Following table is (1 << kAlphaFix) / a. The (v * kInvAlpha[a]) >> kAlphaFix -// formula is then equal to v / a in most (99.6%) cases. Note that this table -// and constant are adjusted very tightly to fit 32b arithmetic. -// In particular, they use the fact that the operands for 'v / a' are actually -// derived as v = (a0.p0 + a1.p1 + a2.p2 + a3.p3) and a = a0 + a1 + a2 + a3 -// with ai in [0..255] and pi in [0..1<> (kAlphaFix - 2)) - -#else - -#define DIVIDE_BY_ALPHA(sum, a) (4 * (sum) / (a)) - -#endif // USE_INVERSE_ALPHA_TABLE - -static WEBP_INLINE int LinearToGammaWeighted(const uint8_t* src, - const uint8_t* a_ptr, - uint32_t total_a, int step, - int rgb_stride) { - const uint32_t sum = - a_ptr[0] * GammaToLinear(src[0]) + - a_ptr[step] * GammaToLinear(src[step]) + - a_ptr[rgb_stride] * GammaToLinear(src[rgb_stride]) + - a_ptr[rgb_stride + step] * GammaToLinear(src[rgb_stride + step]); - assert(total_a > 0 && total_a <= 4 * 0xff); -#if defined(USE_INVERSE_ALPHA_TABLE) - assert((uint64_t)sum * kInvAlpha[total_a] < ((uint64_t)1 << 32)); -#endif - return LinearToGamma(DIVIDE_BY_ALPHA(sum, total_a), 0); -} - -static WEBP_INLINE void ConvertRowToY(const uint8_t* const r_ptr, - const uint8_t* const g_ptr, - const uint8_t* const b_ptr, - int step, - uint8_t* const dst_y, - int width, - VP8Random* const rg) { - int i, j; - for (i = 0, j = 0; i < width; i += 1, j += step) { - dst_y[i] = RGBToY(r_ptr[j], g_ptr[j], b_ptr[j], rg); - } -} - -static WEBP_INLINE void AccumulateRGBA(const uint8_t* const r_ptr, - const uint8_t* const g_ptr, - const uint8_t* const b_ptr, - const uint8_t* const a_ptr, - int rgb_stride, - uint16_t* dst, int width) { - int i, j; - // we loop over 2x2 blocks and produce one R/G/B/A value for each. - for (i = 0, j = 0; i < (width >> 1); i += 1, j += 2 * 4, dst += 4) { - const uint32_t a = SUM4ALPHA(a_ptr + j); - int r, g, b; - if (a == 4 * 0xff || a == 0) { - r = SUM4(r_ptr + j, 4); - g = SUM4(g_ptr + j, 4); - b = SUM4(b_ptr + j, 4); - } else { - r = LinearToGammaWeighted(r_ptr + j, a_ptr + j, a, 4, rgb_stride); - g = LinearToGammaWeighted(g_ptr + j, a_ptr + j, a, 4, rgb_stride); - b = LinearToGammaWeighted(b_ptr + j, a_ptr + j, a, 4, rgb_stride); - } - dst[0] = r; - dst[1] = g; - dst[2] = b; - dst[3] = a; - } - if (width & 1) { - const uint32_t a = 2u * SUM2ALPHA(a_ptr + j); - int r, g, b; - if (a == 4 * 0xff || a == 0) { - r = SUM2(r_ptr + j); - g = SUM2(g_ptr + j); - b = SUM2(b_ptr + j); - } else { - r = LinearToGammaWeighted(r_ptr + j, a_ptr + j, a, 0, rgb_stride); - g = LinearToGammaWeighted(g_ptr + j, a_ptr + j, a, 0, rgb_stride); - b = LinearToGammaWeighted(b_ptr + j, a_ptr + j, a, 0, rgb_stride); - } - dst[0] = r; - dst[1] = g; - dst[2] = b; - dst[3] = a; - } -} - -static WEBP_INLINE void AccumulateRGB(const uint8_t* const r_ptr, - const uint8_t* const g_ptr, - const uint8_t* const b_ptr, - int step, int rgb_stride, - uint16_t* dst, int width) { - int i, j; - for (i = 0, j = 0; i < (width >> 1); i += 1, j += 2 * step, dst += 4) { - dst[0] = SUM4(r_ptr + j, step); - dst[1] = SUM4(g_ptr + j, step); - dst[2] = SUM4(b_ptr + j, step); - } - if (width & 1) { - dst[0] = SUM2(r_ptr + j); - dst[1] = SUM2(g_ptr + j); - dst[2] = SUM2(b_ptr + j); - } -} - -static WEBP_INLINE void ConvertRowsToUV(const uint16_t* rgb, - uint8_t* const dst_u, - uint8_t* const dst_v, - int width, - VP8Random* const rg) { - int i; - for (i = 0; i < width; i += 1, rgb += 4) { - const int r = rgb[0], g = rgb[1], b = rgb[2]; - dst_u[i] = RGBToU(r, g, b, rg); - dst_v[i] = RGBToV(r, g, b, rg); - } -} - -static int ImportYUVAFromRGBA(const uint8_t* r_ptr, - const uint8_t* g_ptr, - const uint8_t* b_ptr, - const uint8_t* a_ptr, - int step, // bytes per pixel - int rgb_stride, // bytes per scanline - float dithering, - int use_iterative_conversion, - WebPPicture* const picture) { - int y; - const int width = picture->width; - const int height = picture->height; - const int has_alpha = CheckNonOpaque(a_ptr, width, height, step, rgb_stride); - const int is_rgb = (r_ptr < b_ptr); // otherwise it's bgr - - picture->colorspace = has_alpha ? WEBP_YUV420A : WEBP_YUV420; - picture->use_argb = 0; - - // disable smart conversion if source is too small (overkill). - if (width < kMinDimensionIterativeConversion || - height < kMinDimensionIterativeConversion) { - use_iterative_conversion = 0; - } - - if (!WebPPictureAllocYUVA(picture, width, height)) { - return 0; - } - if (has_alpha) { - WebPInitAlphaProcessing(); - assert(step == 4); -#if defined(USE_GAMMA_COMPRESSION) && defined(USE_INVERSE_ALPHA_TABLE) - assert(kAlphaFix + kGammaFix <= 31); -#endif - } - - if (use_iterative_conversion) { - InitGammaTablesF(); - if (!PreprocessARGB(r_ptr, g_ptr, b_ptr, step, rgb_stride, picture)) { - return 0; - } - if (has_alpha) { - WebPExtractAlpha(a_ptr, rgb_stride, width, height, - picture->a, picture->a_stride); - } - } else { - const int uv_width = (width + 1) >> 1; - int use_dsp = (step == 3); // use special function in this case - // temporary storage for accumulated R/G/B values during conversion to U/V - uint16_t* const tmp_rgb = - (uint16_t*)WebPSafeMalloc(4 * uv_width, sizeof(*tmp_rgb)); - uint8_t* dst_y = picture->y; - uint8_t* dst_u = picture->u; - uint8_t* dst_v = picture->v; - uint8_t* dst_a = picture->a; - - VP8Random base_rg; - VP8Random* rg = NULL; - if (dithering > 0.) { - VP8InitRandom(&base_rg, dithering); - rg = &base_rg; - use_dsp = 0; // can't use dsp in this case - } - WebPInitConvertARGBToYUV(); - InitGammaTables(); - - if (tmp_rgb == NULL) return 0; // malloc error - - // Downsample Y/U/V planes, two rows at a time - for (y = 0; y < (height >> 1); ++y) { - int rows_have_alpha = has_alpha; - if (use_dsp) { - if (is_rgb) { - WebPConvertRGB24ToY(r_ptr, dst_y, width); - WebPConvertRGB24ToY(r_ptr + rgb_stride, - dst_y + picture->y_stride, width); - } else { - WebPConvertBGR24ToY(b_ptr, dst_y, width); - WebPConvertBGR24ToY(b_ptr + rgb_stride, - dst_y + picture->y_stride, width); - } - } else { - ConvertRowToY(r_ptr, g_ptr, b_ptr, step, dst_y, width, rg); - ConvertRowToY(r_ptr + rgb_stride, - g_ptr + rgb_stride, - b_ptr + rgb_stride, step, - dst_y + picture->y_stride, width, rg); - } - dst_y += 2 * picture->y_stride; - if (has_alpha) { - rows_have_alpha &= !WebPExtractAlpha(a_ptr, rgb_stride, width, 2, - dst_a, picture->a_stride); - dst_a += 2 * picture->a_stride; - } - // Collect averaged R/G/B(/A) - if (!rows_have_alpha) { - AccumulateRGB(r_ptr, g_ptr, b_ptr, step, rgb_stride, tmp_rgb, width); - } else { - AccumulateRGBA(r_ptr, g_ptr, b_ptr, a_ptr, rgb_stride, tmp_rgb, width); - } - // Convert to U/V - if (rg == NULL) { - WebPConvertRGBA32ToUV(tmp_rgb, dst_u, dst_v, uv_width); - } else { - ConvertRowsToUV(tmp_rgb, dst_u, dst_v, uv_width, rg); - } - dst_u += picture->uv_stride; - dst_v += picture->uv_stride; - r_ptr += 2 * rgb_stride; - b_ptr += 2 * rgb_stride; - g_ptr += 2 * rgb_stride; - if (has_alpha) a_ptr += 2 * rgb_stride; - } - if (height & 1) { // extra last row - int row_has_alpha = has_alpha; - if (use_dsp) { - if (r_ptr < b_ptr) { - WebPConvertRGB24ToY(r_ptr, dst_y, width); - } else { - WebPConvertBGR24ToY(b_ptr, dst_y, width); - } - } else { - ConvertRowToY(r_ptr, g_ptr, b_ptr, step, dst_y, width, rg); - } - if (row_has_alpha) { - row_has_alpha &= !WebPExtractAlpha(a_ptr, 0, width, 1, dst_a, 0); - } - // Collect averaged R/G/B(/A) - if (!row_has_alpha) { - // Collect averaged R/G/B - AccumulateRGB(r_ptr, g_ptr, b_ptr, step, /* rgb_stride = */ 0, - tmp_rgb, width); - } else { - AccumulateRGBA(r_ptr, g_ptr, b_ptr, a_ptr, /* rgb_stride = */ 0, - tmp_rgb, width); - } - if (rg == NULL) { - WebPConvertRGBA32ToUV(tmp_rgb, dst_u, dst_v, uv_width); - } else { - ConvertRowsToUV(tmp_rgb, dst_u, dst_v, uv_width, rg); - } - } - WebPSafeFree(tmp_rgb); - } - return 1; -} - -#undef SUM4 -#undef SUM2 -#undef SUM4ALPHA -#undef SUM2ALPHA - -//------------------------------------------------------------------------------ -// call for ARGB->YUVA conversion - -static int PictureARGBToYUVA(WebPPicture* picture, WebPEncCSP colorspace, - float dithering, int use_iterative_conversion) { - if (picture == NULL) return 0; - if (picture->argb == NULL) { - return WebPEncodingSetError(picture, VP8_ENC_ERROR_NULL_PARAMETER); - } else if ((colorspace & WEBP_CSP_UV_MASK) != WEBP_YUV420) { - return WebPEncodingSetError(picture, VP8_ENC_ERROR_INVALID_CONFIGURATION); - } else { - const uint8_t* const argb = (const uint8_t*)picture->argb; - const uint8_t* const r = ALPHA_IS_LAST ? argb + 2 : argb + 1; - const uint8_t* const g = ALPHA_IS_LAST ? argb + 1 : argb + 2; - const uint8_t* const b = ALPHA_IS_LAST ? argb + 0 : argb + 3; - const uint8_t* const a = ALPHA_IS_LAST ? argb + 3 : argb + 0; - - picture->colorspace = WEBP_YUV420; - return ImportYUVAFromRGBA(r, g, b, a, 4, 4 * picture->argb_stride, - dithering, use_iterative_conversion, picture); - } -} - -int WebPPictureARGBToYUVADithered(WebPPicture* picture, WebPEncCSP colorspace, - float dithering) { - return PictureARGBToYUVA(picture, colorspace, dithering, 0); -} - -int WebPPictureARGBToYUVA(WebPPicture* picture, WebPEncCSP colorspace) { - return PictureARGBToYUVA(picture, colorspace, 0.f, 0); -} - -int WebPPictureSmartARGBToYUVA(WebPPicture* picture) { - return PictureARGBToYUVA(picture, WEBP_YUV420, 0.f, 1); -} - -//------------------------------------------------------------------------------ -// call for YUVA -> ARGB conversion - -int WebPPictureYUVAToARGB(WebPPicture* picture) { - if (picture == NULL) return 0; - if (picture->y == NULL || picture->u == NULL || picture->v == NULL) { - return WebPEncodingSetError(picture, VP8_ENC_ERROR_NULL_PARAMETER); - } - if ((picture->colorspace & WEBP_CSP_ALPHA_BIT) && picture->a == NULL) { - return WebPEncodingSetError(picture, VP8_ENC_ERROR_NULL_PARAMETER); - } - if ((picture->colorspace & WEBP_CSP_UV_MASK) != WEBP_YUV420) { - return WebPEncodingSetError(picture, VP8_ENC_ERROR_INVALID_CONFIGURATION); - } - // Allocate a new argb buffer (discarding the previous one). - if (!WebPPictureAllocARGB(picture, picture->width, picture->height)) return 0; - picture->use_argb = 1; - - // Convert - { - int y; - const int width = picture->width; - const int height = picture->height; - const int argb_stride = 4 * picture->argb_stride; - uint8_t* dst = (uint8_t*)picture->argb; - const uint8_t *cur_u = picture->u, *cur_v = picture->v, *cur_y = picture->y; - WebPUpsampleLinePairFunc upsample = WebPGetLinePairConverter(ALPHA_IS_LAST); - - // First row, with replicated top samples. - upsample(cur_y, NULL, cur_u, cur_v, cur_u, cur_v, dst, NULL, width); - cur_y += picture->y_stride; - dst += argb_stride; - // Center rows. - for (y = 1; y + 1 < height; y += 2) { - const uint8_t* const top_u = cur_u; - const uint8_t* const top_v = cur_v; - cur_u += picture->uv_stride; - cur_v += picture->uv_stride; - upsample(cur_y, cur_y + picture->y_stride, top_u, top_v, cur_u, cur_v, - dst, dst + argb_stride, width); - cur_y += 2 * picture->y_stride; - dst += 2 * argb_stride; - } - // Last row (if needed), with replicated bottom samples. - if (height > 1 && !(height & 1)) { - upsample(cur_y, NULL, cur_u, cur_v, cur_u, cur_v, dst, NULL, width); - } - // Insert alpha values if needed, in replacement for the default 0xff ones. - if (picture->colorspace & WEBP_CSP_ALPHA_BIT) { - for (y = 0; y < height; ++y) { - uint32_t* const argb_dst = picture->argb + y * picture->argb_stride; - const uint8_t* const src = picture->a + y * picture->a_stride; - int x; - for (x = 0; x < width; ++x) { - argb_dst[x] = (argb_dst[x] & 0x00ffffffu) | ((uint32_t)src[x] << 24); - } - } - } - } - return 1; -} - -//------------------------------------------------------------------------------ -// automatic import / conversion - -static int Import(WebPPicture* const picture, - const uint8_t* const rgb, int rgb_stride, - int step, int swap_rb, int import_alpha) { - int y; - const uint8_t* r_ptr = rgb + (swap_rb ? 2 : 0); - const uint8_t* g_ptr = rgb + 1; - const uint8_t* b_ptr = rgb + (swap_rb ? 0 : 2); - const uint8_t* a_ptr = import_alpha ? rgb + 3 : NULL; - const int width = picture->width; - const int height = picture->height; - - if (!picture->use_argb) { - return ImportYUVAFromRGBA(r_ptr, g_ptr, b_ptr, a_ptr, step, rgb_stride, - 0.f /* no dithering */, 0, picture); - } - if (!WebPPictureAlloc(picture)) return 0; - - VP8EncDspARGBInit(); - - if (import_alpha) { - uint32_t* dst = picture->argb; - assert(step == 4); - for (y = 0; y < height; ++y) { - VP8PackARGB(a_ptr, r_ptr, g_ptr, b_ptr, width, dst); - a_ptr += rgb_stride; - r_ptr += rgb_stride; - g_ptr += rgb_stride; - b_ptr += rgb_stride; - dst += picture->argb_stride; - } - } else { - uint32_t* dst = picture->argb; - assert(step >= 3); - for (y = 0; y < height; ++y) { - VP8PackRGB(r_ptr, g_ptr, b_ptr, width, step, dst); - r_ptr += rgb_stride; - g_ptr += rgb_stride; - b_ptr += rgb_stride; - dst += picture->argb_stride; - } - } - return 1; -} - -// Public API - -int WebPPictureImportRGB(WebPPicture* picture, - const uint8_t* rgb, int rgb_stride) { - return (picture != NULL && rgb != NULL) - ? Import(picture, rgb, rgb_stride, 3, 0, 0) - : 0; -} - -int WebPPictureImportBGR(WebPPicture* picture, - const uint8_t* rgb, int rgb_stride) { - return (picture != NULL && rgb != NULL) - ? Import(picture, rgb, rgb_stride, 3, 1, 0) - : 0; -} - -int WebPPictureImportRGBA(WebPPicture* picture, - const uint8_t* rgba, int rgba_stride) { - return (picture != NULL && rgba != NULL) - ? Import(picture, rgba, rgba_stride, 4, 0, 1) - : 0; -} - -int WebPPictureImportBGRA(WebPPicture* picture, - const uint8_t* rgba, int rgba_stride) { - return (picture != NULL && rgba != NULL) - ? Import(picture, rgba, rgba_stride, 4, 1, 1) - : 0; -} - -int WebPPictureImportRGBX(WebPPicture* picture, - const uint8_t* rgba, int rgba_stride) { - return (picture != NULL && rgba != NULL) - ? Import(picture, rgba, rgba_stride, 4, 0, 0) - : 0; -} - -int WebPPictureImportBGRX(WebPPicture* picture, - const uint8_t* rgba, int rgba_stride) { - return (picture != NULL && rgba != NULL) - ? Import(picture, rgba, rgba_stride, 4, 1, 0) - : 0; -} - -//------------------------------------------------------------------------------ diff --git a/thirdparty/libwebp/enc/picture_csp_enc.c b/thirdparty/libwebp/enc/picture_csp_enc.c new file mode 100644 index 0000000000..e5d1c75a66 --- /dev/null +++ b/thirdparty/libwebp/enc/picture_csp_enc.c @@ -0,0 +1,1175 @@ +// Copyright 2014 Google Inc. All Rights Reserved. +// +// Use of this source code is governed by a BSD-style license +// that can be found in the COPYING file in the root of the source +// tree. An additional intellectual property rights grant can be found +// in the file PATENTS. All contributing project authors may +// be found in the AUTHORS file in the root of the source tree. +// ----------------------------------------------------------------------------- +// +// WebPPicture utils for colorspace conversion +// +// Author: Skal (pascal.massimino@gmail.com) + +#include +#include +#include + +#include "./vp8i_enc.h" +#include "../utils/random_utils.h" +#include "../utils/utils.h" +#include "../dsp/yuv.h" + +// Uncomment to disable gamma-compression during RGB->U/V averaging +#define USE_GAMMA_COMPRESSION + +// If defined, use table to compute x / alpha. +#define USE_INVERSE_ALPHA_TABLE + +static const union { + uint32_t argb; + uint8_t bytes[4]; +} test_endian = { 0xff000000u }; +#define ALPHA_IS_LAST (test_endian.bytes[3] == 0xff) + +//------------------------------------------------------------------------------ +// Detection of non-trivial transparency + +// Returns true if alpha[] has non-0xff values. +static int CheckNonOpaque(const uint8_t* alpha, int width, int height, + int x_step, int y_step) { + if (alpha == NULL) return 0; + while (height-- > 0) { + int x; + for (x = 0; x < width * x_step; x += x_step) { + if (alpha[x] != 0xff) return 1; // TODO(skal): check 4/8 bytes at a time. + } + alpha += y_step; + } + return 0; +} + +// Checking for the presence of non-opaque alpha. +int WebPPictureHasTransparency(const WebPPicture* picture) { + if (picture == NULL) return 0; + if (!picture->use_argb) { + return CheckNonOpaque(picture->a, picture->width, picture->height, + 1, picture->a_stride); + } else { + int x, y; + const uint32_t* argb = picture->argb; + if (argb == NULL) return 0; + for (y = 0; y < picture->height; ++y) { + for (x = 0; x < picture->width; ++x) { + if (argb[x] < 0xff000000u) return 1; // test any alpha values != 0xff + } + argb += picture->argb_stride; + } + } + return 0; +} + +//------------------------------------------------------------------------------ +// Code for gamma correction + +#if defined(USE_GAMMA_COMPRESSION) + +// gamma-compensates loss of resolution during chroma subsampling +#define kGamma 0.80 // for now we use a different gamma value than kGammaF +#define kGammaFix 12 // fixed-point precision for linear values +#define kGammaScale ((1 << kGammaFix) - 1) +#define kGammaTabFix 7 // fixed-point fractional bits precision +#define kGammaTabScale (1 << kGammaTabFix) +#define kGammaTabRounder (kGammaTabScale >> 1) +#define kGammaTabSize (1 << (kGammaFix - kGammaTabFix)) + +static int kLinearToGammaTab[kGammaTabSize + 1]; +static uint16_t kGammaToLinearTab[256]; +static volatile int kGammaTablesOk = 0; + +static WEBP_TSAN_IGNORE_FUNCTION void InitGammaTables(void) { + if (!kGammaTablesOk) { + int v; + const double scale = (double)(1 << kGammaTabFix) / kGammaScale; + const double norm = 1. / 255.; + for (v = 0; v <= 255; ++v) { + kGammaToLinearTab[v] = + (uint16_t)(pow(norm * v, kGamma) * kGammaScale + .5); + } + for (v = 0; v <= kGammaTabSize; ++v) { + kLinearToGammaTab[v] = (int)(255. * pow(scale * v, 1. / kGamma) + .5); + } + kGammaTablesOk = 1; + } +} + +static WEBP_INLINE uint32_t GammaToLinear(uint8_t v) { + return kGammaToLinearTab[v]; +} + +static WEBP_INLINE int Interpolate(int v) { + const int tab_pos = v >> (kGammaTabFix + 2); // integer part + const int x = v & ((kGammaTabScale << 2) - 1); // fractional part + const int v0 = kLinearToGammaTab[tab_pos]; + const int v1 = kLinearToGammaTab[tab_pos + 1]; + const int y = v1 * x + v0 * ((kGammaTabScale << 2) - x); // interpolate + assert(tab_pos + 1 < kGammaTabSize + 1); + return y; +} + +// Convert a linear value 'v' to YUV_FIX+2 fixed-point precision +// U/V value, suitable for RGBToU/V calls. +static WEBP_INLINE int LinearToGamma(uint32_t base_value, int shift) { + const int y = Interpolate(base_value << shift); // final uplifted value + return (y + kGammaTabRounder) >> kGammaTabFix; // descale +} + +#else + +static WEBP_TSAN_IGNORE_FUNCTION void InitGammaTables(void) {} +static WEBP_INLINE uint32_t GammaToLinear(uint8_t v) { return v; } +static WEBP_INLINE int LinearToGamma(uint32_t base_value, int shift) { + return (int)(base_value << shift); +} + +#endif // USE_GAMMA_COMPRESSION + +//------------------------------------------------------------------------------ +// RGB -> YUV conversion + +static int RGBToY(int r, int g, int b, VP8Random* const rg) { + return (rg == NULL) ? VP8RGBToY(r, g, b, YUV_HALF) + : VP8RGBToY(r, g, b, VP8RandomBits(rg, YUV_FIX)); +} + +static int RGBToU(int r, int g, int b, VP8Random* const rg) { + return (rg == NULL) ? VP8RGBToU(r, g, b, YUV_HALF << 2) + : VP8RGBToU(r, g, b, VP8RandomBits(rg, YUV_FIX + 2)); +} + +static int RGBToV(int r, int g, int b, VP8Random* const rg) { + return (rg == NULL) ? VP8RGBToV(r, g, b, YUV_HALF << 2) + : VP8RGBToV(r, g, b, VP8RandomBits(rg, YUV_FIX + 2)); +} + +//------------------------------------------------------------------------------ +// Sharp RGB->YUV conversion + +static const int kNumIterations = 4; +static const int kMinDimensionIterativeConversion = 4; + +// We could use SFIX=0 and only uint8_t for fixed_y_t, but it produces some +// banding sometimes. Better use extra precision. +#define SFIX 2 // fixed-point precision of RGB and Y/W +typedef int16_t fixed_t; // signed type with extra SFIX precision for UV +typedef uint16_t fixed_y_t; // unsigned type with extra SFIX precision for W + +#define SHALF (1 << SFIX >> 1) +#define MAX_Y_T ((256 << SFIX) - 1) +#define SROUNDER (1 << (YUV_FIX + SFIX - 1)) + +#if defined(USE_GAMMA_COMPRESSION) + +// float variant of gamma-correction +// We use tables of different size and precision for the Rec709 +// transfer function. +#define kGammaF (1./0.45) +static float kGammaToLinearTabF[MAX_Y_T + 1]; // size scales with Y_FIX +static float kLinearToGammaTabF[kGammaTabSize + 2]; +static volatile int kGammaTablesFOk = 0; + +static WEBP_TSAN_IGNORE_FUNCTION void InitGammaTablesF(void) { + if (!kGammaTablesFOk) { + int v; + const double norm = 1. / MAX_Y_T; + const double scale = 1. / kGammaTabSize; + const double a = 0.099; + const double thresh = 0.018; + for (v = 0; v <= MAX_Y_T; ++v) { + const double g = norm * v; + if (g <= thresh * 4.5) { + kGammaToLinearTabF[v] = (float)(g / 4.5); + } else { + const double a_rec = 1. / (1. + a); + kGammaToLinearTabF[v] = (float)pow(a_rec * (g + a), kGammaF); + } + } + for (v = 0; v <= kGammaTabSize; ++v) { + const double g = scale * v; + double value; + if (g <= thresh) { + value = 4.5 * g; + } else { + value = (1. + a) * pow(g, 1. / kGammaF) - a; + } + kLinearToGammaTabF[v] = (float)(MAX_Y_T * value); + } + // to prevent small rounding errors to cause read-overflow: + kLinearToGammaTabF[kGammaTabSize + 1] = kLinearToGammaTabF[kGammaTabSize]; + kGammaTablesFOk = 1; + } +} + +static WEBP_INLINE float GammaToLinearF(int v) { + return kGammaToLinearTabF[v]; +} + +static WEBP_INLINE int LinearToGammaF(float value) { + const float v = value * kGammaTabSize; + const int tab_pos = (int)v; + const float x = v - (float)tab_pos; // fractional part + const float v0 = kLinearToGammaTabF[tab_pos + 0]; + const float v1 = kLinearToGammaTabF[tab_pos + 1]; + const float y = v1 * x + v0 * (1.f - x); // interpolate + return (int)(y + .5); +} + +#else + +static WEBP_TSAN_IGNORE_FUNCTION void InitGammaTablesF(void) {} +static WEBP_INLINE float GammaToLinearF(int v) { + const float norm = 1.f / MAX_Y_T; + return norm * v; +} +static WEBP_INLINE int LinearToGammaF(float value) { + return (int)(MAX_Y_T * value + .5); +} + +#endif // USE_GAMMA_COMPRESSION + +//------------------------------------------------------------------------------ + +static uint8_t clip_8b(fixed_t v) { + return (!(v & ~0xff)) ? (uint8_t)v : (v < 0) ? 0u : 255u; +} + +static fixed_y_t clip_y(int y) { + return (!(y & ~MAX_Y_T)) ? (fixed_y_t)y : (y < 0) ? 0 : MAX_Y_T; +} + +//------------------------------------------------------------------------------ + +static int RGBToGray(int r, int g, int b) { + const int luma = 13933 * r + 46871 * g + 4732 * b + YUV_HALF; + return (luma >> YUV_FIX); +} + +static float RGBToGrayF(float r, float g, float b) { + return (float)(0.2126 * r + 0.7152 * g + 0.0722 * b); +} + +static int ScaleDown(int a, int b, int c, int d) { + const float A = GammaToLinearF(a); + const float B = GammaToLinearF(b); + const float C = GammaToLinearF(c); + const float D = GammaToLinearF(d); + return LinearToGammaF(0.25f * (A + B + C + D)); +} + +static WEBP_INLINE void UpdateW(const fixed_y_t* src, fixed_y_t* dst, int w) { + int i; + for (i = 0; i < w; ++i) { + const float R = GammaToLinearF(src[0 * w + i]); + const float G = GammaToLinearF(src[1 * w + i]); + const float B = GammaToLinearF(src[2 * w + i]); + const float Y = RGBToGrayF(R, G, B); + dst[i] = (fixed_y_t)LinearToGammaF(Y); + } +} + +static void UpdateChroma(const fixed_y_t* src1, const fixed_y_t* src2, + fixed_t* dst, int uv_w) { + int i; + for (i = 0; i < uv_w; ++i) { + const int r = ScaleDown(src1[0 * uv_w + 0], src1[0 * uv_w + 1], + src2[0 * uv_w + 0], src2[0 * uv_w + 1]); + const int g = ScaleDown(src1[2 * uv_w + 0], src1[2 * uv_w + 1], + src2[2 * uv_w + 0], src2[2 * uv_w + 1]); + const int b = ScaleDown(src1[4 * uv_w + 0], src1[4 * uv_w + 1], + src2[4 * uv_w + 0], src2[4 * uv_w + 1]); + const int W = RGBToGray(r, g, b); + dst[0 * uv_w] = (fixed_t)(r - W); + dst[1 * uv_w] = (fixed_t)(g - W); + dst[2 * uv_w] = (fixed_t)(b - W); + dst += 1; + src1 += 2; + src2 += 2; + } +} + +static void StoreGray(const fixed_y_t* rgb, fixed_y_t* y, int w) { + int i; + for (i = 0; i < w; ++i) { + y[i] = RGBToGray(rgb[0 * w + i], rgb[1 * w + i], rgb[2 * w + i]); + } +} + +//------------------------------------------------------------------------------ + +static WEBP_INLINE fixed_y_t Filter2(int A, int B, int W0) { + const int v0 = (A * 3 + B + 2) >> 2; + return clip_y(v0 + W0); +} + +//------------------------------------------------------------------------------ + +static WEBP_INLINE fixed_y_t UpLift(uint8_t a) { // 8bit -> SFIX + return ((fixed_y_t)a << SFIX) | SHALF; +} + +static void ImportOneRow(const uint8_t* const r_ptr, + const uint8_t* const g_ptr, + const uint8_t* const b_ptr, + int step, + int pic_width, + fixed_y_t* const dst) { + int i; + const int w = (pic_width + 1) & ~1; + for (i = 0; i < pic_width; ++i) { + const int off = i * step; + dst[i + 0 * w] = UpLift(r_ptr[off]); + dst[i + 1 * w] = UpLift(g_ptr[off]); + dst[i + 2 * w] = UpLift(b_ptr[off]); + } + if (pic_width & 1) { // replicate rightmost pixel + dst[pic_width + 0 * w] = dst[pic_width + 0 * w - 1]; + dst[pic_width + 1 * w] = dst[pic_width + 1 * w - 1]; + dst[pic_width + 2 * w] = dst[pic_width + 2 * w - 1]; + } +} + +static void InterpolateTwoRows(const fixed_y_t* const best_y, + const fixed_t* prev_uv, + const fixed_t* cur_uv, + const fixed_t* next_uv, + int w, + fixed_y_t* out1, + fixed_y_t* out2) { + const int uv_w = w >> 1; + const int len = (w - 1) >> 1; // length to filter + int k = 3; + while (k-- > 0) { // process each R/G/B segments in turn + // special boundary case for i==0 + out1[0] = Filter2(cur_uv[0], prev_uv[0], best_y[0]); + out2[0] = Filter2(cur_uv[0], next_uv[0], best_y[w]); + + WebPSharpYUVFilterRow(cur_uv, prev_uv, len, best_y + 0 + 1, out1 + 1); + WebPSharpYUVFilterRow(cur_uv, next_uv, len, best_y + w + 1, out2 + 1); + + // special boundary case for i == w - 1 when w is even + if (!(w & 1)) { + out1[w - 1] = Filter2(cur_uv[uv_w - 1], prev_uv[uv_w - 1], + best_y[w - 1 + 0]); + out2[w - 1] = Filter2(cur_uv[uv_w - 1], next_uv[uv_w - 1], + best_y[w - 1 + w]); + } + out1 += w; + out2 += w; + prev_uv += uv_w; + cur_uv += uv_w; + next_uv += uv_w; + } +} + +static WEBP_INLINE uint8_t ConvertRGBToY(int r, int g, int b) { + const int luma = 16839 * r + 33059 * g + 6420 * b + SROUNDER; + return clip_8b(16 + (luma >> (YUV_FIX + SFIX))); +} + +static WEBP_INLINE uint8_t ConvertRGBToU(int r, int g, int b) { + const int u = -9719 * r - 19081 * g + 28800 * b + SROUNDER; + return clip_8b(128 + (u >> (YUV_FIX + SFIX))); +} + +static WEBP_INLINE uint8_t ConvertRGBToV(int r, int g, int b) { + const int v = +28800 * r - 24116 * g - 4684 * b + SROUNDER; + return clip_8b(128 + (v >> (YUV_FIX + SFIX))); +} + +static int ConvertWRGBToYUV(const fixed_y_t* best_y, const fixed_t* best_uv, + WebPPicture* const picture) { + int i, j; + uint8_t* dst_y = picture->y; + uint8_t* dst_u = picture->u; + uint8_t* dst_v = picture->v; + const fixed_t* const best_uv_base = best_uv; + const int w = (picture->width + 1) & ~1; + const int h = (picture->height + 1) & ~1; + const int uv_w = w >> 1; + const int uv_h = h >> 1; + for (best_uv = best_uv_base, j = 0; j < picture->height; ++j) { + for (i = 0; i < picture->width; ++i) { + const int off = (i >> 1); + const int W = best_y[i]; + const int r = best_uv[off + 0 * uv_w] + W; + const int g = best_uv[off + 1 * uv_w] + W; + const int b = best_uv[off + 2 * uv_w] + W; + dst_y[i] = ConvertRGBToY(r, g, b); + } + best_y += w; + best_uv += (j & 1) * 3 * uv_w; + dst_y += picture->y_stride; + } + for (best_uv = best_uv_base, j = 0; j < uv_h; ++j) { + for (i = 0; i < uv_w; ++i) { + const int off = i; + const int r = best_uv[off + 0 * uv_w]; + const int g = best_uv[off + 1 * uv_w]; + const int b = best_uv[off + 2 * uv_w]; + dst_u[i] = ConvertRGBToU(r, g, b); + dst_v[i] = ConvertRGBToV(r, g, b); + } + best_uv += 3 * uv_w; + dst_u += picture->uv_stride; + dst_v += picture->uv_stride; + } + return 1; +} + +//------------------------------------------------------------------------------ +// Main function + +#define SAFE_ALLOC(W, H, T) ((T*)WebPSafeMalloc((W) * (H), sizeof(T))) + +static int PreprocessARGB(const uint8_t* r_ptr, + const uint8_t* g_ptr, + const uint8_t* b_ptr, + int step, int rgb_stride, + WebPPicture* const picture) { + // we expand the right/bottom border if needed + const int w = (picture->width + 1) & ~1; + const int h = (picture->height + 1) & ~1; + const int uv_w = w >> 1; + const int uv_h = h >> 1; + uint64_t prev_diff_y_sum = ~0; + int j, iter; + + // TODO(skal): allocate one big memory chunk. But for now, it's easier + // for valgrind debugging to have several chunks. + fixed_y_t* const tmp_buffer = SAFE_ALLOC(w * 3, 2, fixed_y_t); // scratch + fixed_y_t* const best_y_base = SAFE_ALLOC(w, h, fixed_y_t); + fixed_y_t* const target_y_base = SAFE_ALLOC(w, h, fixed_y_t); + fixed_y_t* const best_rgb_y = SAFE_ALLOC(w, 2, fixed_y_t); + fixed_t* const best_uv_base = SAFE_ALLOC(uv_w * 3, uv_h, fixed_t); + fixed_t* const target_uv_base = SAFE_ALLOC(uv_w * 3, uv_h, fixed_t); + fixed_t* const best_rgb_uv = SAFE_ALLOC(uv_w * 3, 1, fixed_t); + fixed_y_t* best_y = best_y_base; + fixed_y_t* target_y = target_y_base; + fixed_t* best_uv = best_uv_base; + fixed_t* target_uv = target_uv_base; + const uint64_t diff_y_threshold = (uint64_t)(3.0 * w * h); + int ok; + + if (best_y_base == NULL || best_uv_base == NULL || + target_y_base == NULL || target_uv_base == NULL || + best_rgb_y == NULL || best_rgb_uv == NULL || + tmp_buffer == NULL) { + ok = WebPEncodingSetError(picture, VP8_ENC_ERROR_OUT_OF_MEMORY); + goto End; + } + assert(picture->width >= kMinDimensionIterativeConversion); + assert(picture->height >= kMinDimensionIterativeConversion); + + WebPInitConvertARGBToYUV(); + + // Import RGB samples to W/RGB representation. + for (j = 0; j < picture->height; j += 2) { + const int is_last_row = (j == picture->height - 1); + fixed_y_t* const src1 = tmp_buffer + 0 * w; + fixed_y_t* const src2 = tmp_buffer + 3 * w; + + // prepare two rows of input + ImportOneRow(r_ptr, g_ptr, b_ptr, step, picture->width, src1); + if (!is_last_row) { + ImportOneRow(r_ptr + rgb_stride, g_ptr + rgb_stride, b_ptr + rgb_stride, + step, picture->width, src2); + } else { + memcpy(src2, src1, 3 * w * sizeof(*src2)); + } + StoreGray(src1, best_y + 0, w); + StoreGray(src2, best_y + w, w); + + UpdateW(src1, target_y, w); + UpdateW(src2, target_y + w, w); + UpdateChroma(src1, src2, target_uv, uv_w); + memcpy(best_uv, target_uv, 3 * uv_w * sizeof(*best_uv)); + best_y += 2 * w; + best_uv += 3 * uv_w; + target_y += 2 * w; + target_uv += 3 * uv_w; + r_ptr += 2 * rgb_stride; + g_ptr += 2 * rgb_stride; + b_ptr += 2 * rgb_stride; + } + + // Iterate and resolve clipping conflicts. + for (iter = 0; iter < kNumIterations; ++iter) { + const fixed_t* cur_uv = best_uv_base; + const fixed_t* prev_uv = best_uv_base; + uint64_t diff_y_sum = 0; + + best_y = best_y_base; + best_uv = best_uv_base; + target_y = target_y_base; + target_uv = target_uv_base; + for (j = 0; j < h; j += 2) { + fixed_y_t* const src1 = tmp_buffer + 0 * w; + fixed_y_t* const src2 = tmp_buffer + 3 * w; + { + const fixed_t* const next_uv = cur_uv + ((j < h - 2) ? 3 * uv_w : 0); + InterpolateTwoRows(best_y, prev_uv, cur_uv, next_uv, w, src1, src2); + prev_uv = cur_uv; + cur_uv = next_uv; + } + + UpdateW(src1, best_rgb_y + 0 * w, w); + UpdateW(src2, best_rgb_y + 1 * w, w); + UpdateChroma(src1, src2, best_rgb_uv, uv_w); + + // update two rows of Y and one row of RGB + diff_y_sum += WebPSharpYUVUpdateY(target_y, best_rgb_y, best_y, 2 * w); + WebPSharpYUVUpdateRGB(target_uv, best_rgb_uv, best_uv, 3 * uv_w); + + best_y += 2 * w; + best_uv += 3 * uv_w; + target_y += 2 * w; + target_uv += 3 * uv_w; + } + // test exit condition + if (iter > 0) { + if (diff_y_sum < diff_y_threshold) break; + if (diff_y_sum > prev_diff_y_sum) break; + } + prev_diff_y_sum = diff_y_sum; + } + // final reconstruction + ok = ConvertWRGBToYUV(best_y_base, best_uv_base, picture); + + End: + WebPSafeFree(best_y_base); + WebPSafeFree(best_uv_base); + WebPSafeFree(target_y_base); + WebPSafeFree(target_uv_base); + WebPSafeFree(best_rgb_y); + WebPSafeFree(best_rgb_uv); + WebPSafeFree(tmp_buffer); + return ok; +} +#undef SAFE_ALLOC + +//------------------------------------------------------------------------------ +// "Fast" regular RGB->YUV + +#define SUM4(ptr, step) LinearToGamma( \ + GammaToLinear((ptr)[0]) + \ + GammaToLinear((ptr)[(step)]) + \ + GammaToLinear((ptr)[rgb_stride]) + \ + GammaToLinear((ptr)[rgb_stride + (step)]), 0) \ + +#define SUM2(ptr) \ + LinearToGamma(GammaToLinear((ptr)[0]) + GammaToLinear((ptr)[rgb_stride]), 1) + +#define SUM2ALPHA(ptr) ((ptr)[0] + (ptr)[rgb_stride]) +#define SUM4ALPHA(ptr) (SUM2ALPHA(ptr) + SUM2ALPHA((ptr) + 4)) + +#if defined(USE_INVERSE_ALPHA_TABLE) + +static const int kAlphaFix = 19; +// Following table is (1 << kAlphaFix) / a. The (v * kInvAlpha[a]) >> kAlphaFix +// formula is then equal to v / a in most (99.6%) cases. Note that this table +// and constant are adjusted very tightly to fit 32b arithmetic. +// In particular, they use the fact that the operands for 'v / a' are actually +// derived as v = (a0.p0 + a1.p1 + a2.p2 + a3.p3) and a = a0 + a1 + a2 + a3 +// with ai in [0..255] and pi in [0..1<> (kAlphaFix - 2)) + +#else + +#define DIVIDE_BY_ALPHA(sum, a) (4 * (sum) / (a)) + +#endif // USE_INVERSE_ALPHA_TABLE + +static WEBP_INLINE int LinearToGammaWeighted(const uint8_t* src, + const uint8_t* a_ptr, + uint32_t total_a, int step, + int rgb_stride) { + const uint32_t sum = + a_ptr[0] * GammaToLinear(src[0]) + + a_ptr[step] * GammaToLinear(src[step]) + + a_ptr[rgb_stride] * GammaToLinear(src[rgb_stride]) + + a_ptr[rgb_stride + step] * GammaToLinear(src[rgb_stride + step]); + assert(total_a > 0 && total_a <= 4 * 0xff); +#if defined(USE_INVERSE_ALPHA_TABLE) + assert((uint64_t)sum * kInvAlpha[total_a] < ((uint64_t)1 << 32)); +#endif + return LinearToGamma(DIVIDE_BY_ALPHA(sum, total_a), 0); +} + +static WEBP_INLINE void ConvertRowToY(const uint8_t* const r_ptr, + const uint8_t* const g_ptr, + const uint8_t* const b_ptr, + int step, + uint8_t* const dst_y, + int width, + VP8Random* const rg) { + int i, j; + for (i = 0, j = 0; i < width; i += 1, j += step) { + dst_y[i] = RGBToY(r_ptr[j], g_ptr[j], b_ptr[j], rg); + } +} + +static WEBP_INLINE void AccumulateRGBA(const uint8_t* const r_ptr, + const uint8_t* const g_ptr, + const uint8_t* const b_ptr, + const uint8_t* const a_ptr, + int rgb_stride, + uint16_t* dst, int width) { + int i, j; + // we loop over 2x2 blocks and produce one R/G/B/A value for each. + for (i = 0, j = 0; i < (width >> 1); i += 1, j += 2 * 4, dst += 4) { + const uint32_t a = SUM4ALPHA(a_ptr + j); + int r, g, b; + if (a == 4 * 0xff || a == 0) { + r = SUM4(r_ptr + j, 4); + g = SUM4(g_ptr + j, 4); + b = SUM4(b_ptr + j, 4); + } else { + r = LinearToGammaWeighted(r_ptr + j, a_ptr + j, a, 4, rgb_stride); + g = LinearToGammaWeighted(g_ptr + j, a_ptr + j, a, 4, rgb_stride); + b = LinearToGammaWeighted(b_ptr + j, a_ptr + j, a, 4, rgb_stride); + } + dst[0] = r; + dst[1] = g; + dst[2] = b; + dst[3] = a; + } + if (width & 1) { + const uint32_t a = 2u * SUM2ALPHA(a_ptr + j); + int r, g, b; + if (a == 4 * 0xff || a == 0) { + r = SUM2(r_ptr + j); + g = SUM2(g_ptr + j); + b = SUM2(b_ptr + j); + } else { + r = LinearToGammaWeighted(r_ptr + j, a_ptr + j, a, 0, rgb_stride); + g = LinearToGammaWeighted(g_ptr + j, a_ptr + j, a, 0, rgb_stride); + b = LinearToGammaWeighted(b_ptr + j, a_ptr + j, a, 0, rgb_stride); + } + dst[0] = r; + dst[1] = g; + dst[2] = b; + dst[3] = a; + } +} + +static WEBP_INLINE void AccumulateRGB(const uint8_t* const r_ptr, + const uint8_t* const g_ptr, + const uint8_t* const b_ptr, + int step, int rgb_stride, + uint16_t* dst, int width) { + int i, j; + for (i = 0, j = 0; i < (width >> 1); i += 1, j += 2 * step, dst += 4) { + dst[0] = SUM4(r_ptr + j, step); + dst[1] = SUM4(g_ptr + j, step); + dst[2] = SUM4(b_ptr + j, step); + } + if (width & 1) { + dst[0] = SUM2(r_ptr + j); + dst[1] = SUM2(g_ptr + j); + dst[2] = SUM2(b_ptr + j); + } +} + +static WEBP_INLINE void ConvertRowsToUV(const uint16_t* rgb, + uint8_t* const dst_u, + uint8_t* const dst_v, + int width, + VP8Random* const rg) { + int i; + for (i = 0; i < width; i += 1, rgb += 4) { + const int r = rgb[0], g = rgb[1], b = rgb[2]; + dst_u[i] = RGBToU(r, g, b, rg); + dst_v[i] = RGBToV(r, g, b, rg); + } +} + +static int ImportYUVAFromRGBA(const uint8_t* r_ptr, + const uint8_t* g_ptr, + const uint8_t* b_ptr, + const uint8_t* a_ptr, + int step, // bytes per pixel + int rgb_stride, // bytes per scanline + float dithering, + int use_iterative_conversion, + WebPPicture* const picture) { + int y; + const int width = picture->width; + const int height = picture->height; + const int has_alpha = CheckNonOpaque(a_ptr, width, height, step, rgb_stride); + const int is_rgb = (r_ptr < b_ptr); // otherwise it's bgr + + picture->colorspace = has_alpha ? WEBP_YUV420A : WEBP_YUV420; + picture->use_argb = 0; + + // disable smart conversion if source is too small (overkill). + if (width < kMinDimensionIterativeConversion || + height < kMinDimensionIterativeConversion) { + use_iterative_conversion = 0; + } + + if (!WebPPictureAllocYUVA(picture, width, height)) { + return 0; + } + if (has_alpha) { + WebPInitAlphaProcessing(); + assert(step == 4); +#if defined(USE_GAMMA_COMPRESSION) && defined(USE_INVERSE_ALPHA_TABLE) + assert(kAlphaFix + kGammaFix <= 31); +#endif + } + + if (use_iterative_conversion) { + InitGammaTablesF(); + if (!PreprocessARGB(r_ptr, g_ptr, b_ptr, step, rgb_stride, picture)) { + return 0; + } + if (has_alpha) { + WebPExtractAlpha(a_ptr, rgb_stride, width, height, + picture->a, picture->a_stride); + } + } else { + const int uv_width = (width + 1) >> 1; + int use_dsp = (step == 3); // use special function in this case + // temporary storage for accumulated R/G/B values during conversion to U/V + uint16_t* const tmp_rgb = + (uint16_t*)WebPSafeMalloc(4 * uv_width, sizeof(*tmp_rgb)); + uint8_t* dst_y = picture->y; + uint8_t* dst_u = picture->u; + uint8_t* dst_v = picture->v; + uint8_t* dst_a = picture->a; + + VP8Random base_rg; + VP8Random* rg = NULL; + if (dithering > 0.) { + VP8InitRandom(&base_rg, dithering); + rg = &base_rg; + use_dsp = 0; // can't use dsp in this case + } + WebPInitConvertARGBToYUV(); + InitGammaTables(); + + if (tmp_rgb == NULL) return 0; // malloc error + + // Downsample Y/U/V planes, two rows at a time + for (y = 0; y < (height >> 1); ++y) { + int rows_have_alpha = has_alpha; + if (use_dsp) { + if (is_rgb) { + WebPConvertRGB24ToY(r_ptr, dst_y, width); + WebPConvertRGB24ToY(r_ptr + rgb_stride, + dst_y + picture->y_stride, width); + } else { + WebPConvertBGR24ToY(b_ptr, dst_y, width); + WebPConvertBGR24ToY(b_ptr + rgb_stride, + dst_y + picture->y_stride, width); + } + } else { + ConvertRowToY(r_ptr, g_ptr, b_ptr, step, dst_y, width, rg); + ConvertRowToY(r_ptr + rgb_stride, + g_ptr + rgb_stride, + b_ptr + rgb_stride, step, + dst_y + picture->y_stride, width, rg); + } + dst_y += 2 * picture->y_stride; + if (has_alpha) { + rows_have_alpha &= !WebPExtractAlpha(a_ptr, rgb_stride, width, 2, + dst_a, picture->a_stride); + dst_a += 2 * picture->a_stride; + } + // Collect averaged R/G/B(/A) + if (!rows_have_alpha) { + AccumulateRGB(r_ptr, g_ptr, b_ptr, step, rgb_stride, tmp_rgb, width); + } else { + AccumulateRGBA(r_ptr, g_ptr, b_ptr, a_ptr, rgb_stride, tmp_rgb, width); + } + // Convert to U/V + if (rg == NULL) { + WebPConvertRGBA32ToUV(tmp_rgb, dst_u, dst_v, uv_width); + } else { + ConvertRowsToUV(tmp_rgb, dst_u, dst_v, uv_width, rg); + } + dst_u += picture->uv_stride; + dst_v += picture->uv_stride; + r_ptr += 2 * rgb_stride; + b_ptr += 2 * rgb_stride; + g_ptr += 2 * rgb_stride; + if (has_alpha) a_ptr += 2 * rgb_stride; + } + if (height & 1) { // extra last row + int row_has_alpha = has_alpha; + if (use_dsp) { + if (r_ptr < b_ptr) { + WebPConvertRGB24ToY(r_ptr, dst_y, width); + } else { + WebPConvertBGR24ToY(b_ptr, dst_y, width); + } + } else { + ConvertRowToY(r_ptr, g_ptr, b_ptr, step, dst_y, width, rg); + } + if (row_has_alpha) { + row_has_alpha &= !WebPExtractAlpha(a_ptr, 0, width, 1, dst_a, 0); + } + // Collect averaged R/G/B(/A) + if (!row_has_alpha) { + // Collect averaged R/G/B + AccumulateRGB(r_ptr, g_ptr, b_ptr, step, /* rgb_stride = */ 0, + tmp_rgb, width); + } else { + AccumulateRGBA(r_ptr, g_ptr, b_ptr, a_ptr, /* rgb_stride = */ 0, + tmp_rgb, width); + } + if (rg == NULL) { + WebPConvertRGBA32ToUV(tmp_rgb, dst_u, dst_v, uv_width); + } else { + ConvertRowsToUV(tmp_rgb, dst_u, dst_v, uv_width, rg); + } + } + WebPSafeFree(tmp_rgb); + } + return 1; +} + +#undef SUM4 +#undef SUM2 +#undef SUM4ALPHA +#undef SUM2ALPHA + +//------------------------------------------------------------------------------ +// call for ARGB->YUVA conversion + +static int PictureARGBToYUVA(WebPPicture* picture, WebPEncCSP colorspace, + float dithering, int use_iterative_conversion) { + if (picture == NULL) return 0; + if (picture->argb == NULL) { + return WebPEncodingSetError(picture, VP8_ENC_ERROR_NULL_PARAMETER); + } else if ((colorspace & WEBP_CSP_UV_MASK) != WEBP_YUV420) { + return WebPEncodingSetError(picture, VP8_ENC_ERROR_INVALID_CONFIGURATION); + } else { + const uint8_t* const argb = (const uint8_t*)picture->argb; + const uint8_t* const r = ALPHA_IS_LAST ? argb + 2 : argb + 1; + const uint8_t* const g = ALPHA_IS_LAST ? argb + 1 : argb + 2; + const uint8_t* const b = ALPHA_IS_LAST ? argb + 0 : argb + 3; + const uint8_t* const a = ALPHA_IS_LAST ? argb + 3 : argb + 0; + + picture->colorspace = WEBP_YUV420; + return ImportYUVAFromRGBA(r, g, b, a, 4, 4 * picture->argb_stride, + dithering, use_iterative_conversion, picture); + } +} + +int WebPPictureARGBToYUVADithered(WebPPicture* picture, WebPEncCSP colorspace, + float dithering) { + return PictureARGBToYUVA(picture, colorspace, dithering, 0); +} + +int WebPPictureARGBToYUVA(WebPPicture* picture, WebPEncCSP colorspace) { + return PictureARGBToYUVA(picture, colorspace, 0.f, 0); +} + +int WebPPictureSharpARGBToYUVA(WebPPicture* picture) { + return PictureARGBToYUVA(picture, WEBP_YUV420, 0.f, 1); +} +// for backward compatibility +int WebPPictureSmartARGBToYUVA(WebPPicture* picture) { + return WebPPictureSharpARGBToYUVA(picture); +} + +//------------------------------------------------------------------------------ +// call for YUVA -> ARGB conversion + +int WebPPictureYUVAToARGB(WebPPicture* picture) { + if (picture == NULL) return 0; + if (picture->y == NULL || picture->u == NULL || picture->v == NULL) { + return WebPEncodingSetError(picture, VP8_ENC_ERROR_NULL_PARAMETER); + } + if ((picture->colorspace & WEBP_CSP_ALPHA_BIT) && picture->a == NULL) { + return WebPEncodingSetError(picture, VP8_ENC_ERROR_NULL_PARAMETER); + } + if ((picture->colorspace & WEBP_CSP_UV_MASK) != WEBP_YUV420) { + return WebPEncodingSetError(picture, VP8_ENC_ERROR_INVALID_CONFIGURATION); + } + // Allocate a new argb buffer (discarding the previous one). + if (!WebPPictureAllocARGB(picture, picture->width, picture->height)) return 0; + picture->use_argb = 1; + + // Convert + { + int y; + const int width = picture->width; + const int height = picture->height; + const int argb_stride = 4 * picture->argb_stride; + uint8_t* dst = (uint8_t*)picture->argb; + const uint8_t *cur_u = picture->u, *cur_v = picture->v, *cur_y = picture->y; + WebPUpsampleLinePairFunc upsample = WebPGetLinePairConverter(ALPHA_IS_LAST); + + // First row, with replicated top samples. + upsample(cur_y, NULL, cur_u, cur_v, cur_u, cur_v, dst, NULL, width); + cur_y += picture->y_stride; + dst += argb_stride; + // Center rows. + for (y = 1; y + 1 < height; y += 2) { + const uint8_t* const top_u = cur_u; + const uint8_t* const top_v = cur_v; + cur_u += picture->uv_stride; + cur_v += picture->uv_stride; + upsample(cur_y, cur_y + picture->y_stride, top_u, top_v, cur_u, cur_v, + dst, dst + argb_stride, width); + cur_y += 2 * picture->y_stride; + dst += 2 * argb_stride; + } + // Last row (if needed), with replicated bottom samples. + if (height > 1 && !(height & 1)) { + upsample(cur_y, NULL, cur_u, cur_v, cur_u, cur_v, dst, NULL, width); + } + // Insert alpha values if needed, in replacement for the default 0xff ones. + if (picture->colorspace & WEBP_CSP_ALPHA_BIT) { + for (y = 0; y < height; ++y) { + uint32_t* const argb_dst = picture->argb + y * picture->argb_stride; + const uint8_t* const src = picture->a + y * picture->a_stride; + int x; + for (x = 0; x < width; ++x) { + argb_dst[x] = (argb_dst[x] & 0x00ffffffu) | ((uint32_t)src[x] << 24); + } + } + } + } + return 1; +} + +//------------------------------------------------------------------------------ +// automatic import / conversion + +static int Import(WebPPicture* const picture, + const uint8_t* const rgb, int rgb_stride, + int step, int swap_rb, int import_alpha) { + int y; + const uint8_t* r_ptr = rgb + (swap_rb ? 2 : 0); + const uint8_t* g_ptr = rgb + 1; + const uint8_t* b_ptr = rgb + (swap_rb ? 0 : 2); + const uint8_t* a_ptr = import_alpha ? rgb + 3 : NULL; + const int width = picture->width; + const int height = picture->height; + + if (!picture->use_argb) { + return ImportYUVAFromRGBA(r_ptr, g_ptr, b_ptr, a_ptr, step, rgb_stride, + 0.f /* no dithering */, 0, picture); + } + if (!WebPPictureAlloc(picture)) return 0; + + VP8EncDspARGBInit(); + + if (import_alpha) { + uint32_t* dst = picture->argb; + assert(step == 4); + for (y = 0; y < height; ++y) { + VP8PackARGB(a_ptr, r_ptr, g_ptr, b_ptr, width, dst); + a_ptr += rgb_stride; + r_ptr += rgb_stride; + g_ptr += rgb_stride; + b_ptr += rgb_stride; + dst += picture->argb_stride; + } + } else { + uint32_t* dst = picture->argb; + assert(step >= 3); + for (y = 0; y < height; ++y) { + VP8PackRGB(r_ptr, g_ptr, b_ptr, width, step, dst); + r_ptr += rgb_stride; + g_ptr += rgb_stride; + b_ptr += rgb_stride; + dst += picture->argb_stride; + } + } + return 1; +} + +// Public API + +int WebPPictureImportRGB(WebPPicture* picture, + const uint8_t* rgb, int rgb_stride) { + return (picture != NULL && rgb != NULL) + ? Import(picture, rgb, rgb_stride, 3, 0, 0) + : 0; +} + +int WebPPictureImportBGR(WebPPicture* picture, + const uint8_t* rgb, int rgb_stride) { + return (picture != NULL && rgb != NULL) + ? Import(picture, rgb, rgb_stride, 3, 1, 0) + : 0; +} + +int WebPPictureImportRGBA(WebPPicture* picture, + const uint8_t* rgba, int rgba_stride) { + return (picture != NULL && rgba != NULL) + ? Import(picture, rgba, rgba_stride, 4, 0, 1) + : 0; +} + +int WebPPictureImportBGRA(WebPPicture* picture, + const uint8_t* rgba, int rgba_stride) { + return (picture != NULL && rgba != NULL) + ? Import(picture, rgba, rgba_stride, 4, 1, 1) + : 0; +} + +int WebPPictureImportRGBX(WebPPicture* picture, + const uint8_t* rgba, int rgba_stride) { + return (picture != NULL && rgba != NULL) + ? Import(picture, rgba, rgba_stride, 4, 0, 0) + : 0; +} + +int WebPPictureImportBGRX(WebPPicture* picture, + const uint8_t* rgba, int rgba_stride) { + return (picture != NULL && rgba != NULL) + ? Import(picture, rgba, rgba_stride, 4, 1, 0) + : 0; +} + +//------------------------------------------------------------------------------ diff --git a/thirdparty/libwebp/enc/picture_enc.c b/thirdparty/libwebp/enc/picture_enc.c new file mode 100644 index 0000000000..dfa66510fb --- /dev/null +++ b/thirdparty/libwebp/enc/picture_enc.c @@ -0,0 +1,293 @@ +// Copyright 2011 Google Inc. All Rights Reserved. +// +// Use of this source code is governed by a BSD-style license +// that can be found in the COPYING file in the root of the source +// tree. An additional intellectual property rights grant can be found +// in the file PATENTS. All contributing project authors may +// be found in the AUTHORS file in the root of the source tree. +// ----------------------------------------------------------------------------- +// +// WebPPicture class basis +// +// Author: Skal (pascal.massimino@gmail.com) + +#include +#include + +#include "./vp8i_enc.h" +#include "../dsp/dsp.h" +#include "../utils/utils.h" + +//------------------------------------------------------------------------------ +// WebPPicture +//------------------------------------------------------------------------------ + +static int DummyWriter(const uint8_t* data, size_t data_size, + const WebPPicture* const picture) { + // The following are to prevent 'unused variable' error message. + (void)data; + (void)data_size; + (void)picture; + return 1; +} + +int WebPPictureInitInternal(WebPPicture* picture, int version) { + if (WEBP_ABI_IS_INCOMPATIBLE(version, WEBP_ENCODER_ABI_VERSION)) { + return 0; // caller/system version mismatch! + } + if (picture != NULL) { + memset(picture, 0, sizeof(*picture)); + picture->writer = DummyWriter; + WebPEncodingSetError(picture, VP8_ENC_OK); + } + return 1; +} + +//------------------------------------------------------------------------------ + +static void WebPPictureResetBufferARGB(WebPPicture* const picture) { + picture->memory_argb_ = NULL; + picture->argb = NULL; + picture->argb_stride = 0; +} + +static void WebPPictureResetBufferYUVA(WebPPicture* const picture) { + picture->memory_ = NULL; + picture->y = picture->u = picture->v = picture->a = NULL; + picture->y_stride = picture->uv_stride = 0; + picture->a_stride = 0; +} + +void WebPPictureResetBuffers(WebPPicture* const picture) { + WebPPictureResetBufferARGB(picture); + WebPPictureResetBufferYUVA(picture); +} + +int WebPPictureAllocARGB(WebPPicture* const picture, int width, int height) { + void* memory; + const uint64_t argb_size = (uint64_t)width * height; + + assert(picture != NULL); + + WebPSafeFree(picture->memory_argb_); + WebPPictureResetBufferARGB(picture); + + if (width <= 0 || height <= 0) { + return WebPEncodingSetError(picture, VP8_ENC_ERROR_BAD_DIMENSION); + } + // allocate a new buffer. + memory = WebPSafeMalloc(argb_size, sizeof(*picture->argb)); + if (memory == NULL) { + return WebPEncodingSetError(picture, VP8_ENC_ERROR_OUT_OF_MEMORY); + } + // TODO(skal): align plane to cache line? + picture->memory_argb_ = memory; + picture->argb = (uint32_t*)memory; + picture->argb_stride = width; + return 1; +} + +int WebPPictureAllocYUVA(WebPPicture* const picture, int width, int height) { + const WebPEncCSP uv_csp = + (WebPEncCSP)((int)picture->colorspace & WEBP_CSP_UV_MASK); + const int has_alpha = (int)picture->colorspace & WEBP_CSP_ALPHA_BIT; + const int y_stride = width; + const int uv_width = (width + 1) >> 1; + const int uv_height = (height + 1) >> 1; + const int uv_stride = uv_width; + int a_width, a_stride; + uint64_t y_size, uv_size, a_size, total_size; + uint8_t* mem; + + assert(picture != NULL); + + WebPSafeFree(picture->memory_); + WebPPictureResetBufferYUVA(picture); + + if (uv_csp != WEBP_YUV420) { + return WebPEncodingSetError(picture, VP8_ENC_ERROR_INVALID_CONFIGURATION); + } + + // alpha + a_width = has_alpha ? width : 0; + a_stride = a_width; + y_size = (uint64_t)y_stride * height; + uv_size = (uint64_t)uv_stride * uv_height; + a_size = (uint64_t)a_stride * height; + + total_size = y_size + a_size + 2 * uv_size; + + // Security and validation checks + if (width <= 0 || height <= 0 || // luma/alpha param error + uv_width < 0 || uv_height < 0) { // u/v param error + return WebPEncodingSetError(picture, VP8_ENC_ERROR_BAD_DIMENSION); + } + // allocate a new buffer. + mem = (uint8_t*)WebPSafeMalloc(total_size, sizeof(*mem)); + if (mem == NULL) { + return WebPEncodingSetError(picture, VP8_ENC_ERROR_OUT_OF_MEMORY); + } + + // From now on, we're in the clear, we can no longer fail... + picture->memory_ = (void*)mem; + picture->y_stride = y_stride; + picture->uv_stride = uv_stride; + picture->a_stride = a_stride; + + // TODO(skal): we could align the y/u/v planes and adjust stride. + picture->y = mem; + mem += y_size; + + picture->u = mem; + mem += uv_size; + picture->v = mem; + mem += uv_size; + + if (a_size > 0) { + picture->a = mem; + mem += a_size; + } + (void)mem; // makes the static analyzer happy + return 1; +} + +int WebPPictureAlloc(WebPPicture* picture) { + if (picture != NULL) { + const int width = picture->width; + const int height = picture->height; + + WebPPictureFree(picture); // erase previous buffer + + if (!picture->use_argb) { + return WebPPictureAllocYUVA(picture, width, height); + } else { + return WebPPictureAllocARGB(picture, width, height); + } + } + return 1; +} + +void WebPPictureFree(WebPPicture* picture) { + if (picture != NULL) { + WebPSafeFree(picture->memory_); + WebPSafeFree(picture->memory_argb_); + WebPPictureResetBuffers(picture); + } +} + +//------------------------------------------------------------------------------ +// WebPMemoryWriter: Write-to-memory + +void WebPMemoryWriterInit(WebPMemoryWriter* writer) { + writer->mem = NULL; + writer->size = 0; + writer->max_size = 0; +} + +int WebPMemoryWrite(const uint8_t* data, size_t data_size, + const WebPPicture* picture) { + WebPMemoryWriter* const w = (WebPMemoryWriter*)picture->custom_ptr; + uint64_t next_size; + if (w == NULL) { + return 1; + } + next_size = (uint64_t)w->size + data_size; + if (next_size > w->max_size) { + uint8_t* new_mem; + uint64_t next_max_size = 2ULL * w->max_size; + if (next_max_size < next_size) next_max_size = next_size; + if (next_max_size < 8192ULL) next_max_size = 8192ULL; + new_mem = (uint8_t*)WebPSafeMalloc(next_max_size, 1); + if (new_mem == NULL) { + return 0; + } + if (w->size > 0) { + memcpy(new_mem, w->mem, w->size); + } + WebPSafeFree(w->mem); + w->mem = new_mem; + // down-cast is ok, thanks to WebPSafeMalloc + w->max_size = (size_t)next_max_size; + } + if (data_size > 0) { + memcpy(w->mem + w->size, data, data_size); + w->size += data_size; + } + return 1; +} + +void WebPMemoryWriterClear(WebPMemoryWriter* writer) { + if (writer != NULL) { + WebPSafeFree(writer->mem); + writer->mem = NULL; + writer->size = 0; + writer->max_size = 0; + } +} + +//------------------------------------------------------------------------------ +// Simplest high-level calls: + +typedef int (*Importer)(WebPPicture* const, const uint8_t* const, int); + +static size_t Encode(const uint8_t* rgba, int width, int height, int stride, + Importer import, float quality_factor, int lossless, + uint8_t** output) { + WebPPicture pic; + WebPConfig config; + WebPMemoryWriter wrt; + int ok; + + if (output == NULL) return 0; + + if (!WebPConfigPreset(&config, WEBP_PRESET_DEFAULT, quality_factor) || + !WebPPictureInit(&pic)) { + return 0; // shouldn't happen, except if system installation is broken + } + + config.lossless = !!lossless; + pic.use_argb = !!lossless; + pic.width = width; + pic.height = height; + pic.writer = WebPMemoryWrite; + pic.custom_ptr = &wrt; + WebPMemoryWriterInit(&wrt); + + ok = import(&pic, rgba, stride) && WebPEncode(&config, &pic); + WebPPictureFree(&pic); + if (!ok) { + WebPMemoryWriterClear(&wrt); + *output = NULL; + return 0; + } + *output = wrt.mem; + return wrt.size; +} + +#define ENCODE_FUNC(NAME, IMPORTER) \ +size_t NAME(const uint8_t* in, int w, int h, int bps, float q, \ + uint8_t** out) { \ + return Encode(in, w, h, bps, IMPORTER, q, 0, out); \ +} + +ENCODE_FUNC(WebPEncodeRGB, WebPPictureImportRGB) +ENCODE_FUNC(WebPEncodeBGR, WebPPictureImportBGR) +ENCODE_FUNC(WebPEncodeRGBA, WebPPictureImportRGBA) +ENCODE_FUNC(WebPEncodeBGRA, WebPPictureImportBGRA) + +#undef ENCODE_FUNC + +#define LOSSLESS_DEFAULT_QUALITY 70. +#define LOSSLESS_ENCODE_FUNC(NAME, IMPORTER) \ +size_t NAME(const uint8_t* in, int w, int h, int bps, uint8_t** out) { \ + return Encode(in, w, h, bps, IMPORTER, LOSSLESS_DEFAULT_QUALITY, 1, out); \ +} + +LOSSLESS_ENCODE_FUNC(WebPEncodeLosslessRGB, WebPPictureImportRGB) +LOSSLESS_ENCODE_FUNC(WebPEncodeLosslessBGR, WebPPictureImportBGR) +LOSSLESS_ENCODE_FUNC(WebPEncodeLosslessRGBA, WebPPictureImportRGBA) +LOSSLESS_ENCODE_FUNC(WebPEncodeLosslessBGRA, WebPPictureImportBGRA) + +#undef LOSSLESS_ENCODE_FUNC + +//------------------------------------------------------------------------------ diff --git a/thirdparty/libwebp/enc/picture_psnr.c b/thirdparty/libwebp/enc/picture_psnr.c deleted file mode 100644 index 329757deb1..0000000000 --- a/thirdparty/libwebp/enc/picture_psnr.c +++ /dev/null @@ -1,177 +0,0 @@ -// Copyright 2014 Google Inc. All Rights Reserved. -// -// Use of this source code is governed by a BSD-style license -// that can be found in the COPYING file in the root of the source -// tree. An additional intellectual property rights grant can be found -// in the file PATENTS. All contributing project authors may -// be found in the AUTHORS file in the root of the source tree. -// ----------------------------------------------------------------------------- -// -// WebPPicture tools for measuring distortion -// -// Author: Skal (pascal.massimino@gmail.com) - -#include -#include - -#include "./vp8enci.h" -#include "../utils/utils.h" - -//------------------------------------------------------------------------------ -// local-min distortion -// -// For every pixel in the *reference* picture, we search for the local best -// match in the compressed image. This is not a symmetrical measure. - -#define RADIUS 2 // search radius. Shouldn't be too large. - -static void AccumulateLSIM(const uint8_t* src, int src_stride, - const uint8_t* ref, int ref_stride, - int w, int h, VP8DistoStats* stats) { - int x, y; - double total_sse = 0.; - for (y = 0; y < h; ++y) { - const int y_0 = (y - RADIUS < 0) ? 0 : y - RADIUS; - const int y_1 = (y + RADIUS + 1 >= h) ? h : y + RADIUS + 1; - for (x = 0; x < w; ++x) { - const int x_0 = (x - RADIUS < 0) ? 0 : x - RADIUS; - const int x_1 = (x + RADIUS + 1 >= w) ? w : x + RADIUS + 1; - double best_sse = 255. * 255.; - const double value = (double)ref[y * ref_stride + x]; - int i, j; - for (j = y_0; j < y_1; ++j) { - const uint8_t* const s = src + j * src_stride; - for (i = x_0; i < x_1; ++i) { - const double diff = s[i] - value; - const double sse = diff * diff; - if (sse < best_sse) best_sse = sse; - } - } - total_sse += best_sse; - } - } - stats->w = w * h; - stats->xm = 0; - stats->ym = 0; - stats->xxm = total_sse; - stats->yym = 0; - stats->xxm = 0; -} -#undef RADIUS - -//------------------------------------------------------------------------------ -// Distortion - -// Max value returned in case of exact similarity. -static const double kMinDistortion_dB = 99.; -static float GetPSNR(const double v) { - return (float)((v > 0.) ? -4.3429448 * log(v / (255 * 255.)) - : kMinDistortion_dB); -} - -int WebPPictureDistortion(const WebPPicture* src, const WebPPicture* ref, - int type, float result[5]) { - VP8DistoStats stats[5]; - int w, h; - - memset(stats, 0, sizeof(stats)); - - VP8SSIMDspInit(); - - if (src == NULL || ref == NULL || - src->width != ref->width || src->height != ref->height || - src->use_argb != ref->use_argb || result == NULL) { - return 0; - } - w = src->width; - h = src->height; - - if (src->use_argb == 1) { - if (src->argb == NULL || ref->argb == NULL) { - return 0; - } else { - int i, j, c; - uint8_t* tmp1, *tmp2; - uint8_t* const tmp_plane = - (uint8_t*)WebPSafeMalloc(2ULL * w * h, sizeof(*tmp_plane)); - if (tmp_plane == NULL) return 0; - tmp1 = tmp_plane; - tmp2 = tmp_plane + w * h; - for (c = 0; c < 4; ++c) { - for (j = 0; j < h; ++j) { - for (i = 0; i < w; ++i) { - tmp1[j * w + i] = src->argb[i + j * src->argb_stride] >> (c * 8); - tmp2[j * w + i] = ref->argb[i + j * ref->argb_stride] >> (c * 8); - } - } - if (type >= 2) { - AccumulateLSIM(tmp1, w, tmp2, w, w, h, &stats[c]); - } else { - VP8SSIMAccumulatePlane(tmp1, w, tmp2, w, w, h, &stats[c]); - } - } - WebPSafeFree(tmp_plane); - } - } else { - int has_alpha, uv_w, uv_h; - if (src->y == NULL || ref->y == NULL || - src->u == NULL || ref->u == NULL || - src->v == NULL || ref->v == NULL) { - return 0; - } - has_alpha = !!(src->colorspace & WEBP_CSP_ALPHA_BIT); - if (has_alpha != !!(ref->colorspace & WEBP_CSP_ALPHA_BIT) || - (has_alpha && (src->a == NULL || ref->a == NULL))) { - return 0; - } - - uv_w = (src->width + 1) >> 1; - uv_h = (src->height + 1) >> 1; - if (type >= 2) { - AccumulateLSIM(src->y, src->y_stride, ref->y, ref->y_stride, - w, h, &stats[0]); - AccumulateLSIM(src->u, src->uv_stride, ref->u, ref->uv_stride, - uv_w, uv_h, &stats[1]); - AccumulateLSIM(src->v, src->uv_stride, ref->v, ref->uv_stride, - uv_w, uv_h, &stats[2]); - if (has_alpha) { - AccumulateLSIM(src->a, src->a_stride, ref->a, ref->a_stride, - w, h, &stats[3]); - } - } else { - VP8SSIMAccumulatePlane(src->y, src->y_stride, - ref->y, ref->y_stride, - w, h, &stats[0]); - VP8SSIMAccumulatePlane(src->u, src->uv_stride, - ref->u, ref->uv_stride, - uv_w, uv_h, &stats[1]); - VP8SSIMAccumulatePlane(src->v, src->uv_stride, - ref->v, ref->uv_stride, - uv_w, uv_h, &stats[2]); - if (has_alpha) { - VP8SSIMAccumulatePlane(src->a, src->a_stride, - ref->a, ref->a_stride, - w, h, &stats[3]); - } - } - } - // Final stat calculations. - { - int c; - for (c = 0; c <= 4; ++c) { - if (type == 1) { - const double v = VP8SSIMGet(&stats[c]); - result[c] = (float)((v < 1.) ? -10.0 * log10(1. - v) - : kMinDistortion_dB); - } else { - const double v = VP8SSIMGetSquaredError(&stats[c]); - result[c] = GetPSNR(v); - } - // Accumulate forward - if (c < 4) VP8SSIMAddStats(&stats[c], &stats[4]); - } - } - return 1; -} - -//------------------------------------------------------------------------------ diff --git a/thirdparty/libwebp/enc/picture_psnr_enc.c b/thirdparty/libwebp/enc/picture_psnr_enc.c new file mode 100644 index 0000000000..9c0b229507 --- /dev/null +++ b/thirdparty/libwebp/enc/picture_psnr_enc.c @@ -0,0 +1,213 @@ +// Copyright 2014 Google Inc. All Rights Reserved. +// +// Use of this source code is governed by a BSD-style license +// that can be found in the COPYING file in the root of the source +// tree. An additional intellectual property rights grant can be found +// in the file PATENTS. All contributing project authors may +// be found in the AUTHORS file in the root of the source tree. +// ----------------------------------------------------------------------------- +// +// WebPPicture tools for measuring distortion +// +// Author: Skal (pascal.massimino@gmail.com) + +#include +#include + +#include "./vp8i_enc.h" +#include "../utils/utils.h" + +typedef double (*AccumulateFunc)(const uint8_t* src, int src_stride, + const uint8_t* ref, int ref_stride, + int w, int h); + +//------------------------------------------------------------------------------ +// local-min distortion +// +// For every pixel in the *reference* picture, we search for the local best +// match in the compressed image. This is not a symmetrical measure. + +#define RADIUS 2 // search radius. Shouldn't be too large. + +static double AccumulateLSIM(const uint8_t* src, int src_stride, + const uint8_t* ref, int ref_stride, + int w, int h) { + int x, y; + double total_sse = 0.; + for (y = 0; y < h; ++y) { + const int y_0 = (y - RADIUS < 0) ? 0 : y - RADIUS; + const int y_1 = (y + RADIUS + 1 >= h) ? h : y + RADIUS + 1; + for (x = 0; x < w; ++x) { + const int x_0 = (x - RADIUS < 0) ? 0 : x - RADIUS; + const int x_1 = (x + RADIUS + 1 >= w) ? w : x + RADIUS + 1; + double best_sse = 255. * 255.; + const double value = (double)ref[y * ref_stride + x]; + int i, j; + for (j = y_0; j < y_1; ++j) { + const uint8_t* const s = src + j * src_stride; + for (i = x_0; i < x_1; ++i) { + const double diff = s[i] - value; + const double sse = diff * diff; + if (sse < best_sse) best_sse = sse; + } + } + total_sse += best_sse; + } + } + return total_sse; +} +#undef RADIUS + +static double AccumulateSSE(const uint8_t* src, int src_stride, + const uint8_t* ref, int ref_stride, + int w, int h) { + int y; + double total_sse = 0.; + for (y = 0; y < h; ++y) { + total_sse += VP8AccumulateSSE(src, ref, w); + src += src_stride; + ref += ref_stride; + } + return total_sse; +} + +//------------------------------------------------------------------------------ + +static double AccumulateSSIM(const uint8_t* src, int src_stride, + const uint8_t* ref, int ref_stride, + int w, int h) { + const int w0 = (w < VP8_SSIM_KERNEL) ? w : VP8_SSIM_KERNEL; + const int w1 = w - VP8_SSIM_KERNEL - 1; + const int h0 = (h < VP8_SSIM_KERNEL) ? h : VP8_SSIM_KERNEL; + const int h1 = h - VP8_SSIM_KERNEL - 1; + int x, y; + double sum = 0.; + for (y = 0; y < h0; ++y) { + for (x = 0; x < w; ++x) { + sum += VP8SSIMGetClipped(src, src_stride, ref, ref_stride, x, y, w, h); + } + } + for (; y < h1; ++y) { + for (x = 0; x < w0; ++x) { + sum += VP8SSIMGetClipped(src, src_stride, ref, ref_stride, x, y, w, h); + } + for (; x < w1; ++x) { + const int off1 = x - VP8_SSIM_KERNEL + (y - VP8_SSIM_KERNEL) * src_stride; + const int off2 = x - VP8_SSIM_KERNEL + (y - VP8_SSIM_KERNEL) * ref_stride; + sum += VP8SSIMGet(src + off1, src_stride, ref + off2, ref_stride); + } + for (; x < w; ++x) { + sum += VP8SSIMGetClipped(src, src_stride, ref, ref_stride, x, y, w, h); + } + } + for (; y < h; ++y) { + for (x = 0; x < w; ++x) { + sum += VP8SSIMGetClipped(src, src_stride, ref, ref_stride, x, y, w, h); + } + } + return sum; +} + +//------------------------------------------------------------------------------ +// Distortion + +// Max value returned in case of exact similarity. +static const double kMinDistortion_dB = 99.; + +static double GetPSNR(double v, double size) { + return (v > 0. && size > 0.) ? -4.3429448 * log(v / (size * 255 * 255.)) + : kMinDistortion_dB; +} + +static double GetLogSSIM(double v, double size) { + v = (size > 0.) ? v / size : 1.; + return (v < 1.) ? -10.0 * log10(1. - v) : kMinDistortion_dB; +} + +int WebPPlaneDistortion(const uint8_t* src, size_t src_stride, + const uint8_t* ref, size_t ref_stride, + int width, int height, size_t x_step, + int type, float* distortion, float* result) { + uint8_t* allocated = NULL; + const AccumulateFunc metric = (type == 0) ? AccumulateSSE : + (type == 1) ? AccumulateSSIM : + AccumulateLSIM; + if (src == NULL || ref == NULL || + src_stride < x_step * width || ref_stride < x_step * width || + result == NULL || distortion == NULL) { + return 0; + } + + VP8SSIMDspInit(); + if (x_step != 1) { // extract a packed plane if needed + int x, y; + uint8_t* tmp1; + uint8_t* tmp2; + allocated = + (uint8_t*)WebPSafeMalloc(2ULL * width * height, sizeof(*allocated)); + if (allocated == NULL) return 0; + tmp1 = allocated; + tmp2 = tmp1 + (size_t)width * height; + for (y = 0; y < height; ++y) { + for (x = 0; x < width; ++x) { + tmp1[x + y * width] = src[x * x_step + y * src_stride]; + tmp2[x + y * width] = ref[x * x_step + y * ref_stride]; + } + } + src = tmp1; + ref = tmp2; + } + *distortion = (float)metric(src, width, ref, width, width, height); + WebPSafeFree(allocated); + + *result = (type == 1) ? (float)GetLogSSIM(*distortion, (double)width * height) + : (float)GetPSNR(*distortion, (double)width * height); + return 1; +} + +int WebPPictureDistortion(const WebPPicture* src, const WebPPicture* ref, + int type, float results[5]) { + int w, h, c; + int ok = 0; + WebPPicture p0, p1; + double total_size = 0., total_distortion = 0.; + if (src == NULL || ref == NULL || + src->width != ref->width || src->height != ref->height || + results == NULL) { + return 0; + } + + VP8SSIMDspInit(); + if (!WebPPictureInit(&p0) || !WebPPictureInit(&p1)) return 0; + w = src->width; + h = src->height; + if (!WebPPictureView(src, 0, 0, w, h, &p0)) goto Error; + if (!WebPPictureView(ref, 0, 0, w, h, &p1)) goto Error; + + // We always measure distortion in ARGB space. + if (p0.use_argb == 0 && !WebPPictureYUVAToARGB(&p0)) goto Error; + if (p1.use_argb == 0 && !WebPPictureYUVAToARGB(&p1)) goto Error; + for (c = 0; c < 4; ++c) { + float distortion; + const size_t stride0 = 4 * (size_t)p0.argb_stride; + const size_t stride1 = 4 * (size_t)p1.argb_stride; + if (!WebPPlaneDistortion((const uint8_t*)p0.argb + c, stride0, + (const uint8_t*)p1.argb + c, stride1, + w, h, 4, type, &distortion, results + c)) { + goto Error; + } + total_distortion += distortion; + total_size += w * h; + } + + results[4] = (type == 1) ? (float)GetLogSSIM(total_distortion, total_size) + : (float)GetPSNR(total_distortion, total_size); + ok = 1; + + Error: + WebPPictureFree(&p0); + WebPPictureFree(&p1); + return ok; +} + +//------------------------------------------------------------------------------ diff --git a/thirdparty/libwebp/enc/picture_rescale.c b/thirdparty/libwebp/enc/picture_rescale.c deleted file mode 100644 index 9f19e8e80f..0000000000 --- a/thirdparty/libwebp/enc/picture_rescale.c +++ /dev/null @@ -1,264 +0,0 @@ -// Copyright 2014 Google Inc. All Rights Reserved. -// -// Use of this source code is governed by a BSD-style license -// that can be found in the COPYING file in the root of the source -// tree. An additional intellectual property rights grant can be found -// in the file PATENTS. All contributing project authors may -// be found in the AUTHORS file in the root of the source tree. -// ----------------------------------------------------------------------------- -// -// WebPPicture tools: copy, crop, rescaling and view. -// -// Author: Skal (pascal.massimino@gmail.com) - -#include -#include - -#include "./vp8enci.h" -#include "../utils/rescaler.h" -#include "../utils/utils.h" - -#define HALVE(x) (((x) + 1) >> 1) - -// Grab the 'specs' (writer, *opaque, width, height...) from 'src' and copy them -// into 'dst'. Mark 'dst' as not owning any memory. -static void PictureGrabSpecs(const WebPPicture* const src, - WebPPicture* const dst) { - assert(src != NULL && dst != NULL); - *dst = *src; - WebPPictureResetBuffers(dst); -} - -//------------------------------------------------------------------------------ - -// Adjust top-left corner to chroma sample position. -static void SnapTopLeftPosition(const WebPPicture* const pic, - int* const left, int* const top) { - if (!pic->use_argb) { - *left &= ~1; - *top &= ~1; - } -} - -// Adjust top-left corner and verify that the sub-rectangle is valid. -static int AdjustAndCheckRectangle(const WebPPicture* const pic, - int* const left, int* const top, - int width, int height) { - SnapTopLeftPosition(pic, left, top); - if ((*left) < 0 || (*top) < 0) return 0; - if (width <= 0 || height <= 0) return 0; - if ((*left) + width > pic->width) return 0; - if ((*top) + height > pic->height) return 0; - return 1; -} - -int WebPPictureCopy(const WebPPicture* src, WebPPicture* dst) { - if (src == NULL || dst == NULL) return 0; - if (src == dst) return 1; - - PictureGrabSpecs(src, dst); - if (!WebPPictureAlloc(dst)) return 0; - - if (!src->use_argb) { - WebPCopyPlane(src->y, src->y_stride, - dst->y, dst->y_stride, dst->width, dst->height); - WebPCopyPlane(src->u, src->uv_stride, dst->u, dst->uv_stride, - HALVE(dst->width), HALVE(dst->height)); - WebPCopyPlane(src->v, src->uv_stride, dst->v, dst->uv_stride, - HALVE(dst->width), HALVE(dst->height)); - if (dst->a != NULL) { - WebPCopyPlane(src->a, src->a_stride, - dst->a, dst->a_stride, dst->width, dst->height); - } - } else { - WebPCopyPlane((const uint8_t*)src->argb, 4 * src->argb_stride, - (uint8_t*)dst->argb, 4 * dst->argb_stride, - 4 * dst->width, dst->height); - } - return 1; -} - -int WebPPictureIsView(const WebPPicture* picture) { - if (picture == NULL) return 0; - if (picture->use_argb) { - return (picture->memory_argb_ == NULL); - } - return (picture->memory_ == NULL); -} - -int WebPPictureView(const WebPPicture* src, - int left, int top, int width, int height, - WebPPicture* dst) { - if (src == NULL || dst == NULL) return 0; - - // verify rectangle position. - if (!AdjustAndCheckRectangle(src, &left, &top, width, height)) return 0; - - if (src != dst) { // beware of aliasing! We don't want to leak 'memory_'. - PictureGrabSpecs(src, dst); - } - dst->width = width; - dst->height = height; - if (!src->use_argb) { - dst->y = src->y + top * src->y_stride + left; - dst->u = src->u + (top >> 1) * src->uv_stride + (left >> 1); - dst->v = src->v + (top >> 1) * src->uv_stride + (left >> 1); - dst->y_stride = src->y_stride; - dst->uv_stride = src->uv_stride; - if (src->a != NULL) { - dst->a = src->a + top * src->a_stride + left; - dst->a_stride = src->a_stride; - } - } else { - dst->argb = src->argb + top * src->argb_stride + left; - dst->argb_stride = src->argb_stride; - } - return 1; -} - -//------------------------------------------------------------------------------ -// Picture cropping - -int WebPPictureCrop(WebPPicture* pic, - int left, int top, int width, int height) { - WebPPicture tmp; - - if (pic == NULL) return 0; - if (!AdjustAndCheckRectangle(pic, &left, &top, width, height)) return 0; - - PictureGrabSpecs(pic, &tmp); - tmp.width = width; - tmp.height = height; - if (!WebPPictureAlloc(&tmp)) return 0; - - if (!pic->use_argb) { - const int y_offset = top * pic->y_stride + left; - const int uv_offset = (top / 2) * pic->uv_stride + left / 2; - WebPCopyPlane(pic->y + y_offset, pic->y_stride, - tmp.y, tmp.y_stride, width, height); - WebPCopyPlane(pic->u + uv_offset, pic->uv_stride, - tmp.u, tmp.uv_stride, HALVE(width), HALVE(height)); - WebPCopyPlane(pic->v + uv_offset, pic->uv_stride, - tmp.v, tmp.uv_stride, HALVE(width), HALVE(height)); - - if (tmp.a != NULL) { - const int a_offset = top * pic->a_stride + left; - WebPCopyPlane(pic->a + a_offset, pic->a_stride, - tmp.a, tmp.a_stride, width, height); - } - } else { - const uint8_t* const src = - (const uint8_t*)(pic->argb + top * pic->argb_stride + left); - WebPCopyPlane(src, pic->argb_stride * 4, (uint8_t*)tmp.argb, - tmp.argb_stride * 4, width * 4, height); - } - WebPPictureFree(pic); - *pic = tmp; - return 1; -} - -//------------------------------------------------------------------------------ -// Simple picture rescaler - -static void RescalePlane(const uint8_t* src, - int src_width, int src_height, int src_stride, - uint8_t* dst, - int dst_width, int dst_height, int dst_stride, - rescaler_t* const work, - int num_channels) { - WebPRescaler rescaler; - int y = 0; - WebPRescalerInit(&rescaler, src_width, src_height, - dst, dst_width, dst_height, dst_stride, - num_channels, work); - while (y < src_height) { - y += WebPRescalerImport(&rescaler, src_height - y, - src + y * src_stride, src_stride); - WebPRescalerExport(&rescaler); - } -} - -static void AlphaMultiplyARGB(WebPPicture* const pic, int inverse) { - assert(pic->argb != NULL); - WebPMultARGBRows((uint8_t*)pic->argb, pic->argb_stride * sizeof(*pic->argb), - pic->width, pic->height, inverse); -} - -static void AlphaMultiplyY(WebPPicture* const pic, int inverse) { - if (pic->a != NULL) { - WebPMultRows(pic->y, pic->y_stride, pic->a, pic->a_stride, - pic->width, pic->height, inverse); - } -} - -int WebPPictureRescale(WebPPicture* pic, int width, int height) { - WebPPicture tmp; - int prev_width, prev_height; - rescaler_t* work; - - if (pic == NULL) return 0; - prev_width = pic->width; - prev_height = pic->height; - if (!WebPRescalerGetScaledDimensions( - prev_width, prev_height, &width, &height)) { - return 0; - } - - PictureGrabSpecs(pic, &tmp); - tmp.width = width; - tmp.height = height; - if (!WebPPictureAlloc(&tmp)) return 0; - - if (!pic->use_argb) { - work = (rescaler_t*)WebPSafeMalloc(2ULL * width, sizeof(*work)); - if (work == NULL) { - WebPPictureFree(&tmp); - return 0; - } - // If present, we need to rescale alpha first (for AlphaMultiplyY). - if (pic->a != NULL) { - WebPInitAlphaProcessing(); - RescalePlane(pic->a, prev_width, prev_height, pic->a_stride, - tmp.a, width, height, tmp.a_stride, work, 1); - } - - // We take transparency into account on the luma plane only. That's not - // totally exact blending, but still is a good approximation. - AlphaMultiplyY(pic, 0); - RescalePlane(pic->y, prev_width, prev_height, pic->y_stride, - tmp.y, width, height, tmp.y_stride, work, 1); - AlphaMultiplyY(&tmp, 1); - - RescalePlane(pic->u, - HALVE(prev_width), HALVE(prev_height), pic->uv_stride, - tmp.u, - HALVE(width), HALVE(height), tmp.uv_stride, work, 1); - RescalePlane(pic->v, - HALVE(prev_width), HALVE(prev_height), pic->uv_stride, - tmp.v, - HALVE(width), HALVE(height), tmp.uv_stride, work, 1); - } else { - work = (rescaler_t*)WebPSafeMalloc(2ULL * width * 4, sizeof(*work)); - if (work == NULL) { - WebPPictureFree(&tmp); - return 0; - } - // In order to correctly interpolate colors, we need to apply the alpha - // weighting first (black-matting), scale the RGB values, and remove - // the premultiplication afterward (while preserving the alpha channel). - WebPInitAlphaProcessing(); - AlphaMultiplyARGB(pic, 0); - RescalePlane((const uint8_t*)pic->argb, prev_width, prev_height, - pic->argb_stride * 4, - (uint8_t*)tmp.argb, width, height, - tmp.argb_stride * 4, - work, 4); - AlphaMultiplyARGB(&tmp, 1); - } - WebPPictureFree(pic); - WebPSafeFree(work); - *pic = tmp; - return 1; -} - -//------------------------------------------------------------------------------ diff --git a/thirdparty/libwebp/enc/picture_rescale_enc.c b/thirdparty/libwebp/enc/picture_rescale_enc.c new file mode 100644 index 0000000000..0b7181c0d7 --- /dev/null +++ b/thirdparty/libwebp/enc/picture_rescale_enc.c @@ -0,0 +1,264 @@ +// Copyright 2014 Google Inc. All Rights Reserved. +// +// Use of this source code is governed by a BSD-style license +// that can be found in the COPYING file in the root of the source +// tree. An additional intellectual property rights grant can be found +// in the file PATENTS. All contributing project authors may +// be found in the AUTHORS file in the root of the source tree. +// ----------------------------------------------------------------------------- +// +// WebPPicture tools: copy, crop, rescaling and view. +// +// Author: Skal (pascal.massimino@gmail.com) + +#include +#include + +#include "./vp8i_enc.h" +#include "../utils/rescaler_utils.h" +#include "../utils/utils.h" + +#define HALVE(x) (((x) + 1) >> 1) + +// Grab the 'specs' (writer, *opaque, width, height...) from 'src' and copy them +// into 'dst'. Mark 'dst' as not owning any memory. +static void PictureGrabSpecs(const WebPPicture* const src, + WebPPicture* const dst) { + assert(src != NULL && dst != NULL); + *dst = *src; + WebPPictureResetBuffers(dst); +} + +//------------------------------------------------------------------------------ + +// Adjust top-left corner to chroma sample position. +static void SnapTopLeftPosition(const WebPPicture* const pic, + int* const left, int* const top) { + if (!pic->use_argb) { + *left &= ~1; + *top &= ~1; + } +} + +// Adjust top-left corner and verify that the sub-rectangle is valid. +static int AdjustAndCheckRectangle(const WebPPicture* const pic, + int* const left, int* const top, + int width, int height) { + SnapTopLeftPosition(pic, left, top); + if ((*left) < 0 || (*top) < 0) return 0; + if (width <= 0 || height <= 0) return 0; + if ((*left) + width > pic->width) return 0; + if ((*top) + height > pic->height) return 0; + return 1; +} + +int WebPPictureCopy(const WebPPicture* src, WebPPicture* dst) { + if (src == NULL || dst == NULL) return 0; + if (src == dst) return 1; + + PictureGrabSpecs(src, dst); + if (!WebPPictureAlloc(dst)) return 0; + + if (!src->use_argb) { + WebPCopyPlane(src->y, src->y_stride, + dst->y, dst->y_stride, dst->width, dst->height); + WebPCopyPlane(src->u, src->uv_stride, dst->u, dst->uv_stride, + HALVE(dst->width), HALVE(dst->height)); + WebPCopyPlane(src->v, src->uv_stride, dst->v, dst->uv_stride, + HALVE(dst->width), HALVE(dst->height)); + if (dst->a != NULL) { + WebPCopyPlane(src->a, src->a_stride, + dst->a, dst->a_stride, dst->width, dst->height); + } + } else { + WebPCopyPlane((const uint8_t*)src->argb, 4 * src->argb_stride, + (uint8_t*)dst->argb, 4 * dst->argb_stride, + 4 * dst->width, dst->height); + } + return 1; +} + +int WebPPictureIsView(const WebPPicture* picture) { + if (picture == NULL) return 0; + if (picture->use_argb) { + return (picture->memory_argb_ == NULL); + } + return (picture->memory_ == NULL); +} + +int WebPPictureView(const WebPPicture* src, + int left, int top, int width, int height, + WebPPicture* dst) { + if (src == NULL || dst == NULL) return 0; + + // verify rectangle position. + if (!AdjustAndCheckRectangle(src, &left, &top, width, height)) return 0; + + if (src != dst) { // beware of aliasing! We don't want to leak 'memory_'. + PictureGrabSpecs(src, dst); + } + dst->width = width; + dst->height = height; + if (!src->use_argb) { + dst->y = src->y + top * src->y_stride + left; + dst->u = src->u + (top >> 1) * src->uv_stride + (left >> 1); + dst->v = src->v + (top >> 1) * src->uv_stride + (left >> 1); + dst->y_stride = src->y_stride; + dst->uv_stride = src->uv_stride; + if (src->a != NULL) { + dst->a = src->a + top * src->a_stride + left; + dst->a_stride = src->a_stride; + } + } else { + dst->argb = src->argb + top * src->argb_stride + left; + dst->argb_stride = src->argb_stride; + } + return 1; +} + +//------------------------------------------------------------------------------ +// Picture cropping + +int WebPPictureCrop(WebPPicture* pic, + int left, int top, int width, int height) { + WebPPicture tmp; + + if (pic == NULL) return 0; + if (!AdjustAndCheckRectangle(pic, &left, &top, width, height)) return 0; + + PictureGrabSpecs(pic, &tmp); + tmp.width = width; + tmp.height = height; + if (!WebPPictureAlloc(&tmp)) return 0; + + if (!pic->use_argb) { + const int y_offset = top * pic->y_stride + left; + const int uv_offset = (top / 2) * pic->uv_stride + left / 2; + WebPCopyPlane(pic->y + y_offset, pic->y_stride, + tmp.y, tmp.y_stride, width, height); + WebPCopyPlane(pic->u + uv_offset, pic->uv_stride, + tmp.u, tmp.uv_stride, HALVE(width), HALVE(height)); + WebPCopyPlane(pic->v + uv_offset, pic->uv_stride, + tmp.v, tmp.uv_stride, HALVE(width), HALVE(height)); + + if (tmp.a != NULL) { + const int a_offset = top * pic->a_stride + left; + WebPCopyPlane(pic->a + a_offset, pic->a_stride, + tmp.a, tmp.a_stride, width, height); + } + } else { + const uint8_t* const src = + (const uint8_t*)(pic->argb + top * pic->argb_stride + left); + WebPCopyPlane(src, pic->argb_stride * 4, (uint8_t*)tmp.argb, + tmp.argb_stride * 4, width * 4, height); + } + WebPPictureFree(pic); + *pic = tmp; + return 1; +} + +//------------------------------------------------------------------------------ +// Simple picture rescaler + +static void RescalePlane(const uint8_t* src, + int src_width, int src_height, int src_stride, + uint8_t* dst, + int dst_width, int dst_height, int dst_stride, + rescaler_t* const work, + int num_channels) { + WebPRescaler rescaler; + int y = 0; + WebPRescalerInit(&rescaler, src_width, src_height, + dst, dst_width, dst_height, dst_stride, + num_channels, work); + while (y < src_height) { + y += WebPRescalerImport(&rescaler, src_height - y, + src + y * src_stride, src_stride); + WebPRescalerExport(&rescaler); + } +} + +static void AlphaMultiplyARGB(WebPPicture* const pic, int inverse) { + assert(pic->argb != NULL); + WebPMultARGBRows((uint8_t*)pic->argb, pic->argb_stride * sizeof(*pic->argb), + pic->width, pic->height, inverse); +} + +static void AlphaMultiplyY(WebPPicture* const pic, int inverse) { + if (pic->a != NULL) { + WebPMultRows(pic->y, pic->y_stride, pic->a, pic->a_stride, + pic->width, pic->height, inverse); + } +} + +int WebPPictureRescale(WebPPicture* pic, int width, int height) { + WebPPicture tmp; + int prev_width, prev_height; + rescaler_t* work; + + if (pic == NULL) return 0; + prev_width = pic->width; + prev_height = pic->height; + if (!WebPRescalerGetScaledDimensions( + prev_width, prev_height, &width, &height)) { + return 0; + } + + PictureGrabSpecs(pic, &tmp); + tmp.width = width; + tmp.height = height; + if (!WebPPictureAlloc(&tmp)) return 0; + + if (!pic->use_argb) { + work = (rescaler_t*)WebPSafeMalloc(2ULL * width, sizeof(*work)); + if (work == NULL) { + WebPPictureFree(&tmp); + return 0; + } + // If present, we need to rescale alpha first (for AlphaMultiplyY). + if (pic->a != NULL) { + WebPInitAlphaProcessing(); + RescalePlane(pic->a, prev_width, prev_height, pic->a_stride, + tmp.a, width, height, tmp.a_stride, work, 1); + } + + // We take transparency into account on the luma plane only. That's not + // totally exact blending, but still is a good approximation. + AlphaMultiplyY(pic, 0); + RescalePlane(pic->y, prev_width, prev_height, pic->y_stride, + tmp.y, width, height, tmp.y_stride, work, 1); + AlphaMultiplyY(&tmp, 1); + + RescalePlane(pic->u, + HALVE(prev_width), HALVE(prev_height), pic->uv_stride, + tmp.u, + HALVE(width), HALVE(height), tmp.uv_stride, work, 1); + RescalePlane(pic->v, + HALVE(prev_width), HALVE(prev_height), pic->uv_stride, + tmp.v, + HALVE(width), HALVE(height), tmp.uv_stride, work, 1); + } else { + work = (rescaler_t*)WebPSafeMalloc(2ULL * width * 4, sizeof(*work)); + if (work == NULL) { + WebPPictureFree(&tmp); + return 0; + } + // In order to correctly interpolate colors, we need to apply the alpha + // weighting first (black-matting), scale the RGB values, and remove + // the premultiplication afterward (while preserving the alpha channel). + WebPInitAlphaProcessing(); + AlphaMultiplyARGB(pic, 0); + RescalePlane((const uint8_t*)pic->argb, prev_width, prev_height, + pic->argb_stride * 4, + (uint8_t*)tmp.argb, width, height, + tmp.argb_stride * 4, + work, 4); + AlphaMultiplyARGB(&tmp, 1); + } + WebPPictureFree(pic); + WebPSafeFree(work); + *pic = tmp; + return 1; +} + +//------------------------------------------------------------------------------ diff --git a/thirdparty/libwebp/enc/picture_tools.c b/thirdparty/libwebp/enc/picture_tools.c deleted file mode 100644 index bf97af8408..0000000000 --- a/thirdparty/libwebp/enc/picture_tools.c +++ /dev/null @@ -1,226 +0,0 @@ -// Copyright 2014 Google Inc. All Rights Reserved. -// -// Use of this source code is governed by a BSD-style license -// that can be found in the COPYING file in the root of the source -// tree. An additional intellectual property rights grant can be found -// in the file PATENTS. All contributing project authors may -// be found in the AUTHORS file in the root of the source tree. -// ----------------------------------------------------------------------------- -// -// WebPPicture tools: alpha handling, etc. -// -// Author: Skal (pascal.massimino@gmail.com) - -#include - -#include "./vp8enci.h" -#include "../dsp/yuv.h" - -static WEBP_INLINE uint32_t MakeARGB32(int r, int g, int b) { - return (0xff000000u | (r << 16) | (g << 8) | b); -} - -//------------------------------------------------------------------------------ -// Helper: clean up fully transparent area to help compressibility. - -#define SIZE 8 -#define SIZE2 (SIZE / 2) -static int is_transparent_area(const uint8_t* ptr, int stride, int size) { - int y, x; - for (y = 0; y < size; ++y) { - for (x = 0; x < size; ++x) { - if (ptr[x]) { - return 0; - } - } - ptr += stride; - } - return 1; -} - -static int is_transparent_argb_area(const uint32_t* ptr, int stride, int size) { - int y, x; - for (y = 0; y < size; ++y) { - for (x = 0; x < size; ++x) { - if (ptr[x] & 0xff000000u) { - return 0; - } - } - ptr += stride; - } - return 1; -} - -static void flatten(uint8_t* ptr, int v, int stride, int size) { - int y; - for (y = 0; y < size; ++y) { - memset(ptr, v, size); - ptr += stride; - } -} - -static void flatten_argb(uint32_t* ptr, uint32_t v, int stride, int size) { - int x, y; - for (y = 0; y < size; ++y) { - for (x = 0; x < size; ++x) ptr[x] = v; - ptr += stride; - } -} - -void WebPCleanupTransparentArea(WebPPicture* pic) { - int x, y, w, h; - if (pic == NULL) return; - w = pic->width / SIZE; - h = pic->height / SIZE; - - // note: we ignore the left-overs on right/bottom - if (pic->use_argb) { - uint32_t argb_value = 0; - for (y = 0; y < h; ++y) { - int need_reset = 1; - for (x = 0; x < w; ++x) { - const int off = (y * pic->argb_stride + x) * SIZE; - if (is_transparent_argb_area(pic->argb + off, pic->argb_stride, SIZE)) { - if (need_reset) { - argb_value = pic->argb[off]; - need_reset = 0; - } - flatten_argb(pic->argb + off, argb_value, pic->argb_stride, SIZE); - } else { - need_reset = 1; - } - } - } - } else { - const uint8_t* const a_ptr = pic->a; - int values[3] = { 0 }; - if (a_ptr == NULL) return; // nothing to do - for (y = 0; y < h; ++y) { - int need_reset = 1; - for (x = 0; x < w; ++x) { - const int off_a = (y * pic->a_stride + x) * SIZE; - const int off_y = (y * pic->y_stride + x) * SIZE; - const int off_uv = (y * pic->uv_stride + x) * SIZE2; - if (is_transparent_area(a_ptr + off_a, pic->a_stride, SIZE)) { - if (need_reset) { - values[0] = pic->y[off_y]; - values[1] = pic->u[off_uv]; - values[2] = pic->v[off_uv]; - need_reset = 0; - } - flatten(pic->y + off_y, values[0], pic->y_stride, SIZE); - flatten(pic->u + off_uv, values[1], pic->uv_stride, SIZE2); - flatten(pic->v + off_uv, values[2], pic->uv_stride, SIZE2); - } else { - need_reset = 1; - } - } - } - } -} - -#undef SIZE -#undef SIZE2 - -void WebPCleanupTransparentAreaLossless(WebPPicture* const pic) { - int x, y, w, h; - uint32_t* argb; - assert(pic != NULL && pic->use_argb); - w = pic->width; - h = pic->height; - argb = pic->argb; - - for (y = 0; y < h; ++y) { - for (x = 0; x < w; ++x) { - if ((argb[x] & 0xff000000) == 0) { - argb[x] = 0x00000000; - } - } - argb += pic->argb_stride; - } -} - -//------------------------------------------------------------------------------ -// Blend color and remove transparency info - -#define BLEND(V0, V1, ALPHA) \ - ((((V0) * (255 - (ALPHA)) + (V1) * (ALPHA)) * 0x101) >> 16) -#define BLEND_10BIT(V0, V1, ALPHA) \ - ((((V0) * (1020 - (ALPHA)) + (V1) * (ALPHA)) * 0x101) >> 18) - -void WebPBlendAlpha(WebPPicture* pic, uint32_t background_rgb) { - const int red = (background_rgb >> 16) & 0xff; - const int green = (background_rgb >> 8) & 0xff; - const int blue = (background_rgb >> 0) & 0xff; - int x, y; - if (pic == NULL) return; - if (!pic->use_argb) { - const int uv_width = (pic->width >> 1); // omit last pixel during u/v loop - const int Y0 = VP8RGBToY(red, green, blue, YUV_HALF); - // VP8RGBToU/V expects the u/v values summed over four pixels - const int U0 = VP8RGBToU(4 * red, 4 * green, 4 * blue, 4 * YUV_HALF); - const int V0 = VP8RGBToV(4 * red, 4 * green, 4 * blue, 4 * YUV_HALF); - const int has_alpha = pic->colorspace & WEBP_CSP_ALPHA_BIT; - if (!has_alpha || pic->a == NULL) return; // nothing to do - for (y = 0; y < pic->height; ++y) { - // Luma blending - uint8_t* const y_ptr = pic->y + y * pic->y_stride; - uint8_t* const a_ptr = pic->a + y * pic->a_stride; - for (x = 0; x < pic->width; ++x) { - const int alpha = a_ptr[x]; - if (alpha < 0xff) { - y_ptr[x] = BLEND(Y0, y_ptr[x], a_ptr[x]); - } - } - // Chroma blending every even line - if ((y & 1) == 0) { - uint8_t* const u = pic->u + (y >> 1) * pic->uv_stride; - uint8_t* const v = pic->v + (y >> 1) * pic->uv_stride; - uint8_t* const a_ptr2 = - (y + 1 == pic->height) ? a_ptr : a_ptr + pic->a_stride; - for (x = 0; x < uv_width; ++x) { - // Average four alpha values into a single blending weight. - // TODO(skal): might lead to visible contouring. Can we do better? - const int alpha = - a_ptr[2 * x + 0] + a_ptr[2 * x + 1] + - a_ptr2[2 * x + 0] + a_ptr2[2 * x + 1]; - u[x] = BLEND_10BIT(U0, u[x], alpha); - v[x] = BLEND_10BIT(V0, v[x], alpha); - } - if (pic->width & 1) { // rightmost pixel - const int alpha = 2 * (a_ptr[2 * x + 0] + a_ptr2[2 * x + 0]); - u[x] = BLEND_10BIT(U0, u[x], alpha); - v[x] = BLEND_10BIT(V0, v[x], alpha); - } - } - memset(a_ptr, 0xff, pic->width); - } - } else { - uint32_t* argb = pic->argb; - const uint32_t background = MakeARGB32(red, green, blue); - for (y = 0; y < pic->height; ++y) { - for (x = 0; x < pic->width; ++x) { - const int alpha = (argb[x] >> 24) & 0xff; - if (alpha != 0xff) { - if (alpha > 0) { - int r = (argb[x] >> 16) & 0xff; - int g = (argb[x] >> 8) & 0xff; - int b = (argb[x] >> 0) & 0xff; - r = BLEND(red, r, alpha); - g = BLEND(green, g, alpha); - b = BLEND(blue, b, alpha); - argb[x] = MakeARGB32(r, g, b); - } else { - argb[x] = background; - } - } - } - argb += pic->argb_stride; - } - } -} - -#undef BLEND -#undef BLEND_10BIT - -//------------------------------------------------------------------------------ diff --git a/thirdparty/libwebp/enc/picture_tools_enc.c b/thirdparty/libwebp/enc/picture_tools_enc.c new file mode 100644 index 0000000000..895df51156 --- /dev/null +++ b/thirdparty/libwebp/enc/picture_tools_enc.c @@ -0,0 +1,226 @@ +// Copyright 2014 Google Inc. All Rights Reserved. +// +// Use of this source code is governed by a BSD-style license +// that can be found in the COPYING file in the root of the source +// tree. An additional intellectual property rights grant can be found +// in the file PATENTS. All contributing project authors may +// be found in the AUTHORS file in the root of the source tree. +// ----------------------------------------------------------------------------- +// +// WebPPicture tools: alpha handling, etc. +// +// Author: Skal (pascal.massimino@gmail.com) + +#include + +#include "./vp8i_enc.h" +#include "../dsp/yuv.h" + +static WEBP_INLINE uint32_t MakeARGB32(int r, int g, int b) { + return (0xff000000u | (r << 16) | (g << 8) | b); +} + +//------------------------------------------------------------------------------ +// Helper: clean up fully transparent area to help compressibility. + +#define SIZE 8 +#define SIZE2 (SIZE / 2) +static int is_transparent_area(const uint8_t* ptr, int stride, int size) { + int y, x; + for (y = 0; y < size; ++y) { + for (x = 0; x < size; ++x) { + if (ptr[x]) { + return 0; + } + } + ptr += stride; + } + return 1; +} + +static int is_transparent_argb_area(const uint32_t* ptr, int stride, int size) { + int y, x; + for (y = 0; y < size; ++y) { + for (x = 0; x < size; ++x) { + if (ptr[x] & 0xff000000u) { + return 0; + } + } + ptr += stride; + } + return 1; +} + +static void flatten(uint8_t* ptr, int v, int stride, int size) { + int y; + for (y = 0; y < size; ++y) { + memset(ptr, v, size); + ptr += stride; + } +} + +static void flatten_argb(uint32_t* ptr, uint32_t v, int stride, int size) { + int x, y; + for (y = 0; y < size; ++y) { + for (x = 0; x < size; ++x) ptr[x] = v; + ptr += stride; + } +} + +void WebPCleanupTransparentArea(WebPPicture* pic) { + int x, y, w, h; + if (pic == NULL) return; + w = pic->width / SIZE; + h = pic->height / SIZE; + + // note: we ignore the left-overs on right/bottom + if (pic->use_argb) { + uint32_t argb_value = 0; + for (y = 0; y < h; ++y) { + int need_reset = 1; + for (x = 0; x < w; ++x) { + const int off = (y * pic->argb_stride + x) * SIZE; + if (is_transparent_argb_area(pic->argb + off, pic->argb_stride, SIZE)) { + if (need_reset) { + argb_value = pic->argb[off]; + need_reset = 0; + } + flatten_argb(pic->argb + off, argb_value, pic->argb_stride, SIZE); + } else { + need_reset = 1; + } + } + } + } else { + const uint8_t* const a_ptr = pic->a; + int values[3] = { 0 }; + if (a_ptr == NULL) return; // nothing to do + for (y = 0; y < h; ++y) { + int need_reset = 1; + for (x = 0; x < w; ++x) { + const int off_a = (y * pic->a_stride + x) * SIZE; + const int off_y = (y * pic->y_stride + x) * SIZE; + const int off_uv = (y * pic->uv_stride + x) * SIZE2; + if (is_transparent_area(a_ptr + off_a, pic->a_stride, SIZE)) { + if (need_reset) { + values[0] = pic->y[off_y]; + values[1] = pic->u[off_uv]; + values[2] = pic->v[off_uv]; + need_reset = 0; + } + flatten(pic->y + off_y, values[0], pic->y_stride, SIZE); + flatten(pic->u + off_uv, values[1], pic->uv_stride, SIZE2); + flatten(pic->v + off_uv, values[2], pic->uv_stride, SIZE2); + } else { + need_reset = 1; + } + } + } + } +} + +#undef SIZE +#undef SIZE2 + +void WebPCleanupTransparentAreaLossless(WebPPicture* const pic) { + int x, y, w, h; + uint32_t* argb; + assert(pic != NULL && pic->use_argb); + w = pic->width; + h = pic->height; + argb = pic->argb; + + for (y = 0; y < h; ++y) { + for (x = 0; x < w; ++x) { + if ((argb[x] & 0xff000000) == 0) { + argb[x] = 0x00000000; + } + } + argb += pic->argb_stride; + } +} + +//------------------------------------------------------------------------------ +// Blend color and remove transparency info + +#define BLEND(V0, V1, ALPHA) \ + ((((V0) * (255 - (ALPHA)) + (V1) * (ALPHA)) * 0x101) >> 16) +#define BLEND_10BIT(V0, V1, ALPHA) \ + ((((V0) * (1020 - (ALPHA)) + (V1) * (ALPHA)) * 0x101) >> 18) + +void WebPBlendAlpha(WebPPicture* pic, uint32_t background_rgb) { + const int red = (background_rgb >> 16) & 0xff; + const int green = (background_rgb >> 8) & 0xff; + const int blue = (background_rgb >> 0) & 0xff; + int x, y; + if (pic == NULL) return; + if (!pic->use_argb) { + const int uv_width = (pic->width >> 1); // omit last pixel during u/v loop + const int Y0 = VP8RGBToY(red, green, blue, YUV_HALF); + // VP8RGBToU/V expects the u/v values summed over four pixels + const int U0 = VP8RGBToU(4 * red, 4 * green, 4 * blue, 4 * YUV_HALF); + const int V0 = VP8RGBToV(4 * red, 4 * green, 4 * blue, 4 * YUV_HALF); + const int has_alpha = pic->colorspace & WEBP_CSP_ALPHA_BIT; + if (!has_alpha || pic->a == NULL) return; // nothing to do + for (y = 0; y < pic->height; ++y) { + // Luma blending + uint8_t* const y_ptr = pic->y + y * pic->y_stride; + uint8_t* const a_ptr = pic->a + y * pic->a_stride; + for (x = 0; x < pic->width; ++x) { + const int alpha = a_ptr[x]; + if (alpha < 0xff) { + y_ptr[x] = BLEND(Y0, y_ptr[x], a_ptr[x]); + } + } + // Chroma blending every even line + if ((y & 1) == 0) { + uint8_t* const u = pic->u + (y >> 1) * pic->uv_stride; + uint8_t* const v = pic->v + (y >> 1) * pic->uv_stride; + uint8_t* const a_ptr2 = + (y + 1 == pic->height) ? a_ptr : a_ptr + pic->a_stride; + for (x = 0; x < uv_width; ++x) { + // Average four alpha values into a single blending weight. + // TODO(skal): might lead to visible contouring. Can we do better? + const int alpha = + a_ptr[2 * x + 0] + a_ptr[2 * x + 1] + + a_ptr2[2 * x + 0] + a_ptr2[2 * x + 1]; + u[x] = BLEND_10BIT(U0, u[x], alpha); + v[x] = BLEND_10BIT(V0, v[x], alpha); + } + if (pic->width & 1) { // rightmost pixel + const int alpha = 2 * (a_ptr[2 * x + 0] + a_ptr2[2 * x + 0]); + u[x] = BLEND_10BIT(U0, u[x], alpha); + v[x] = BLEND_10BIT(V0, v[x], alpha); + } + } + memset(a_ptr, 0xff, pic->width); + } + } else { + uint32_t* argb = pic->argb; + const uint32_t background = MakeARGB32(red, green, blue); + for (y = 0; y < pic->height; ++y) { + for (x = 0; x < pic->width; ++x) { + const int alpha = (argb[x] >> 24) & 0xff; + if (alpha != 0xff) { + if (alpha > 0) { + int r = (argb[x] >> 16) & 0xff; + int g = (argb[x] >> 8) & 0xff; + int b = (argb[x] >> 0) & 0xff; + r = BLEND(red, r, alpha); + g = BLEND(green, g, alpha); + b = BLEND(blue, b, alpha); + argb[x] = MakeARGB32(r, g, b); + } else { + argb[x] = background; + } + } + } + argb += pic->argb_stride; + } + } +} + +#undef BLEND +#undef BLEND_10BIT + +//------------------------------------------------------------------------------ diff --git a/thirdparty/libwebp/enc/predictor_enc.c b/thirdparty/libwebp/enc/predictor_enc.c new file mode 100644 index 0000000000..0639b74f1c --- /dev/null +++ b/thirdparty/libwebp/enc/predictor_enc.c @@ -0,0 +1,750 @@ +// Copyright 2016 Google Inc. All Rights Reserved. +// +// Use of this source code is governed by a BSD-style license +// that can be found in the COPYING file in the root of the source +// tree. An additional intellectual property rights grant can be found +// in the file PATENTS. All contributing project authors may +// be found in the AUTHORS file in the root of the source tree. +// ----------------------------------------------------------------------------- +// +// Image transform methods for lossless encoder. +// +// Authors: Vikas Arora (vikaas.arora@gmail.com) +// Jyrki Alakuijala (jyrki@google.com) +// Urvang Joshi (urvang@google.com) +// Vincent Rabaud (vrabaud@google.com) + +#include "../dsp/lossless.h" +#include "../dsp/lossless_common.h" +#include "./vp8li_enc.h" + +#define MAX_DIFF_COST (1e30f) + +static const float kSpatialPredictorBias = 15.f; +static const int kPredLowEffort = 11; +static const uint32_t kMaskAlpha = 0xff000000; + +// Mostly used to reduce code size + readability +static WEBP_INLINE int GetMin(int a, int b) { return (a > b) ? b : a; } +static WEBP_INLINE int GetMax(int a, int b) { return (a < b) ? b : a; } + +//------------------------------------------------------------------------------ +// Methods to calculate Entropy (Shannon). + +static float PredictionCostSpatial(const int counts[256], int weight_0, + double exp_val) { + const int significant_symbols = 256 >> 4; + const double exp_decay_factor = 0.6; + double bits = weight_0 * counts[0]; + int i; + for (i = 1; i < significant_symbols; ++i) { + bits += exp_val * (counts[i] + counts[256 - i]); + exp_val *= exp_decay_factor; + } + return (float)(-0.1 * bits); +} + +static float PredictionCostSpatialHistogram(const int accumulated[4][256], + const int tile[4][256]) { + int i; + double retval = 0; + for (i = 0; i < 4; ++i) { + const double kExpValue = 0.94; + retval += PredictionCostSpatial(tile[i], 1, kExpValue); + retval += VP8LCombinedShannonEntropy(tile[i], accumulated[i]); + } + return (float)retval; +} + +static WEBP_INLINE void UpdateHisto(int histo_argb[4][256], uint32_t argb) { + ++histo_argb[0][argb >> 24]; + ++histo_argb[1][(argb >> 16) & 0xff]; + ++histo_argb[2][(argb >> 8) & 0xff]; + ++histo_argb[3][argb & 0xff]; +} + +//------------------------------------------------------------------------------ +// Spatial transform functions. + +static WEBP_INLINE void PredictBatch(int mode, int x_start, int y, + int num_pixels, const uint32_t* current, + const uint32_t* upper, uint32_t* out) { + if (x_start == 0) { + if (y == 0) { + // ARGB_BLACK. + VP8LPredictorsSub[0](current, NULL, 1, out); + } else { + // Top one. + VP8LPredictorsSub[2](current, upper, 1, out); + } + ++x_start; + ++out; + --num_pixels; + } + if (y == 0) { + // Left one. + VP8LPredictorsSub[1](current + x_start, NULL, num_pixels, out); + } else { + VP8LPredictorsSub[mode](current + x_start, upper + x_start, num_pixels, + out); + } +} + +static int MaxDiffBetweenPixels(uint32_t p1, uint32_t p2) { + const int diff_a = abs((int)(p1 >> 24) - (int)(p2 >> 24)); + const int diff_r = abs((int)((p1 >> 16) & 0xff) - (int)((p2 >> 16) & 0xff)); + const int diff_g = abs((int)((p1 >> 8) & 0xff) - (int)((p2 >> 8) & 0xff)); + const int diff_b = abs((int)(p1 & 0xff) - (int)(p2 & 0xff)); + return GetMax(GetMax(diff_a, diff_r), GetMax(diff_g, diff_b)); +} + +static int MaxDiffAroundPixel(uint32_t current, uint32_t up, uint32_t down, + uint32_t left, uint32_t right) { + const int diff_up = MaxDiffBetweenPixels(current, up); + const int diff_down = MaxDiffBetweenPixels(current, down); + const int diff_left = MaxDiffBetweenPixels(current, left); + const int diff_right = MaxDiffBetweenPixels(current, right); + return GetMax(GetMax(diff_up, diff_down), GetMax(diff_left, diff_right)); +} + +static uint32_t AddGreenToBlueAndRed(uint32_t argb) { + const uint32_t green = (argb >> 8) & 0xff; + uint32_t red_blue = argb & 0x00ff00ffu; + red_blue += (green << 16) | green; + red_blue &= 0x00ff00ffu; + return (argb & 0xff00ff00u) | red_blue; +} + +static void MaxDiffsForRow(int width, int stride, const uint32_t* const argb, + uint8_t* const max_diffs, int used_subtract_green) { + uint32_t current, up, down, left, right; + int x; + if (width <= 2) return; + current = argb[0]; + right = argb[1]; + if (used_subtract_green) { + current = AddGreenToBlueAndRed(current); + right = AddGreenToBlueAndRed(right); + } + // max_diffs[0] and max_diffs[width - 1] are never used. + for (x = 1; x < width - 1; ++x) { + up = argb[-stride + x]; + down = argb[stride + x]; + left = current; + current = right; + right = argb[x + 1]; + if (used_subtract_green) { + up = AddGreenToBlueAndRed(up); + down = AddGreenToBlueAndRed(down); + right = AddGreenToBlueAndRed(right); + } + max_diffs[x] = MaxDiffAroundPixel(current, up, down, left, right); + } +} + +// Quantize the difference between the actual component value and its prediction +// to a multiple of quantization, working modulo 256, taking care not to cross +// a boundary (inclusive upper limit). +static uint8_t NearLosslessComponent(uint8_t value, uint8_t predict, + uint8_t boundary, int quantization) { + const int residual = (value - predict) & 0xff; + const int boundary_residual = (boundary - predict) & 0xff; + const int lower = residual & ~(quantization - 1); + const int upper = lower + quantization; + // Resolve ties towards a value closer to the prediction (i.e. towards lower + // if value comes after prediction and towards upper otherwise). + const int bias = ((boundary - value) & 0xff) < boundary_residual; + if (residual - lower < upper - residual + bias) { + // lower is closer to residual than upper. + if (residual > boundary_residual && lower <= boundary_residual) { + // Halve quantization step to avoid crossing boundary. This midpoint is + // on the same side of boundary as residual because midpoint >= residual + // (since lower is closer than upper) and residual is above the boundary. + return lower + (quantization >> 1); + } + return lower; + } else { + // upper is closer to residual than lower. + if (residual <= boundary_residual && upper > boundary_residual) { + // Halve quantization step to avoid crossing boundary. This midpoint is + // on the same side of boundary as residual because midpoint <= residual + // (since upper is closer than lower) and residual is below the boundary. + return lower + (quantization >> 1); + } + return upper & 0xff; + } +} + +// Quantize every component of the difference between the actual pixel value and +// its prediction to a multiple of a quantization (a power of 2, not larger than +// max_quantization which is a power of 2, smaller than max_diff). Take care if +// value and predict have undergone subtract green, which means that red and +// blue are represented as offsets from green. +static uint32_t NearLossless(uint32_t value, uint32_t predict, + int max_quantization, int max_diff, + int used_subtract_green) { + int quantization; + uint8_t new_green = 0; + uint8_t green_diff = 0; + uint8_t a, r, g, b; + if (max_diff <= 2) { + return VP8LSubPixels(value, predict); + } + quantization = max_quantization; + while (quantization >= max_diff) { + quantization >>= 1; + } + if ((value >> 24) == 0 || (value >> 24) == 0xff) { + // Preserve transparency of fully transparent or fully opaque pixels. + a = ((value >> 24) - (predict >> 24)) & 0xff; + } else { + a = NearLosslessComponent(value >> 24, predict >> 24, 0xff, quantization); + } + g = NearLosslessComponent((value >> 8) & 0xff, (predict >> 8) & 0xff, 0xff, + quantization); + if (used_subtract_green) { + // The green offset will be added to red and blue components during decoding + // to obtain the actual red and blue values. + new_green = ((predict >> 8) + g) & 0xff; + // The amount by which green has been adjusted during quantization. It is + // subtracted from red and blue for compensation, to avoid accumulating two + // quantization errors in them. + green_diff = (new_green - (value >> 8)) & 0xff; + } + r = NearLosslessComponent(((value >> 16) - green_diff) & 0xff, + (predict >> 16) & 0xff, 0xff - new_green, + quantization); + b = NearLosslessComponent((value - green_diff) & 0xff, predict & 0xff, + 0xff - new_green, quantization); + return ((uint32_t)a << 24) | ((uint32_t)r << 16) | ((uint32_t)g << 8) | b; +} + +// Stores the difference between the pixel and its prediction in "out". +// In case of a lossy encoding, updates the source image to avoid propagating +// the deviation further to pixels which depend on the current pixel for their +// predictions. +static WEBP_INLINE void GetResidual( + int width, int height, uint32_t* const upper_row, + uint32_t* const current_row, const uint8_t* const max_diffs, int mode, + int x_start, int x_end, int y, int max_quantization, int exact, + int used_subtract_green, uint32_t* const out) { + if (exact) { + PredictBatch(mode, x_start, y, x_end - x_start, current_row, upper_row, + out); + } else { + const VP8LPredictorFunc pred_func = VP8LPredictors[mode]; + int x; + for (x = x_start; x < x_end; ++x) { + uint32_t predict; + uint32_t residual; + if (y == 0) { + predict = (x == 0) ? ARGB_BLACK : current_row[x - 1]; // Left. + } else if (x == 0) { + predict = upper_row[x]; // Top. + } else { + predict = pred_func(current_row[x - 1], upper_row + x); + } + if (max_quantization == 1 || mode == 0 || y == 0 || y == height - 1 || + x == 0 || x == width - 1) { + residual = VP8LSubPixels(current_row[x], predict); + } else { + residual = NearLossless(current_row[x], predict, max_quantization, + max_diffs[x], used_subtract_green); + // Update the source image. + current_row[x] = VP8LAddPixels(predict, residual); + // x is never 0 here so we do not need to update upper_row like below. + } + if ((current_row[x] & kMaskAlpha) == 0) { + // If alpha is 0, cleanup RGB. We can choose the RGB values of the + // residual for best compression. The prediction of alpha itself can be + // non-zero and must be kept though. We choose RGB of the residual to be + // 0. + residual &= kMaskAlpha; + // Update the source image. + current_row[x] = predict & ~kMaskAlpha; + // The prediction for the rightmost pixel in a row uses the leftmost + // pixel + // in that row as its top-right context pixel. Hence if we change the + // leftmost pixel of current_row, the corresponding change must be + // applied + // to upper_row as well where top-right context is being read from. + if (x == 0 && y != 0) upper_row[width] = current_row[0]; + } + out[x - x_start] = residual; + } + } +} + +// Returns best predictor and updates the accumulated histogram. +// If max_quantization > 1, assumes that near lossless processing will be +// applied, quantizing residuals to multiples of quantization levels up to +// max_quantization (the actual quantization level depends on smoothness near +// the given pixel). +static int GetBestPredictorForTile(int width, int height, + int tile_x, int tile_y, int bits, + int accumulated[4][256], + uint32_t* const argb_scratch, + const uint32_t* const argb, + int max_quantization, + int exact, int used_subtract_green, + const uint32_t* const modes) { + const int kNumPredModes = 14; + const int start_x = tile_x << bits; + const int start_y = tile_y << bits; + const int tile_size = 1 << bits; + const int max_y = GetMin(tile_size, height - start_y); + const int max_x = GetMin(tile_size, width - start_x); + // Whether there exist columns just outside the tile. + const int have_left = (start_x > 0); + const int have_right = (max_x < width - start_x); + // Position and size of the strip covering the tile and adjacent columns if + // they exist. + const int context_start_x = start_x - have_left; + const int context_width = max_x + have_left + have_right; + const int tiles_per_row = VP8LSubSampleSize(width, bits); + // Prediction modes of the left and above neighbor tiles. + const int left_mode = (tile_x > 0) ? + (modes[tile_y * tiles_per_row + tile_x - 1] >> 8) & 0xff : 0xff; + const int above_mode = (tile_y > 0) ? + (modes[(tile_y - 1) * tiles_per_row + tile_x] >> 8) & 0xff : 0xff; + // The width of upper_row and current_row is one pixel larger than image width + // to allow the top right pixel to point to the leftmost pixel of the next row + // when at the right edge. + uint32_t* upper_row = argb_scratch; + uint32_t* current_row = upper_row + width + 1; + uint8_t* const max_diffs = (uint8_t*)(current_row + width + 1); + float best_diff = MAX_DIFF_COST; + int best_mode = 0; + int mode; + int histo_stack_1[4][256]; + int histo_stack_2[4][256]; + // Need pointers to be able to swap arrays. + int (*histo_argb)[256] = histo_stack_1; + int (*best_histo)[256] = histo_stack_2; + int i, j; + uint32_t residuals[1 << MAX_TRANSFORM_BITS]; + assert(bits <= MAX_TRANSFORM_BITS); + assert(max_x <= (1 << MAX_TRANSFORM_BITS)); + + for (mode = 0; mode < kNumPredModes; ++mode) { + float cur_diff; + int relative_y; + memset(histo_argb, 0, sizeof(histo_stack_1)); + if (start_y > 0) { + // Read the row above the tile which will become the first upper_row. + // Include a pixel to the left if it exists; include a pixel to the right + // in all cases (wrapping to the leftmost pixel of the next row if it does + // not exist). + memcpy(current_row + context_start_x, + argb + (start_y - 1) * width + context_start_x, + sizeof(*argb) * (max_x + have_left + 1)); + } + for (relative_y = 0; relative_y < max_y; ++relative_y) { + const int y = start_y + relative_y; + int relative_x; + uint32_t* tmp = upper_row; + upper_row = current_row; + current_row = tmp; + // Read current_row. Include a pixel to the left if it exists; include a + // pixel to the right in all cases except at the bottom right corner of + // the image (wrapping to the leftmost pixel of the next row if it does + // not exist in the current row). + memcpy(current_row + context_start_x, + argb + y * width + context_start_x, + sizeof(*argb) * (max_x + have_left + (y + 1 < height))); + if (max_quantization > 1 && y >= 1 && y + 1 < height) { + MaxDiffsForRow(context_width, width, argb + y * width + context_start_x, + max_diffs + context_start_x, used_subtract_green); + } + + GetResidual(width, height, upper_row, current_row, max_diffs, mode, + start_x, start_x + max_x, y, max_quantization, exact, + used_subtract_green, residuals); + for (relative_x = 0; relative_x < max_x; ++relative_x) { + UpdateHisto(histo_argb, residuals[relative_x]); + } + } + cur_diff = PredictionCostSpatialHistogram( + (const int (*)[256])accumulated, (const int (*)[256])histo_argb); + // Favor keeping the areas locally similar. + if (mode == left_mode) cur_diff -= kSpatialPredictorBias; + if (mode == above_mode) cur_diff -= kSpatialPredictorBias; + + if (cur_diff < best_diff) { + int (*tmp)[256] = histo_argb; + histo_argb = best_histo; + best_histo = tmp; + best_diff = cur_diff; + best_mode = mode; + } + } + + for (i = 0; i < 4; i++) { + for (j = 0; j < 256; j++) { + accumulated[i][j] += best_histo[i][j]; + } + } + + return best_mode; +} + +// Converts pixels of the image to residuals with respect to predictions. +// If max_quantization > 1, applies near lossless processing, quantizing +// residuals to multiples of quantization levels up to max_quantization +// (the actual quantization level depends on smoothness near the given pixel). +static void CopyImageWithPrediction(int width, int height, + int bits, uint32_t* const modes, + uint32_t* const argb_scratch, + uint32_t* const argb, + int low_effort, int max_quantization, + int exact, int used_subtract_green) { + const int tiles_per_row = VP8LSubSampleSize(width, bits); + // The width of upper_row and current_row is one pixel larger than image width + // to allow the top right pixel to point to the leftmost pixel of the next row + // when at the right edge. + uint32_t* upper_row = argb_scratch; + uint32_t* current_row = upper_row + width + 1; + uint8_t* current_max_diffs = (uint8_t*)(current_row + width + 1); + uint8_t* lower_max_diffs = current_max_diffs + width; + int y; + + for (y = 0; y < height; ++y) { + int x; + uint32_t* const tmp32 = upper_row; + upper_row = current_row; + current_row = tmp32; + memcpy(current_row, argb + y * width, + sizeof(*argb) * (width + (y + 1 < height))); + + if (low_effort) { + PredictBatch(kPredLowEffort, 0, y, width, current_row, upper_row, + argb + y * width); + } else { + if (max_quantization > 1) { + // Compute max_diffs for the lower row now, because that needs the + // contents of argb for the current row, which we will overwrite with + // residuals before proceeding with the next row. + uint8_t* const tmp8 = current_max_diffs; + current_max_diffs = lower_max_diffs; + lower_max_diffs = tmp8; + if (y + 2 < height) { + MaxDiffsForRow(width, width, argb + (y + 1) * width, lower_max_diffs, + used_subtract_green); + } + } + for (x = 0; x < width;) { + const int mode = + (modes[(y >> bits) * tiles_per_row + (x >> bits)] >> 8) & 0xff; + int x_end = x + (1 << bits); + if (x_end > width) x_end = width; + GetResidual(width, height, upper_row, current_row, current_max_diffs, + mode, x, x_end, y, max_quantization, exact, + used_subtract_green, argb + y * width + x); + x = x_end; + } + } + } +} + +// Finds the best predictor for each tile, and converts the image to residuals +// with respect to predictions. If near_lossless_quality < 100, applies +// near lossless processing, shaving off more bits of residuals for lower +// qualities. +void VP8LResidualImage(int width, int height, int bits, int low_effort, + uint32_t* const argb, uint32_t* const argb_scratch, + uint32_t* const image, int near_lossless_quality, + int exact, int used_subtract_green) { + const int tiles_per_row = VP8LSubSampleSize(width, bits); + const int tiles_per_col = VP8LSubSampleSize(height, bits); + int tile_y; + int histo[4][256]; + const int max_quantization = 1 << VP8LNearLosslessBits(near_lossless_quality); + if (low_effort) { + int i; + for (i = 0; i < tiles_per_row * tiles_per_col; ++i) { + image[i] = ARGB_BLACK | (kPredLowEffort << 8); + } + } else { + memset(histo, 0, sizeof(histo)); + for (tile_y = 0; tile_y < tiles_per_col; ++tile_y) { + int tile_x; + for (tile_x = 0; tile_x < tiles_per_row; ++tile_x) { + const int pred = GetBestPredictorForTile(width, height, tile_x, tile_y, + bits, histo, argb_scratch, argb, max_quantization, exact, + used_subtract_green, image); + image[tile_y * tiles_per_row + tile_x] = ARGB_BLACK | (pred << 8); + } + } + } + + CopyImageWithPrediction(width, height, bits, image, argb_scratch, argb, + low_effort, max_quantization, exact, + used_subtract_green); +} + +//------------------------------------------------------------------------------ +// Color transform functions. + +static WEBP_INLINE void MultipliersClear(VP8LMultipliers* const m) { + m->green_to_red_ = 0; + m->green_to_blue_ = 0; + m->red_to_blue_ = 0; +} + +static WEBP_INLINE void ColorCodeToMultipliers(uint32_t color_code, + VP8LMultipliers* const m) { + m->green_to_red_ = (color_code >> 0) & 0xff; + m->green_to_blue_ = (color_code >> 8) & 0xff; + m->red_to_blue_ = (color_code >> 16) & 0xff; +} + +static WEBP_INLINE uint32_t MultipliersToColorCode( + const VP8LMultipliers* const m) { + return 0xff000000u | + ((uint32_t)(m->red_to_blue_) << 16) | + ((uint32_t)(m->green_to_blue_) << 8) | + m->green_to_red_; +} + +static float PredictionCostCrossColor(const int accumulated[256], + const int counts[256]) { + // Favor low entropy, locally and globally. + // Favor small absolute values for PredictionCostSpatial + static const double kExpValue = 2.4; + return VP8LCombinedShannonEntropy(counts, accumulated) + + PredictionCostSpatial(counts, 3, kExpValue); +} + +static float GetPredictionCostCrossColorRed( + const uint32_t* argb, int stride, int tile_width, int tile_height, + VP8LMultipliers prev_x, VP8LMultipliers prev_y, int green_to_red, + const int accumulated_red_histo[256]) { + int histo[256] = { 0 }; + float cur_diff; + + VP8LCollectColorRedTransforms(argb, stride, tile_width, tile_height, + green_to_red, histo); + + cur_diff = PredictionCostCrossColor(accumulated_red_histo, histo); + if ((uint8_t)green_to_red == prev_x.green_to_red_) { + cur_diff -= 3; // favor keeping the areas locally similar + } + if ((uint8_t)green_to_red == prev_y.green_to_red_) { + cur_diff -= 3; // favor keeping the areas locally similar + } + if (green_to_red == 0) { + cur_diff -= 3; + } + return cur_diff; +} + +static void GetBestGreenToRed( + const uint32_t* argb, int stride, int tile_width, int tile_height, + VP8LMultipliers prev_x, VP8LMultipliers prev_y, int quality, + const int accumulated_red_histo[256], VP8LMultipliers* const best_tx) { + const int kMaxIters = 4 + ((7 * quality) >> 8); // in range [4..6] + int green_to_red_best = 0; + int iter, offset; + float best_diff = GetPredictionCostCrossColorRed( + argb, stride, tile_width, tile_height, prev_x, prev_y, + green_to_red_best, accumulated_red_histo); + for (iter = 0; iter < kMaxIters; ++iter) { + // ColorTransformDelta is a 3.5 bit fixed point, so 32 is equal to + // one in color computation. Having initial delta here as 1 is sufficient + // to explore the range of (-2, 2). + const int delta = 32 >> iter; + // Try a negative and a positive delta from the best known value. + for (offset = -delta; offset <= delta; offset += 2 * delta) { + const int green_to_red_cur = offset + green_to_red_best; + const float cur_diff = GetPredictionCostCrossColorRed( + argb, stride, tile_width, tile_height, prev_x, prev_y, + green_to_red_cur, accumulated_red_histo); + if (cur_diff < best_diff) { + best_diff = cur_diff; + green_to_red_best = green_to_red_cur; + } + } + } + best_tx->green_to_red_ = green_to_red_best; +} + +static float GetPredictionCostCrossColorBlue( + const uint32_t* argb, int stride, int tile_width, int tile_height, + VP8LMultipliers prev_x, VP8LMultipliers prev_y, + int green_to_blue, int red_to_blue, const int accumulated_blue_histo[256]) { + int histo[256] = { 0 }; + float cur_diff; + + VP8LCollectColorBlueTransforms(argb, stride, tile_width, tile_height, + green_to_blue, red_to_blue, histo); + + cur_diff = PredictionCostCrossColor(accumulated_blue_histo, histo); + if ((uint8_t)green_to_blue == prev_x.green_to_blue_) { + cur_diff -= 3; // favor keeping the areas locally similar + } + if ((uint8_t)green_to_blue == prev_y.green_to_blue_) { + cur_diff -= 3; // favor keeping the areas locally similar + } + if ((uint8_t)red_to_blue == prev_x.red_to_blue_) { + cur_diff -= 3; // favor keeping the areas locally similar + } + if ((uint8_t)red_to_blue == prev_y.red_to_blue_) { + cur_diff -= 3; // favor keeping the areas locally similar + } + if (green_to_blue == 0) { + cur_diff -= 3; + } + if (red_to_blue == 0) { + cur_diff -= 3; + } + return cur_diff; +} + +#define kGreenRedToBlueNumAxis 8 +#define kGreenRedToBlueMaxIters 7 +static void GetBestGreenRedToBlue( + const uint32_t* argb, int stride, int tile_width, int tile_height, + VP8LMultipliers prev_x, VP8LMultipliers prev_y, int quality, + const int accumulated_blue_histo[256], + VP8LMultipliers* const best_tx) { + const int8_t offset[kGreenRedToBlueNumAxis][2] = + {{0, -1}, {0, 1}, {-1, 0}, {1, 0}, {-1, -1}, {-1, 1}, {1, -1}, {1, 1}}; + const int8_t delta_lut[kGreenRedToBlueMaxIters] = { 16, 16, 8, 4, 2, 2, 2 }; + const int iters = + (quality < 25) ? 1 : (quality > 50) ? kGreenRedToBlueMaxIters : 4; + int green_to_blue_best = 0; + int red_to_blue_best = 0; + int iter; + // Initial value at origin: + float best_diff = GetPredictionCostCrossColorBlue( + argb, stride, tile_width, tile_height, prev_x, prev_y, + green_to_blue_best, red_to_blue_best, accumulated_blue_histo); + for (iter = 0; iter < iters; ++iter) { + const int delta = delta_lut[iter]; + int axis; + for (axis = 0; axis < kGreenRedToBlueNumAxis; ++axis) { + const int green_to_blue_cur = + offset[axis][0] * delta + green_to_blue_best; + const int red_to_blue_cur = offset[axis][1] * delta + red_to_blue_best; + const float cur_diff = GetPredictionCostCrossColorBlue( + argb, stride, tile_width, tile_height, prev_x, prev_y, + green_to_blue_cur, red_to_blue_cur, accumulated_blue_histo); + if (cur_diff < best_diff) { + best_diff = cur_diff; + green_to_blue_best = green_to_blue_cur; + red_to_blue_best = red_to_blue_cur; + } + if (quality < 25 && iter == 4) { + // Only axis aligned diffs for lower quality. + break; // next iter. + } + } + if (delta == 2 && green_to_blue_best == 0 && red_to_blue_best == 0) { + // Further iterations would not help. + break; // out of iter-loop. + } + } + best_tx->green_to_blue_ = green_to_blue_best; + best_tx->red_to_blue_ = red_to_blue_best; +} +#undef kGreenRedToBlueMaxIters +#undef kGreenRedToBlueNumAxis + +static VP8LMultipliers GetBestColorTransformForTile( + int tile_x, int tile_y, int bits, + VP8LMultipliers prev_x, + VP8LMultipliers prev_y, + int quality, int xsize, int ysize, + const int accumulated_red_histo[256], + const int accumulated_blue_histo[256], + const uint32_t* const argb) { + const int max_tile_size = 1 << bits; + const int tile_y_offset = tile_y * max_tile_size; + const int tile_x_offset = tile_x * max_tile_size; + const int all_x_max = GetMin(tile_x_offset + max_tile_size, xsize); + const int all_y_max = GetMin(tile_y_offset + max_tile_size, ysize); + const int tile_width = all_x_max - tile_x_offset; + const int tile_height = all_y_max - tile_y_offset; + const uint32_t* const tile_argb = argb + tile_y_offset * xsize + + tile_x_offset; + VP8LMultipliers best_tx; + MultipliersClear(&best_tx); + + GetBestGreenToRed(tile_argb, xsize, tile_width, tile_height, + prev_x, prev_y, quality, accumulated_red_histo, &best_tx); + GetBestGreenRedToBlue(tile_argb, xsize, tile_width, tile_height, + prev_x, prev_y, quality, accumulated_blue_histo, + &best_tx); + return best_tx; +} + +static void CopyTileWithColorTransform(int xsize, int ysize, + int tile_x, int tile_y, + int max_tile_size, + VP8LMultipliers color_transform, + uint32_t* argb) { + const int xscan = GetMin(max_tile_size, xsize - tile_x); + int yscan = GetMin(max_tile_size, ysize - tile_y); + argb += tile_y * xsize + tile_x; + while (yscan-- > 0) { + VP8LTransformColor(&color_transform, argb, xscan); + argb += xsize; + } +} + +void VP8LColorSpaceTransform(int width, int height, int bits, int quality, + uint32_t* const argb, uint32_t* image) { + const int max_tile_size = 1 << bits; + const int tile_xsize = VP8LSubSampleSize(width, bits); + const int tile_ysize = VP8LSubSampleSize(height, bits); + int accumulated_red_histo[256] = { 0 }; + int accumulated_blue_histo[256] = { 0 }; + int tile_x, tile_y; + VP8LMultipliers prev_x, prev_y; + MultipliersClear(&prev_y); + MultipliersClear(&prev_x); + for (tile_y = 0; tile_y < tile_ysize; ++tile_y) { + for (tile_x = 0; tile_x < tile_xsize; ++tile_x) { + int y; + const int tile_x_offset = tile_x * max_tile_size; + const int tile_y_offset = tile_y * max_tile_size; + const int all_x_max = GetMin(tile_x_offset + max_tile_size, width); + const int all_y_max = GetMin(tile_y_offset + max_tile_size, height); + const int offset = tile_y * tile_xsize + tile_x; + if (tile_y != 0) { + ColorCodeToMultipliers(image[offset - tile_xsize], &prev_y); + } + prev_x = GetBestColorTransformForTile(tile_x, tile_y, bits, + prev_x, prev_y, + quality, width, height, + accumulated_red_histo, + accumulated_blue_histo, + argb); + image[offset] = MultipliersToColorCode(&prev_x); + CopyTileWithColorTransform(width, height, tile_x_offset, tile_y_offset, + max_tile_size, prev_x, argb); + + // Gather accumulated histogram data. + for (y = tile_y_offset; y < all_y_max; ++y) { + int ix = y * width + tile_x_offset; + const int ix_end = ix + all_x_max - tile_x_offset; + for (; ix < ix_end; ++ix) { + const uint32_t pix = argb[ix]; + if (ix >= 2 && + pix == argb[ix - 2] && + pix == argb[ix - 1]) { + continue; // repeated pixels are handled by backward references + } + if (ix >= width + 2 && + argb[ix - 2] == argb[ix - width - 2] && + argb[ix - 1] == argb[ix - width - 1] && + pix == argb[ix - width]) { + continue; // repeated pixels are handled by backward references + } + ++accumulated_red_histo[(pix >> 16) & 0xff]; + ++accumulated_blue_histo[(pix >> 0) & 0xff]; + } + } + } + } +} diff --git a/thirdparty/libwebp/enc/quant.c b/thirdparty/libwebp/enc/quant.c deleted file mode 100644 index 07ffaf0aeb..0000000000 --- a/thirdparty/libwebp/enc/quant.c +++ /dev/null @@ -1,1284 +0,0 @@ -// Copyright 2011 Google Inc. All Rights Reserved. -// -// Use of this source code is governed by a BSD-style license -// that can be found in the COPYING file in the root of the source -// tree. An additional intellectual property rights grant can be found -// in the file PATENTS. All contributing project authors may -// be found in the AUTHORS file in the root of the source tree. -// ----------------------------------------------------------------------------- -// -// Quantization -// -// Author: Skal (pascal.massimino@gmail.com) - -#include -#include -#include // for abs() - -#include "./vp8enci.h" -#include "./cost.h" - -#define DO_TRELLIS_I4 1 -#define DO_TRELLIS_I16 1 // not a huge gain, but ok at low bitrate. -#define DO_TRELLIS_UV 0 // disable trellis for UV. Risky. Not worth. -#define USE_TDISTO 1 - -#define MID_ALPHA 64 // neutral value for susceptibility -#define MIN_ALPHA 30 // lowest usable value for susceptibility -#define MAX_ALPHA 100 // higher meaningful value for susceptibility - -#define SNS_TO_DQ 0.9 // Scaling constant between the sns value and the QP - // power-law modulation. Must be strictly less than 1. - -// number of non-zero coeffs below which we consider the block very flat -// (and apply a penalty to complex predictions) -#define FLATNESS_LIMIT_I16 10 // I16 mode -#define FLATNESS_LIMIT_I4 3 // I4 mode -#define FLATNESS_LIMIT_UV 2 // UV mode -#define FLATNESS_PENALTY 140 // roughly ~1bit per block - -#define MULT_8B(a, b) (((a) * (b) + 128) >> 8) - -#define RD_DISTO_MULT 256 // distortion multiplier (equivalent of lambda) - -// #define DEBUG_BLOCK - -//------------------------------------------------------------------------------ - -#if defined(DEBUG_BLOCK) - -#include -#include - -static void PrintBlockInfo(const VP8EncIterator* const it, - const VP8ModeScore* const rd) { - int i, j; - const int is_i16 = (it->mb_->type_ == 1); - const uint8_t* const y_in = it->yuv_in_ + Y_OFF_ENC; - const uint8_t* const y_out = it->yuv_out_ + Y_OFF_ENC; - const uint8_t* const uv_in = it->yuv_in_ + U_OFF_ENC; - const uint8_t* const uv_out = it->yuv_out_ + U_OFF_ENC; - printf("SOURCE / OUTPUT / ABS DELTA\n"); - for (j = 0; j < 16; ++j) { - for (i = 0; i < 16; ++i) printf("%3d ", y_in[i + j * BPS]); - printf(" "); - for (i = 0; i < 16; ++i) printf("%3d ", y_out[i + j * BPS]); - printf(" "); - for (i = 0; i < 16; ++i) { - printf("%1d ", abs(y_in[i + j * BPS] - y_out[i + j * BPS])); - } - printf("\n"); - } - printf("\n"); // newline before the U/V block - for (j = 0; j < 8; ++j) { - for (i = 0; i < 8; ++i) printf("%3d ", uv_in[i + j * BPS]); - printf(" "); - for (i = 8; i < 16; ++i) printf("%3d ", uv_in[i + j * BPS]); - printf(" "); - for (i = 0; i < 8; ++i) printf("%3d ", uv_out[i + j * BPS]); - printf(" "); - for (i = 8; i < 16; ++i) printf("%3d ", uv_out[i + j * BPS]); - printf(" "); - for (i = 0; i < 8; ++i) { - printf("%1d ", abs(uv_out[i + j * BPS] - uv_in[i + j * BPS])); - } - printf(" "); - for (i = 8; i < 16; ++i) { - printf("%1d ", abs(uv_out[i + j * BPS] - uv_in[i + j * BPS])); - } - printf("\n"); - } - printf("\nD:%d SD:%d R:%d H:%d nz:0x%x score:%d\n", - (int)rd->D, (int)rd->SD, (int)rd->R, (int)rd->H, (int)rd->nz, - (int)rd->score); - if (is_i16) { - printf("Mode: %d\n", rd->mode_i16); - printf("y_dc_levels:"); - for (i = 0; i < 16; ++i) printf("%3d ", rd->y_dc_levels[i]); - printf("\n"); - } else { - printf("Modes[16]: "); - for (i = 0; i < 16; ++i) printf("%d ", rd->modes_i4[i]); - printf("\n"); - } - printf("y_ac_levels:\n"); - for (j = 0; j < 16; ++j) { - for (i = is_i16 ? 1 : 0; i < 16; ++i) { - printf("%4d ", rd->y_ac_levels[j][i]); - } - printf("\n"); - } - printf("\n"); - printf("uv_levels (mode=%d):\n", rd->mode_uv); - for (j = 0; j < 8; ++j) { - for (i = 0; i < 16; ++i) { - printf("%4d ", rd->uv_levels[j][i]); - } - printf("\n"); - } -} - -#endif // DEBUG_BLOCK - -//------------------------------------------------------------------------------ - -static WEBP_INLINE int clip(int v, int m, int M) { - return v < m ? m : v > M ? M : v; -} - -static const uint8_t kZigzag[16] = { - 0, 1, 4, 8, 5, 2, 3, 6, 9, 12, 13, 10, 7, 11, 14, 15 -}; - -static const uint8_t kDcTable[128] = { - 4, 5, 6, 7, 8, 9, 10, 10, - 11, 12, 13, 14, 15, 16, 17, 17, - 18, 19, 20, 20, 21, 21, 22, 22, - 23, 23, 24, 25, 25, 26, 27, 28, - 29, 30, 31, 32, 33, 34, 35, 36, - 37, 37, 38, 39, 40, 41, 42, 43, - 44, 45, 46, 46, 47, 48, 49, 50, - 51, 52, 53, 54, 55, 56, 57, 58, - 59, 60, 61, 62, 63, 64, 65, 66, - 67, 68, 69, 70, 71, 72, 73, 74, - 75, 76, 76, 77, 78, 79, 80, 81, - 82, 83, 84, 85, 86, 87, 88, 89, - 91, 93, 95, 96, 98, 100, 101, 102, - 104, 106, 108, 110, 112, 114, 116, 118, - 122, 124, 126, 128, 130, 132, 134, 136, - 138, 140, 143, 145, 148, 151, 154, 157 -}; - -static const uint16_t kAcTable[128] = { - 4, 5, 6, 7, 8, 9, 10, 11, - 12, 13, 14, 15, 16, 17, 18, 19, - 20, 21, 22, 23, 24, 25, 26, 27, - 28, 29, 30, 31, 32, 33, 34, 35, - 36, 37, 38, 39, 40, 41, 42, 43, - 44, 45, 46, 47, 48, 49, 50, 51, - 52, 53, 54, 55, 56, 57, 58, 60, - 62, 64, 66, 68, 70, 72, 74, 76, - 78, 80, 82, 84, 86, 88, 90, 92, - 94, 96, 98, 100, 102, 104, 106, 108, - 110, 112, 114, 116, 119, 122, 125, 128, - 131, 134, 137, 140, 143, 146, 149, 152, - 155, 158, 161, 164, 167, 170, 173, 177, - 181, 185, 189, 193, 197, 201, 205, 209, - 213, 217, 221, 225, 229, 234, 239, 245, - 249, 254, 259, 264, 269, 274, 279, 284 -}; - -static const uint16_t kAcTable2[128] = { - 8, 8, 9, 10, 12, 13, 15, 17, - 18, 20, 21, 23, 24, 26, 27, 29, - 31, 32, 34, 35, 37, 38, 40, 41, - 43, 44, 46, 48, 49, 51, 52, 54, - 55, 57, 58, 60, 62, 63, 65, 66, - 68, 69, 71, 72, 74, 75, 77, 79, - 80, 82, 83, 85, 86, 88, 89, 93, - 96, 99, 102, 105, 108, 111, 114, 117, - 120, 124, 127, 130, 133, 136, 139, 142, - 145, 148, 151, 155, 158, 161, 164, 167, - 170, 173, 176, 179, 184, 189, 193, 198, - 203, 207, 212, 217, 221, 226, 230, 235, - 240, 244, 249, 254, 258, 263, 268, 274, - 280, 286, 292, 299, 305, 311, 317, 323, - 330, 336, 342, 348, 354, 362, 370, 379, - 385, 393, 401, 409, 416, 424, 432, 440 -}; - -static const uint8_t kBiasMatrices[3][2] = { // [luma-ac,luma-dc,chroma][dc,ac] - { 96, 110 }, { 96, 108 }, { 110, 115 } -}; - -// Sharpening by (slightly) raising the hi-frequency coeffs. -// Hack-ish but helpful for mid-bitrate range. Use with care. -#define SHARPEN_BITS 11 // number of descaling bits for sharpening bias -static const uint8_t kFreqSharpening[16] = { - 0, 30, 60, 90, - 30, 60, 90, 90, - 60, 90, 90, 90, - 90, 90, 90, 90 -}; - -//------------------------------------------------------------------------------ -// Initialize quantization parameters in VP8Matrix - -// Returns the average quantizer -static int ExpandMatrix(VP8Matrix* const m, int type) { - int i, sum; - for (i = 0; i < 2; ++i) { - const int is_ac_coeff = (i > 0); - const int bias = kBiasMatrices[type][is_ac_coeff]; - m->iq_[i] = (1 << QFIX) / m->q_[i]; - m->bias_[i] = BIAS(bias); - // zthresh_ is the exact value such that QUANTDIV(coeff, iQ, B) is: - // * zero if coeff <= zthresh - // * non-zero if coeff > zthresh - m->zthresh_[i] = ((1 << QFIX) - 1 - m->bias_[i]) / m->iq_[i]; - } - for (i = 2; i < 16; ++i) { - m->q_[i] = m->q_[1]; - m->iq_[i] = m->iq_[1]; - m->bias_[i] = m->bias_[1]; - m->zthresh_[i] = m->zthresh_[1]; - } - for (sum = 0, i = 0; i < 16; ++i) { - if (type == 0) { // we only use sharpening for AC luma coeffs - m->sharpen_[i] = (kFreqSharpening[i] * m->q_[i]) >> SHARPEN_BITS; - } else { - m->sharpen_[i] = 0; - } - sum += m->q_[i]; - } - return (sum + 8) >> 4; -} - -static void CheckLambdaValue(int* const v) { if (*v < 1) *v = 1; } - -static void SetupMatrices(VP8Encoder* enc) { - int i; - const int tlambda_scale = - (enc->method_ >= 4) ? enc->config_->sns_strength - : 0; - const int num_segments = enc->segment_hdr_.num_segments_; - for (i = 0; i < num_segments; ++i) { - VP8SegmentInfo* const m = &enc->dqm_[i]; - const int q = m->quant_; - int q_i4, q_i16, q_uv; - m->y1_.q_[0] = kDcTable[clip(q + enc->dq_y1_dc_, 0, 127)]; - m->y1_.q_[1] = kAcTable[clip(q, 0, 127)]; - - m->y2_.q_[0] = kDcTable[ clip(q + enc->dq_y2_dc_, 0, 127)] * 2; - m->y2_.q_[1] = kAcTable2[clip(q + enc->dq_y2_ac_, 0, 127)]; - - m->uv_.q_[0] = kDcTable[clip(q + enc->dq_uv_dc_, 0, 117)]; - m->uv_.q_[1] = kAcTable[clip(q + enc->dq_uv_ac_, 0, 127)]; - - q_i4 = ExpandMatrix(&m->y1_, 0); - q_i16 = ExpandMatrix(&m->y2_, 1); - q_uv = ExpandMatrix(&m->uv_, 2); - - m->lambda_i4_ = (3 * q_i4 * q_i4) >> 7; - m->lambda_i16_ = (3 * q_i16 * q_i16); - m->lambda_uv_ = (3 * q_uv * q_uv) >> 6; - m->lambda_mode_ = (1 * q_i4 * q_i4) >> 7; - m->lambda_trellis_i4_ = (7 * q_i4 * q_i4) >> 3; - m->lambda_trellis_i16_ = (q_i16 * q_i16) >> 2; - m->lambda_trellis_uv_ = (q_uv * q_uv) << 1; - m->tlambda_ = (tlambda_scale * q_i4) >> 5; - - // none of these constants should be < 1 - CheckLambdaValue(&m->lambda_i4_); - CheckLambdaValue(&m->lambda_i16_); - CheckLambdaValue(&m->lambda_uv_); - CheckLambdaValue(&m->lambda_mode_); - CheckLambdaValue(&m->lambda_trellis_i4_); - CheckLambdaValue(&m->lambda_trellis_i16_); - CheckLambdaValue(&m->lambda_trellis_uv_); - CheckLambdaValue(&m->tlambda_); - - m->min_disto_ = 20 * m->y1_.q_[0]; // quantization-aware min disto - m->max_edge_ = 0; - - m->i4_penalty_ = 1000 * q_i4 * q_i4; - } -} - -//------------------------------------------------------------------------------ -// Initialize filtering parameters - -// Very small filter-strength values have close to no visual effect. So we can -// save a little decoding-CPU by turning filtering off for these. -#define FSTRENGTH_CUTOFF 2 - -static void SetupFilterStrength(VP8Encoder* const enc) { - int i; - // level0 is in [0..500]. Using '-f 50' as filter_strength is mid-filtering. - const int level0 = 5 * enc->config_->filter_strength; - for (i = 0; i < NUM_MB_SEGMENTS; ++i) { - VP8SegmentInfo* const m = &enc->dqm_[i]; - // We focus on the quantization of AC coeffs. - const int qstep = kAcTable[clip(m->quant_, 0, 127)] >> 2; - const int base_strength = - VP8FilterStrengthFromDelta(enc->filter_hdr_.sharpness_, qstep); - // Segments with lower complexity ('beta') will be less filtered. - const int f = base_strength * level0 / (256 + m->beta_); - m->fstrength_ = (f < FSTRENGTH_CUTOFF) ? 0 : (f > 63) ? 63 : f; - } - // We record the initial strength (mainly for the case of 1-segment only). - enc->filter_hdr_.level_ = enc->dqm_[0].fstrength_; - enc->filter_hdr_.simple_ = (enc->config_->filter_type == 0); - enc->filter_hdr_.sharpness_ = enc->config_->filter_sharpness; -} - -//------------------------------------------------------------------------------ - -// Note: if you change the values below, remember that the max range -// allowed by the syntax for DQ_UV is [-16,16]. -#define MAX_DQ_UV (6) -#define MIN_DQ_UV (-4) - -// We want to emulate jpeg-like behaviour where the expected "good" quality -// is around q=75. Internally, our "good" middle is around c=50. So we -// map accordingly using linear piece-wise function -static double QualityToCompression(double c) { - const double linear_c = (c < 0.75) ? c * (2. / 3.) : 2. * c - 1.; - // The file size roughly scales as pow(quantizer, 3.). Actually, the - // exponent is somewhere between 2.8 and 3.2, but we're mostly interested - // in the mid-quant range. So we scale the compressibility inversely to - // this power-law: quant ~= compression ^ 1/3. This law holds well for - // low quant. Finer modeling for high-quant would make use of kAcTable[] - // more explicitly. - const double v = pow(linear_c, 1 / 3.); - return v; -} - -static double QualityToJPEGCompression(double c, double alpha) { - // We map the complexity 'alpha' and quality setting 'c' to a compression - // exponent empirically matched to the compression curve of libjpeg6b. - // On average, the WebP output size will be roughly similar to that of a - // JPEG file compressed with same quality factor. - const double amin = 0.30; - const double amax = 0.85; - const double exp_min = 0.4; - const double exp_max = 0.9; - const double slope = (exp_min - exp_max) / (amax - amin); - // Linearly interpolate 'expn' from exp_min to exp_max - // in the [amin, amax] range. - const double expn = (alpha > amax) ? exp_min - : (alpha < amin) ? exp_max - : exp_max + slope * (alpha - amin); - const double v = pow(c, expn); - return v; -} - -static int SegmentsAreEquivalent(const VP8SegmentInfo* const S1, - const VP8SegmentInfo* const S2) { - return (S1->quant_ == S2->quant_) && (S1->fstrength_ == S2->fstrength_); -} - -static void SimplifySegments(VP8Encoder* const enc) { - int map[NUM_MB_SEGMENTS] = { 0, 1, 2, 3 }; - // 'num_segments_' is previously validated and <= NUM_MB_SEGMENTS, but an - // explicit check is needed to avoid a spurious warning about 'i' exceeding - // array bounds of 'dqm_' with some compilers (noticed with gcc-4.9). - const int num_segments = (enc->segment_hdr_.num_segments_ < NUM_MB_SEGMENTS) - ? enc->segment_hdr_.num_segments_ - : NUM_MB_SEGMENTS; - int num_final_segments = 1; - int s1, s2; - for (s1 = 1; s1 < num_segments; ++s1) { // find similar segments - const VP8SegmentInfo* const S1 = &enc->dqm_[s1]; - int found = 0; - // check if we already have similar segment - for (s2 = 0; s2 < num_final_segments; ++s2) { - const VP8SegmentInfo* const S2 = &enc->dqm_[s2]; - if (SegmentsAreEquivalent(S1, S2)) { - found = 1; - break; - } - } - map[s1] = s2; - if (!found) { - if (num_final_segments != s1) { - enc->dqm_[num_final_segments] = enc->dqm_[s1]; - } - ++num_final_segments; - } - } - if (num_final_segments < num_segments) { // Remap - int i = enc->mb_w_ * enc->mb_h_; - while (i-- > 0) enc->mb_info_[i].segment_ = map[enc->mb_info_[i].segment_]; - enc->segment_hdr_.num_segments_ = num_final_segments; - // Replicate the trailing segment infos (it's mostly cosmetics) - for (i = num_final_segments; i < num_segments; ++i) { - enc->dqm_[i] = enc->dqm_[num_final_segments - 1]; - } - } -} - -void VP8SetSegmentParams(VP8Encoder* const enc, float quality) { - int i; - int dq_uv_ac, dq_uv_dc; - const int num_segments = enc->segment_hdr_.num_segments_; - const double amp = SNS_TO_DQ * enc->config_->sns_strength / 100. / 128.; - const double Q = quality / 100.; - const double c_base = enc->config_->emulate_jpeg_size ? - QualityToJPEGCompression(Q, enc->alpha_ / 255.) : - QualityToCompression(Q); - for (i = 0; i < num_segments; ++i) { - // We modulate the base coefficient to accommodate for the quantization - // susceptibility and allow denser segments to be quantized more. - const double expn = 1. - amp * enc->dqm_[i].alpha_; - const double c = pow(c_base, expn); - const int q = (int)(127. * (1. - c)); - assert(expn > 0.); - enc->dqm_[i].quant_ = clip(q, 0, 127); - } - - // purely indicative in the bitstream (except for the 1-segment case) - enc->base_quant_ = enc->dqm_[0].quant_; - - // fill-in values for the unused segments (required by the syntax) - for (i = num_segments; i < NUM_MB_SEGMENTS; ++i) { - enc->dqm_[i].quant_ = enc->base_quant_; - } - - // uv_alpha_ is normally spread around ~60. The useful range is - // typically ~30 (quite bad) to ~100 (ok to decimate UV more). - // We map it to the safe maximal range of MAX/MIN_DQ_UV for dq_uv. - dq_uv_ac = (enc->uv_alpha_ - MID_ALPHA) * (MAX_DQ_UV - MIN_DQ_UV) - / (MAX_ALPHA - MIN_ALPHA); - // we rescale by the user-defined strength of adaptation - dq_uv_ac = dq_uv_ac * enc->config_->sns_strength / 100; - // and make it safe. - dq_uv_ac = clip(dq_uv_ac, MIN_DQ_UV, MAX_DQ_UV); - // We also boost the dc-uv-quant a little, based on sns-strength, since - // U/V channels are quite more reactive to high quants (flat DC-blocks - // tend to appear, and are unpleasant). - dq_uv_dc = -4 * enc->config_->sns_strength / 100; - dq_uv_dc = clip(dq_uv_dc, -15, 15); // 4bit-signed max allowed - - enc->dq_y1_dc_ = 0; // TODO(skal): dq-lum - enc->dq_y2_dc_ = 0; - enc->dq_y2_ac_ = 0; - enc->dq_uv_dc_ = dq_uv_dc; - enc->dq_uv_ac_ = dq_uv_ac; - - SetupFilterStrength(enc); // initialize segments' filtering, eventually - - if (num_segments > 1) SimplifySegments(enc); - - SetupMatrices(enc); // finalize quantization matrices -} - -//------------------------------------------------------------------------------ -// Form the predictions in cache - -// Must be ordered using {DC_PRED, TM_PRED, V_PRED, H_PRED} as index -const int VP8I16ModeOffsets[4] = { I16DC16, I16TM16, I16VE16, I16HE16 }; -const int VP8UVModeOffsets[4] = { C8DC8, C8TM8, C8VE8, C8HE8 }; - -// Must be indexed using {B_DC_PRED -> B_HU_PRED} as index -const int VP8I4ModeOffsets[NUM_BMODES] = { - I4DC4, I4TM4, I4VE4, I4HE4, I4RD4, I4VR4, I4LD4, I4VL4, I4HD4, I4HU4 -}; - -void VP8MakeLuma16Preds(const VP8EncIterator* const it) { - const uint8_t* const left = it->x_ ? it->y_left_ : NULL; - const uint8_t* const top = it->y_ ? it->y_top_ : NULL; - VP8EncPredLuma16(it->yuv_p_, left, top); -} - -void VP8MakeChroma8Preds(const VP8EncIterator* const it) { - const uint8_t* const left = it->x_ ? it->u_left_ : NULL; - const uint8_t* const top = it->y_ ? it->uv_top_ : NULL; - VP8EncPredChroma8(it->yuv_p_, left, top); -} - -void VP8MakeIntra4Preds(const VP8EncIterator* const it) { - VP8EncPredLuma4(it->yuv_p_, it->i4_top_); -} - -//------------------------------------------------------------------------------ -// Quantize - -// Layout: -// +----+----+ -// |YYYY|UUVV| 0 -// |YYYY|UUVV| 4 -// |YYYY|....| 8 -// |YYYY|....| 12 -// +----+----+ - -const int VP8Scan[16] = { // Luma - 0 + 0 * BPS, 4 + 0 * BPS, 8 + 0 * BPS, 12 + 0 * BPS, - 0 + 4 * BPS, 4 + 4 * BPS, 8 + 4 * BPS, 12 + 4 * BPS, - 0 + 8 * BPS, 4 + 8 * BPS, 8 + 8 * BPS, 12 + 8 * BPS, - 0 + 12 * BPS, 4 + 12 * BPS, 8 + 12 * BPS, 12 + 12 * BPS, -}; - -static const int VP8ScanUV[4 + 4] = { - 0 + 0 * BPS, 4 + 0 * BPS, 0 + 4 * BPS, 4 + 4 * BPS, // U - 8 + 0 * BPS, 12 + 0 * BPS, 8 + 4 * BPS, 12 + 4 * BPS // V -}; - -//------------------------------------------------------------------------------ -// Distortion measurement - -static const uint16_t kWeightY[16] = { - 38, 32, 20, 9, 32, 28, 17, 7, 20, 17, 10, 4, 9, 7, 4, 2 -}; - -static const uint16_t kWeightTrellis[16] = { -#if USE_TDISTO == 0 - 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16 -#else - 30, 27, 19, 11, - 27, 24, 17, 10, - 19, 17, 12, 8, - 11, 10, 8, 6 -#endif -}; - -// Init/Copy the common fields in score. -static void InitScore(VP8ModeScore* const rd) { - rd->D = 0; - rd->SD = 0; - rd->R = 0; - rd->H = 0; - rd->nz = 0; - rd->score = MAX_COST; -} - -static void CopyScore(VP8ModeScore* const dst, const VP8ModeScore* const src) { - dst->D = src->D; - dst->SD = src->SD; - dst->R = src->R; - dst->H = src->H; - dst->nz = src->nz; // note that nz is not accumulated, but just copied. - dst->score = src->score; -} - -static void AddScore(VP8ModeScore* const dst, const VP8ModeScore* const src) { - dst->D += src->D; - dst->SD += src->SD; - dst->R += src->R; - dst->H += src->H; - dst->nz |= src->nz; // here, new nz bits are accumulated. - dst->score += src->score; -} - -//------------------------------------------------------------------------------ -// Performs trellis-optimized quantization. - -// Trellis node -typedef struct { - int8_t prev; // best previous node - int8_t sign; // sign of coeff_i - int16_t level; // level -} Node; - -// Score state -typedef struct { - score_t score; // partial RD score - const uint16_t* costs; // shortcut to cost tables -} ScoreState; - -// If a coefficient was quantized to a value Q (using a neutral bias), -// we test all alternate possibilities between [Q-MIN_DELTA, Q+MAX_DELTA] -// We don't test negative values though. -#define MIN_DELTA 0 // how much lower level to try -#define MAX_DELTA 1 // how much higher -#define NUM_NODES (MIN_DELTA + 1 + MAX_DELTA) -#define NODE(n, l) (nodes[(n)][(l) + MIN_DELTA]) -#define SCORE_STATE(n, l) (score_states[n][(l) + MIN_DELTA]) - -static WEBP_INLINE void SetRDScore(int lambda, VP8ModeScore* const rd) { - rd->score = (rd->R + rd->H) * lambda + RD_DISTO_MULT * (rd->D + rd->SD); -} - -static WEBP_INLINE score_t RDScoreTrellis(int lambda, score_t rate, - score_t distortion) { - return rate * lambda + RD_DISTO_MULT * distortion; -} - -static int TrellisQuantizeBlock(const VP8Encoder* const enc, - int16_t in[16], int16_t out[16], - int ctx0, int coeff_type, - const VP8Matrix* const mtx, - int lambda) { - const ProbaArray* const probas = enc->proba_.coeffs_[coeff_type]; - CostArrayPtr const costs = - (CostArrayPtr)enc->proba_.remapped_costs_[coeff_type]; - const int first = (coeff_type == 0) ? 1 : 0; - Node nodes[16][NUM_NODES]; - ScoreState score_states[2][NUM_NODES]; - ScoreState* ss_cur = &SCORE_STATE(0, MIN_DELTA); - ScoreState* ss_prev = &SCORE_STATE(1, MIN_DELTA); - int best_path[3] = {-1, -1, -1}; // store best-last/best-level/best-previous - score_t best_score; - int n, m, p, last; - - { - score_t cost; - const int thresh = mtx->q_[1] * mtx->q_[1] / 4; - const int last_proba = probas[VP8EncBands[first]][ctx0][0]; - - // compute the position of the last interesting coefficient - last = first - 1; - for (n = 15; n >= first; --n) { - const int j = kZigzag[n]; - const int err = in[j] * in[j]; - if (err > thresh) { - last = n; - break; - } - } - // we don't need to go inspect up to n = 16 coeffs. We can just go up - // to last + 1 (inclusive) without losing much. - if (last < 15) ++last; - - // compute 'skip' score. This is the max score one can do. - cost = VP8BitCost(0, last_proba); - best_score = RDScoreTrellis(lambda, cost, 0); - - // initialize source node. - for (m = -MIN_DELTA; m <= MAX_DELTA; ++m) { - const score_t rate = (ctx0 == 0) ? VP8BitCost(1, last_proba) : 0; - ss_cur[m].score = RDScoreTrellis(lambda, rate, 0); - ss_cur[m].costs = costs[first][ctx0]; - } - } - - // traverse trellis. - for (n = first; n <= last; ++n) { - const int j = kZigzag[n]; - const uint32_t Q = mtx->q_[j]; - const uint32_t iQ = mtx->iq_[j]; - const uint32_t B = BIAS(0x00); // neutral bias - // note: it's important to take sign of the _original_ coeff, - // so we don't have to consider level < 0 afterward. - const int sign = (in[j] < 0); - const uint32_t coeff0 = (sign ? -in[j] : in[j]) + mtx->sharpen_[j]; - int level0 = QUANTDIV(coeff0, iQ, B); - if (level0 > MAX_LEVEL) level0 = MAX_LEVEL; - - { // Swap current and previous score states - ScoreState* const tmp = ss_cur; - ss_cur = ss_prev; - ss_prev = tmp; - } - - // test all alternate level values around level0. - for (m = -MIN_DELTA; m <= MAX_DELTA; ++m) { - Node* const cur = &NODE(n, m); - int level = level0 + m; - const int ctx = (level > 2) ? 2 : level; - const int band = VP8EncBands[n + 1]; - score_t base_score, last_pos_score; - score_t best_cur_score = MAX_COST; - int best_prev = 0; // default, in case - - ss_cur[m].score = MAX_COST; - ss_cur[m].costs = costs[n + 1][ctx]; - if (level > MAX_LEVEL || level < 0) { // node is dead? - continue; - } - - // Compute extra rate cost if last coeff's position is < 15 - { - const score_t last_pos_cost = - (n < 15) ? VP8BitCost(0, probas[band][ctx][0]) : 0; - last_pos_score = RDScoreTrellis(lambda, last_pos_cost, 0); - } - - { - // Compute delta_error = how much coding this level will - // subtract to max_error as distortion. - // Here, distortion = sum of (|coeff_i| - level_i * Q_i)^2 - const int new_error = coeff0 - level * Q; - const int delta_error = - kWeightTrellis[j] * (new_error * new_error - coeff0 * coeff0); - base_score = RDScoreTrellis(lambda, 0, delta_error); - } - - // Inspect all possible non-dead predecessors. Retain only the best one. - for (p = -MIN_DELTA; p <= MAX_DELTA; ++p) { - // Dead nodes (with ss_prev[p].score >= MAX_COST) are automatically - // eliminated since their score can't be better than the current best. - const score_t cost = VP8LevelCost(ss_prev[p].costs, level); - // Examine node assuming it's a non-terminal one. - const score_t score = - base_score + ss_prev[p].score + RDScoreTrellis(lambda, cost, 0); - if (score < best_cur_score) { - best_cur_score = score; - best_prev = p; - } - } - // Store best finding in current node. - cur->sign = sign; - cur->level = level; - cur->prev = best_prev; - ss_cur[m].score = best_cur_score; - - // Now, record best terminal node (and thus best entry in the graph). - if (level != 0) { - const score_t score = best_cur_score + last_pos_score; - if (score < best_score) { - best_score = score; - best_path[0] = n; // best eob position - best_path[1] = m; // best node index - best_path[2] = best_prev; // best predecessor - } - } - } - } - - // Fresh start - memset(in + first, 0, (16 - first) * sizeof(*in)); - memset(out + first, 0, (16 - first) * sizeof(*out)); - if (best_path[0] == -1) { - return 0; // skip! - } - - { - // Unwind the best path. - // Note: best-prev on terminal node is not necessarily equal to the - // best_prev for non-terminal. So we patch best_path[2] in. - int nz = 0; - int best_node = best_path[1]; - n = best_path[0]; - NODE(n, best_node).prev = best_path[2]; // force best-prev for terminal - - for (; n >= first; --n) { - const Node* const node = &NODE(n, best_node); - const int j = kZigzag[n]; - out[n] = node->sign ? -node->level : node->level; - nz |= node->level; - in[j] = out[n] * mtx->q_[j]; - best_node = node->prev; - } - return (nz != 0); - } -} - -#undef NODE - -//------------------------------------------------------------------------------ -// Performs: difference, transform, quantize, back-transform, add -// all at once. Output is the reconstructed block in *yuv_out, and the -// quantized levels in *levels. - -static int ReconstructIntra16(VP8EncIterator* const it, - VP8ModeScore* const rd, - uint8_t* const yuv_out, - int mode) { - const VP8Encoder* const enc = it->enc_; - const uint8_t* const ref = it->yuv_p_ + VP8I16ModeOffsets[mode]; - const uint8_t* const src = it->yuv_in_ + Y_OFF_ENC; - const VP8SegmentInfo* const dqm = &enc->dqm_[it->mb_->segment_]; - int nz = 0; - int n; - int16_t tmp[16][16], dc_tmp[16]; - - for (n = 0; n < 16; n += 2) { - VP8FTransform2(src + VP8Scan[n], ref + VP8Scan[n], tmp[n]); - } - VP8FTransformWHT(tmp[0], dc_tmp); - nz |= VP8EncQuantizeBlockWHT(dc_tmp, rd->y_dc_levels, &dqm->y2_) << 24; - - if (DO_TRELLIS_I16 && it->do_trellis_) { - int x, y; - VP8IteratorNzToBytes(it); - for (y = 0, n = 0; y < 4; ++y) { - for (x = 0; x < 4; ++x, ++n) { - const int ctx = it->top_nz_[x] + it->left_nz_[y]; - const int non_zero = - TrellisQuantizeBlock(enc, tmp[n], rd->y_ac_levels[n], ctx, 0, - &dqm->y1_, dqm->lambda_trellis_i16_); - it->top_nz_[x] = it->left_nz_[y] = non_zero; - rd->y_ac_levels[n][0] = 0; - nz |= non_zero << n; - } - } - } else { - for (n = 0; n < 16; n += 2) { - // Zero-out the first coeff, so that: a) nz is correct below, and - // b) finding 'last' non-zero coeffs in SetResidualCoeffs() is simplified. - tmp[n][0] = tmp[n + 1][0] = 0; - nz |= VP8EncQuantize2Blocks(tmp[n], rd->y_ac_levels[n], &dqm->y1_) << n; - assert(rd->y_ac_levels[n + 0][0] == 0); - assert(rd->y_ac_levels[n + 1][0] == 0); - } - } - - // Transform back - VP8TransformWHT(dc_tmp, tmp[0]); - for (n = 0; n < 16; n += 2) { - VP8ITransform(ref + VP8Scan[n], tmp[n], yuv_out + VP8Scan[n], 1); - } - - return nz; -} - -static int ReconstructIntra4(VP8EncIterator* const it, - int16_t levels[16], - const uint8_t* const src, - uint8_t* const yuv_out, - int mode) { - const VP8Encoder* const enc = it->enc_; - const uint8_t* const ref = it->yuv_p_ + VP8I4ModeOffsets[mode]; - const VP8SegmentInfo* const dqm = &enc->dqm_[it->mb_->segment_]; - int nz = 0; - int16_t tmp[16]; - - VP8FTransform(src, ref, tmp); - if (DO_TRELLIS_I4 && it->do_trellis_) { - const int x = it->i4_ & 3, y = it->i4_ >> 2; - const int ctx = it->top_nz_[x] + it->left_nz_[y]; - nz = TrellisQuantizeBlock(enc, tmp, levels, ctx, 3, &dqm->y1_, - dqm->lambda_trellis_i4_); - } else { - nz = VP8EncQuantizeBlock(tmp, levels, &dqm->y1_); - } - VP8ITransform(ref, tmp, yuv_out, 0); - return nz; -} - -static int ReconstructUV(VP8EncIterator* const it, VP8ModeScore* const rd, - uint8_t* const yuv_out, int mode) { - const VP8Encoder* const enc = it->enc_; - const uint8_t* const ref = it->yuv_p_ + VP8UVModeOffsets[mode]; - const uint8_t* const src = it->yuv_in_ + U_OFF_ENC; - const VP8SegmentInfo* const dqm = &enc->dqm_[it->mb_->segment_]; - int nz = 0; - int n; - int16_t tmp[8][16]; - - for (n = 0; n < 8; n += 2) { - VP8FTransform2(src + VP8ScanUV[n], ref + VP8ScanUV[n], tmp[n]); - } - if (DO_TRELLIS_UV && it->do_trellis_) { - int ch, x, y; - for (ch = 0, n = 0; ch <= 2; ch += 2) { - for (y = 0; y < 2; ++y) { - for (x = 0; x < 2; ++x, ++n) { - const int ctx = it->top_nz_[4 + ch + x] + it->left_nz_[4 + ch + y]; - const int non_zero = - TrellisQuantizeBlock(enc, tmp[n], rd->uv_levels[n], ctx, 2, - &dqm->uv_, dqm->lambda_trellis_uv_); - it->top_nz_[4 + ch + x] = it->left_nz_[4 + ch + y] = non_zero; - nz |= non_zero << n; - } - } - } - } else { - for (n = 0; n < 8; n += 2) { - nz |= VP8EncQuantize2Blocks(tmp[n], rd->uv_levels[n], &dqm->uv_) << n; - } - } - - for (n = 0; n < 8; n += 2) { - VP8ITransform(ref + VP8ScanUV[n], tmp[n], yuv_out + VP8ScanUV[n], 1); - } - return (nz << 16); -} - -//------------------------------------------------------------------------------ -// RD-opt decision. Reconstruct each modes, evalue distortion and bit-cost. -// Pick the mode is lower RD-cost = Rate + lambda * Distortion. - -static void StoreMaxDelta(VP8SegmentInfo* const dqm, const int16_t DCs[16]) { - // We look at the first three AC coefficients to determine what is the average - // delta between each sub-4x4 block. - const int v0 = abs(DCs[1]); - const int v1 = abs(DCs[2]); - const int v2 = abs(DCs[4]); - int max_v = (v1 > v0) ? v1 : v0; - max_v = (v2 > max_v) ? v2 : max_v; - if (max_v > dqm->max_edge_) dqm->max_edge_ = max_v; -} - -static void SwapModeScore(VP8ModeScore** a, VP8ModeScore** b) { - VP8ModeScore* const tmp = *a; - *a = *b; - *b = tmp; -} - -static void SwapPtr(uint8_t** a, uint8_t** b) { - uint8_t* const tmp = *a; - *a = *b; - *b = tmp; -} - -static void SwapOut(VP8EncIterator* const it) { - SwapPtr(&it->yuv_out_, &it->yuv_out2_); -} - -static score_t IsFlat(const int16_t* levels, int num_blocks, score_t thresh) { - score_t score = 0; - while (num_blocks-- > 0) { // TODO(skal): refine positional scoring? - int i; - for (i = 1; i < 16; ++i) { // omit DC, we're only interested in AC - score += (levels[i] != 0); - if (score > thresh) return 0; - } - levels += 16; - } - return 1; -} - -static void PickBestIntra16(VP8EncIterator* const it, VP8ModeScore* rd) { - const int kNumBlocks = 16; - VP8SegmentInfo* const dqm = &it->enc_->dqm_[it->mb_->segment_]; - const int lambda = dqm->lambda_i16_; - const int tlambda = dqm->tlambda_; - const uint8_t* const src = it->yuv_in_ + Y_OFF_ENC; - VP8ModeScore rd_tmp; - VP8ModeScore* rd_cur = &rd_tmp; - VP8ModeScore* rd_best = rd; - int mode; - - rd->mode_i16 = -1; - for (mode = 0; mode < NUM_PRED_MODES; ++mode) { - uint8_t* const tmp_dst = it->yuv_out2_ + Y_OFF_ENC; // scratch buffer - rd_cur->mode_i16 = mode; - - // Reconstruct - rd_cur->nz = ReconstructIntra16(it, rd_cur, tmp_dst, mode); - - // Measure RD-score - rd_cur->D = VP8SSE16x16(src, tmp_dst); - rd_cur->SD = - tlambda ? MULT_8B(tlambda, VP8TDisto16x16(src, tmp_dst, kWeightY)) : 0; - rd_cur->H = VP8FixedCostsI16[mode]; - rd_cur->R = VP8GetCostLuma16(it, rd_cur); - if (mode > 0 && - IsFlat(rd_cur->y_ac_levels[0], kNumBlocks, FLATNESS_LIMIT_I16)) { - // penalty to avoid flat area to be mispredicted by complex mode - rd_cur->R += FLATNESS_PENALTY * kNumBlocks; - } - - // Since we always examine Intra16 first, we can overwrite *rd directly. - SetRDScore(lambda, rd_cur); - if (mode == 0 || rd_cur->score < rd_best->score) { - SwapModeScore(&rd_cur, &rd_best); - SwapOut(it); - } - } - if (rd_best != rd) { - memcpy(rd, rd_best, sizeof(*rd)); - } - SetRDScore(dqm->lambda_mode_, rd); // finalize score for mode decision. - VP8SetIntra16Mode(it, rd->mode_i16); - - // we have a blocky macroblock (only DCs are non-zero) with fairly high - // distortion, record max delta so we can later adjust the minimal filtering - // strength needed to smooth these blocks out. - if ((rd->nz & 0x100ffff) == 0x1000000 && rd->D > dqm->min_disto_) { - StoreMaxDelta(dqm, rd->y_dc_levels); - } -} - -//------------------------------------------------------------------------------ - -// return the cost array corresponding to the surrounding prediction modes. -static const uint16_t* GetCostModeI4(VP8EncIterator* const it, - const uint8_t modes[16]) { - const int preds_w = it->enc_->preds_w_; - const int x = (it->i4_ & 3), y = it->i4_ >> 2; - const int left = (x == 0) ? it->preds_[y * preds_w - 1] : modes[it->i4_ - 1]; - const int top = (y == 0) ? it->preds_[-preds_w + x] : modes[it->i4_ - 4]; - return VP8FixedCostsI4[top][left]; -} - -static int PickBestIntra4(VP8EncIterator* const it, VP8ModeScore* const rd) { - const VP8Encoder* const enc = it->enc_; - const VP8SegmentInfo* const dqm = &enc->dqm_[it->mb_->segment_]; - const int lambda = dqm->lambda_i4_; - const int tlambda = dqm->tlambda_; - const uint8_t* const src0 = it->yuv_in_ + Y_OFF_ENC; - uint8_t* const best_blocks = it->yuv_out2_ + Y_OFF_ENC; - int total_header_bits = 0; - VP8ModeScore rd_best; - - if (enc->max_i4_header_bits_ == 0) { - return 0; - } - - InitScore(&rd_best); - rd_best.H = 211; // '211' is the value of VP8BitCost(0, 145) - SetRDScore(dqm->lambda_mode_, &rd_best); - VP8IteratorStartI4(it); - do { - const int kNumBlocks = 1; - VP8ModeScore rd_i4; - int mode; - int best_mode = -1; - const uint8_t* const src = src0 + VP8Scan[it->i4_]; - const uint16_t* const mode_costs = GetCostModeI4(it, rd->modes_i4); - uint8_t* best_block = best_blocks + VP8Scan[it->i4_]; - uint8_t* tmp_dst = it->yuv_p_ + I4TMP; // scratch buffer. - - InitScore(&rd_i4); - VP8MakeIntra4Preds(it); - for (mode = 0; mode < NUM_BMODES; ++mode) { - VP8ModeScore rd_tmp; - int16_t tmp_levels[16]; - - // Reconstruct - rd_tmp.nz = - ReconstructIntra4(it, tmp_levels, src, tmp_dst, mode) << it->i4_; - - // Compute RD-score - rd_tmp.D = VP8SSE4x4(src, tmp_dst); - rd_tmp.SD = - tlambda ? MULT_8B(tlambda, VP8TDisto4x4(src, tmp_dst, kWeightY)) - : 0; - rd_tmp.H = mode_costs[mode]; - - // Add flatness penalty - if (mode > 0 && IsFlat(tmp_levels, kNumBlocks, FLATNESS_LIMIT_I4)) { - rd_tmp.R = FLATNESS_PENALTY * kNumBlocks; - } else { - rd_tmp.R = 0; - } - - // early-out check - SetRDScore(lambda, &rd_tmp); - if (best_mode >= 0 && rd_tmp.score >= rd_i4.score) continue; - - // finish computing score - rd_tmp.R += VP8GetCostLuma4(it, tmp_levels); - SetRDScore(lambda, &rd_tmp); - - if (best_mode < 0 || rd_tmp.score < rd_i4.score) { - CopyScore(&rd_i4, &rd_tmp); - best_mode = mode; - SwapPtr(&tmp_dst, &best_block); - memcpy(rd_best.y_ac_levels[it->i4_], tmp_levels, - sizeof(rd_best.y_ac_levels[it->i4_])); - } - } - SetRDScore(dqm->lambda_mode_, &rd_i4); - AddScore(&rd_best, &rd_i4); - if (rd_best.score >= rd->score) { - return 0; - } - total_header_bits += (int)rd_i4.H; // <- equal to mode_costs[best_mode]; - if (total_header_bits > enc->max_i4_header_bits_) { - return 0; - } - // Copy selected samples if not in the right place already. - if (best_block != best_blocks + VP8Scan[it->i4_]) { - VP8Copy4x4(best_block, best_blocks + VP8Scan[it->i4_]); - } - rd->modes_i4[it->i4_] = best_mode; - it->top_nz_[it->i4_ & 3] = it->left_nz_[it->i4_ >> 2] = (rd_i4.nz ? 1 : 0); - } while (VP8IteratorRotateI4(it, best_blocks)); - - // finalize state - CopyScore(rd, &rd_best); - VP8SetIntra4Mode(it, rd->modes_i4); - SwapOut(it); - memcpy(rd->y_ac_levels, rd_best.y_ac_levels, sizeof(rd->y_ac_levels)); - return 1; // select intra4x4 over intra16x16 -} - -//------------------------------------------------------------------------------ - -static void PickBestUV(VP8EncIterator* const it, VP8ModeScore* const rd) { - const int kNumBlocks = 8; - const VP8SegmentInfo* const dqm = &it->enc_->dqm_[it->mb_->segment_]; - const int lambda = dqm->lambda_uv_; - const uint8_t* const src = it->yuv_in_ + U_OFF_ENC; - uint8_t* tmp_dst = it->yuv_out2_ + U_OFF_ENC; // scratch buffer - uint8_t* dst0 = it->yuv_out_ + U_OFF_ENC; - uint8_t* dst = dst0; - VP8ModeScore rd_best; - int mode; - - rd->mode_uv = -1; - InitScore(&rd_best); - for (mode = 0; mode < NUM_PRED_MODES; ++mode) { - VP8ModeScore rd_uv; - - // Reconstruct - rd_uv.nz = ReconstructUV(it, &rd_uv, tmp_dst, mode); - - // Compute RD-score - rd_uv.D = VP8SSE16x8(src, tmp_dst); - rd_uv.SD = 0; // not calling TDisto here: it tends to flatten areas. - rd_uv.H = VP8FixedCostsUV[mode]; - rd_uv.R = VP8GetCostUV(it, &rd_uv); - if (mode > 0 && IsFlat(rd_uv.uv_levels[0], kNumBlocks, FLATNESS_LIMIT_UV)) { - rd_uv.R += FLATNESS_PENALTY * kNumBlocks; - } - - SetRDScore(lambda, &rd_uv); - if (mode == 0 || rd_uv.score < rd_best.score) { - CopyScore(&rd_best, &rd_uv); - rd->mode_uv = mode; - memcpy(rd->uv_levels, rd_uv.uv_levels, sizeof(rd->uv_levels)); - SwapPtr(&dst, &tmp_dst); - } - } - VP8SetIntraUVMode(it, rd->mode_uv); - AddScore(rd, &rd_best); - if (dst != dst0) { // copy 16x8 block if needed - VP8Copy16x8(dst, dst0); - } -} - -//------------------------------------------------------------------------------ -// Final reconstruction and quantization. - -static void SimpleQuantize(VP8EncIterator* const it, VP8ModeScore* const rd) { - const VP8Encoder* const enc = it->enc_; - const int is_i16 = (it->mb_->type_ == 1); - int nz = 0; - - if (is_i16) { - nz = ReconstructIntra16(it, rd, it->yuv_out_ + Y_OFF_ENC, it->preds_[0]); - } else { - VP8IteratorStartI4(it); - do { - const int mode = - it->preds_[(it->i4_ & 3) + (it->i4_ >> 2) * enc->preds_w_]; - const uint8_t* const src = it->yuv_in_ + Y_OFF_ENC + VP8Scan[it->i4_]; - uint8_t* const dst = it->yuv_out_ + Y_OFF_ENC + VP8Scan[it->i4_]; - VP8MakeIntra4Preds(it); - nz |= ReconstructIntra4(it, rd->y_ac_levels[it->i4_], - src, dst, mode) << it->i4_; - } while (VP8IteratorRotateI4(it, it->yuv_out_ + Y_OFF_ENC)); - } - - nz |= ReconstructUV(it, rd, it->yuv_out_ + U_OFF_ENC, it->mb_->uv_mode_); - rd->nz = nz; -} - -// Refine intra16/intra4 sub-modes based on distortion only (not rate). -static void RefineUsingDistortion(VP8EncIterator* const it, - int try_both_modes, int refine_uv_mode, - VP8ModeScore* const rd) { - score_t best_score = MAX_COST; - int nz = 0; - int mode; - int is_i16 = try_both_modes || (it->mb_->type_ == 1); - - const VP8SegmentInfo* const dqm = &it->enc_->dqm_[it->mb_->segment_]; - // Some empiric constants, of approximate order of magnitude. - const int lambda_d_i16 = 106; - const int lambda_d_i4 = 11; - const int lambda_d_uv = 120; - score_t score_i4 = dqm->i4_penalty_; - score_t i4_bit_sum = 0; - const score_t bit_limit = try_both_modes ? it->enc_->mb_header_limit_ - : MAX_COST; // no early-out allowed - - if (is_i16) { // First, evaluate Intra16 distortion - int best_mode = -1; - const uint8_t* const src = it->yuv_in_ + Y_OFF_ENC; - for (mode = 0; mode < NUM_PRED_MODES; ++mode) { - const uint8_t* const ref = it->yuv_p_ + VP8I16ModeOffsets[mode]; - const score_t score = VP8SSE16x16(src, ref) * RD_DISTO_MULT - + VP8FixedCostsI16[mode] * lambda_d_i16; - if (mode > 0 && VP8FixedCostsI16[mode] > bit_limit) { - continue; - } - if (score < best_score) { - best_mode = mode; - best_score = score; - } - } - VP8SetIntra16Mode(it, best_mode); - // we'll reconstruct later, if i16 mode actually gets selected - } - - // Next, evaluate Intra4 - if (try_both_modes || !is_i16) { - // We don't evaluate the rate here, but just account for it through a - // constant penalty (i4 mode usually needs more bits compared to i16). - is_i16 = 0; - VP8IteratorStartI4(it); - do { - int best_i4_mode = -1; - score_t best_i4_score = MAX_COST; - const uint8_t* const src = it->yuv_in_ + Y_OFF_ENC + VP8Scan[it->i4_]; - const uint16_t* const mode_costs = GetCostModeI4(it, rd->modes_i4); - - VP8MakeIntra4Preds(it); - for (mode = 0; mode < NUM_BMODES; ++mode) { - const uint8_t* const ref = it->yuv_p_ + VP8I4ModeOffsets[mode]; - const score_t score = VP8SSE4x4(src, ref) * RD_DISTO_MULT - + mode_costs[mode] * lambda_d_i4; - if (score < best_i4_score) { - best_i4_mode = mode; - best_i4_score = score; - } - } - i4_bit_sum += mode_costs[best_i4_mode]; - rd->modes_i4[it->i4_] = best_i4_mode; - score_i4 += best_i4_score; - if (score_i4 >= best_score || i4_bit_sum > bit_limit) { - // Intra4 won't be better than Intra16. Bail out and pick Intra16. - is_i16 = 1; - break; - } else { // reconstruct partial block inside yuv_out2_ buffer - uint8_t* const tmp_dst = it->yuv_out2_ + Y_OFF_ENC + VP8Scan[it->i4_]; - nz |= ReconstructIntra4(it, rd->y_ac_levels[it->i4_], - src, tmp_dst, best_i4_mode) << it->i4_; - } - } while (VP8IteratorRotateI4(it, it->yuv_out2_ + Y_OFF_ENC)); - } - - // Final reconstruction, depending on which mode is selected. - if (!is_i16) { - VP8SetIntra4Mode(it, rd->modes_i4); - SwapOut(it); - best_score = score_i4; - } else { - nz = ReconstructIntra16(it, rd, it->yuv_out_ + Y_OFF_ENC, it->preds_[0]); - } - - // ... and UV! - if (refine_uv_mode) { - int best_mode = -1; - score_t best_uv_score = MAX_COST; - const uint8_t* const src = it->yuv_in_ + U_OFF_ENC; - for (mode = 0; mode < NUM_PRED_MODES; ++mode) { - const uint8_t* const ref = it->yuv_p_ + VP8UVModeOffsets[mode]; - const score_t score = VP8SSE16x8(src, ref) * RD_DISTO_MULT - + VP8FixedCostsUV[mode] * lambda_d_uv; - if (score < best_uv_score) { - best_mode = mode; - best_uv_score = score; - } - } - VP8SetIntraUVMode(it, best_mode); - } - nz |= ReconstructUV(it, rd, it->yuv_out_ + U_OFF_ENC, it->mb_->uv_mode_); - - rd->nz = nz; - rd->score = best_score; -} - -//------------------------------------------------------------------------------ -// Entry point - -int VP8Decimate(VP8EncIterator* const it, VP8ModeScore* const rd, - VP8RDLevel rd_opt) { - int is_skipped; - const int method = it->enc_->method_; - - InitScore(rd); - - // We can perform predictions for Luma16x16 and Chroma8x8 already. - // Luma4x4 predictions needs to be done as-we-go. - VP8MakeLuma16Preds(it); - VP8MakeChroma8Preds(it); - - if (rd_opt > RD_OPT_NONE) { - it->do_trellis_ = (rd_opt >= RD_OPT_TRELLIS_ALL); - PickBestIntra16(it, rd); - if (method >= 2) { - PickBestIntra4(it, rd); - } - PickBestUV(it, rd); - if (rd_opt == RD_OPT_TRELLIS) { // finish off with trellis-optim now - it->do_trellis_ = 1; - SimpleQuantize(it, rd); - } - } else { - // At this point we have heuristically decided intra16 / intra4. - // For method >= 2, pick the best intra4/intra16 based on SSE (~tad slower). - // For method <= 1, we don't re-examine the decision but just go ahead with - // quantization/reconstruction. - RefineUsingDistortion(it, (method >= 2), (method >= 1), rd); - } - is_skipped = (rd->nz == 0); - VP8SetSkip(it, is_skipped); - return is_skipped; -} diff --git a/thirdparty/libwebp/enc/quant_enc.c b/thirdparty/libwebp/enc/quant_enc.c new file mode 100644 index 0000000000..b118fb2a13 --- /dev/null +++ b/thirdparty/libwebp/enc/quant_enc.c @@ -0,0 +1,1283 @@ +// Copyright 2011 Google Inc. All Rights Reserved. +// +// Use of this source code is governed by a BSD-style license +// that can be found in the COPYING file in the root of the source +// tree. An additional intellectual property rights grant can be found +// in the file PATENTS. All contributing project authors may +// be found in the AUTHORS file in the root of the source tree. +// ----------------------------------------------------------------------------- +// +// Quantization +// +// Author: Skal (pascal.massimino@gmail.com) + +#include +#include +#include // for abs() + +#include "./vp8i_enc.h" +#include "./cost_enc.h" + +#define DO_TRELLIS_I4 1 +#define DO_TRELLIS_I16 1 // not a huge gain, but ok at low bitrate. +#define DO_TRELLIS_UV 0 // disable trellis for UV. Risky. Not worth. +#define USE_TDISTO 1 + +#define MID_ALPHA 64 // neutral value for susceptibility +#define MIN_ALPHA 30 // lowest usable value for susceptibility +#define MAX_ALPHA 100 // higher meaningful value for susceptibility + +#define SNS_TO_DQ 0.9 // Scaling constant between the sns value and the QP + // power-law modulation. Must be strictly less than 1. + +// number of non-zero coeffs below which we consider the block very flat +// (and apply a penalty to complex predictions) +#define FLATNESS_LIMIT_I16 10 // I16 mode +#define FLATNESS_LIMIT_I4 3 // I4 mode +#define FLATNESS_LIMIT_UV 2 // UV mode +#define FLATNESS_PENALTY 140 // roughly ~1bit per block + +#define MULT_8B(a, b) (((a) * (b) + 128) >> 8) + +#define RD_DISTO_MULT 256 // distortion multiplier (equivalent of lambda) + +// #define DEBUG_BLOCK + +//------------------------------------------------------------------------------ + +#if defined(DEBUG_BLOCK) + +#include +#include + +static void PrintBlockInfo(const VP8EncIterator* const it, + const VP8ModeScore* const rd) { + int i, j; + const int is_i16 = (it->mb_->type_ == 1); + const uint8_t* const y_in = it->yuv_in_ + Y_OFF_ENC; + const uint8_t* const y_out = it->yuv_out_ + Y_OFF_ENC; + const uint8_t* const uv_in = it->yuv_in_ + U_OFF_ENC; + const uint8_t* const uv_out = it->yuv_out_ + U_OFF_ENC; + printf("SOURCE / OUTPUT / ABS DELTA\n"); + for (j = 0; j < 16; ++j) { + for (i = 0; i < 16; ++i) printf("%3d ", y_in[i + j * BPS]); + printf(" "); + for (i = 0; i < 16; ++i) printf("%3d ", y_out[i + j * BPS]); + printf(" "); + for (i = 0; i < 16; ++i) { + printf("%1d ", abs(y_in[i + j * BPS] - y_out[i + j * BPS])); + } + printf("\n"); + } + printf("\n"); // newline before the U/V block + for (j = 0; j < 8; ++j) { + for (i = 0; i < 8; ++i) printf("%3d ", uv_in[i + j * BPS]); + printf(" "); + for (i = 8; i < 16; ++i) printf("%3d ", uv_in[i + j * BPS]); + printf(" "); + for (i = 0; i < 8; ++i) printf("%3d ", uv_out[i + j * BPS]); + printf(" "); + for (i = 8; i < 16; ++i) printf("%3d ", uv_out[i + j * BPS]); + printf(" "); + for (i = 0; i < 8; ++i) { + printf("%1d ", abs(uv_out[i + j * BPS] - uv_in[i + j * BPS])); + } + printf(" "); + for (i = 8; i < 16; ++i) { + printf("%1d ", abs(uv_out[i + j * BPS] - uv_in[i + j * BPS])); + } + printf("\n"); + } + printf("\nD:%d SD:%d R:%d H:%d nz:0x%x score:%d\n", + (int)rd->D, (int)rd->SD, (int)rd->R, (int)rd->H, (int)rd->nz, + (int)rd->score); + if (is_i16) { + printf("Mode: %d\n", rd->mode_i16); + printf("y_dc_levels:"); + for (i = 0; i < 16; ++i) printf("%3d ", rd->y_dc_levels[i]); + printf("\n"); + } else { + printf("Modes[16]: "); + for (i = 0; i < 16; ++i) printf("%d ", rd->modes_i4[i]); + printf("\n"); + } + printf("y_ac_levels:\n"); + for (j = 0; j < 16; ++j) { + for (i = is_i16 ? 1 : 0; i < 16; ++i) { + printf("%4d ", rd->y_ac_levels[j][i]); + } + printf("\n"); + } + printf("\n"); + printf("uv_levels (mode=%d):\n", rd->mode_uv); + for (j = 0; j < 8; ++j) { + for (i = 0; i < 16; ++i) { + printf("%4d ", rd->uv_levels[j][i]); + } + printf("\n"); + } +} + +#endif // DEBUG_BLOCK + +//------------------------------------------------------------------------------ + +static WEBP_INLINE int clip(int v, int m, int M) { + return v < m ? m : v > M ? M : v; +} + +static const uint8_t kZigzag[16] = { + 0, 1, 4, 8, 5, 2, 3, 6, 9, 12, 13, 10, 7, 11, 14, 15 +}; + +static const uint8_t kDcTable[128] = { + 4, 5, 6, 7, 8, 9, 10, 10, + 11, 12, 13, 14, 15, 16, 17, 17, + 18, 19, 20, 20, 21, 21, 22, 22, + 23, 23, 24, 25, 25, 26, 27, 28, + 29, 30, 31, 32, 33, 34, 35, 36, + 37, 37, 38, 39, 40, 41, 42, 43, + 44, 45, 46, 46, 47, 48, 49, 50, + 51, 52, 53, 54, 55, 56, 57, 58, + 59, 60, 61, 62, 63, 64, 65, 66, + 67, 68, 69, 70, 71, 72, 73, 74, + 75, 76, 76, 77, 78, 79, 80, 81, + 82, 83, 84, 85, 86, 87, 88, 89, + 91, 93, 95, 96, 98, 100, 101, 102, + 104, 106, 108, 110, 112, 114, 116, 118, + 122, 124, 126, 128, 130, 132, 134, 136, + 138, 140, 143, 145, 148, 151, 154, 157 +}; + +static const uint16_t kAcTable[128] = { + 4, 5, 6, 7, 8, 9, 10, 11, + 12, 13, 14, 15, 16, 17, 18, 19, + 20, 21, 22, 23, 24, 25, 26, 27, + 28, 29, 30, 31, 32, 33, 34, 35, + 36, 37, 38, 39, 40, 41, 42, 43, + 44, 45, 46, 47, 48, 49, 50, 51, + 52, 53, 54, 55, 56, 57, 58, 60, + 62, 64, 66, 68, 70, 72, 74, 76, + 78, 80, 82, 84, 86, 88, 90, 92, + 94, 96, 98, 100, 102, 104, 106, 108, + 110, 112, 114, 116, 119, 122, 125, 128, + 131, 134, 137, 140, 143, 146, 149, 152, + 155, 158, 161, 164, 167, 170, 173, 177, + 181, 185, 189, 193, 197, 201, 205, 209, + 213, 217, 221, 225, 229, 234, 239, 245, + 249, 254, 259, 264, 269, 274, 279, 284 +}; + +static const uint16_t kAcTable2[128] = { + 8, 8, 9, 10, 12, 13, 15, 17, + 18, 20, 21, 23, 24, 26, 27, 29, + 31, 32, 34, 35, 37, 38, 40, 41, + 43, 44, 46, 48, 49, 51, 52, 54, + 55, 57, 58, 60, 62, 63, 65, 66, + 68, 69, 71, 72, 74, 75, 77, 79, + 80, 82, 83, 85, 86, 88, 89, 93, + 96, 99, 102, 105, 108, 111, 114, 117, + 120, 124, 127, 130, 133, 136, 139, 142, + 145, 148, 151, 155, 158, 161, 164, 167, + 170, 173, 176, 179, 184, 189, 193, 198, + 203, 207, 212, 217, 221, 226, 230, 235, + 240, 244, 249, 254, 258, 263, 268, 274, + 280, 286, 292, 299, 305, 311, 317, 323, + 330, 336, 342, 348, 354, 362, 370, 379, + 385, 393, 401, 409, 416, 424, 432, 440 +}; + +static const uint8_t kBiasMatrices[3][2] = { // [luma-ac,luma-dc,chroma][dc,ac] + { 96, 110 }, { 96, 108 }, { 110, 115 } +}; + +// Sharpening by (slightly) raising the hi-frequency coeffs. +// Hack-ish but helpful for mid-bitrate range. Use with care. +#define SHARPEN_BITS 11 // number of descaling bits for sharpening bias +static const uint8_t kFreqSharpening[16] = { + 0, 30, 60, 90, + 30, 60, 90, 90, + 60, 90, 90, 90, + 90, 90, 90, 90 +}; + +//------------------------------------------------------------------------------ +// Initialize quantization parameters in VP8Matrix + +// Returns the average quantizer +static int ExpandMatrix(VP8Matrix* const m, int type) { + int i, sum; + for (i = 0; i < 2; ++i) { + const int is_ac_coeff = (i > 0); + const int bias = kBiasMatrices[type][is_ac_coeff]; + m->iq_[i] = (1 << QFIX) / m->q_[i]; + m->bias_[i] = BIAS(bias); + // zthresh_ is the exact value such that QUANTDIV(coeff, iQ, B) is: + // * zero if coeff <= zthresh + // * non-zero if coeff > zthresh + m->zthresh_[i] = ((1 << QFIX) - 1 - m->bias_[i]) / m->iq_[i]; + } + for (i = 2; i < 16; ++i) { + m->q_[i] = m->q_[1]; + m->iq_[i] = m->iq_[1]; + m->bias_[i] = m->bias_[1]; + m->zthresh_[i] = m->zthresh_[1]; + } + for (sum = 0, i = 0; i < 16; ++i) { + if (type == 0) { // we only use sharpening for AC luma coeffs + m->sharpen_[i] = (kFreqSharpening[i] * m->q_[i]) >> SHARPEN_BITS; + } else { + m->sharpen_[i] = 0; + } + sum += m->q_[i]; + } + return (sum + 8) >> 4; +} + +static void CheckLambdaValue(int* const v) { if (*v < 1) *v = 1; } + +static void SetupMatrices(VP8Encoder* enc) { + int i; + const int tlambda_scale = + (enc->method_ >= 4) ? enc->config_->sns_strength + : 0; + const int num_segments = enc->segment_hdr_.num_segments_; + for (i = 0; i < num_segments; ++i) { + VP8SegmentInfo* const m = &enc->dqm_[i]; + const int q = m->quant_; + int q_i4, q_i16, q_uv; + m->y1_.q_[0] = kDcTable[clip(q + enc->dq_y1_dc_, 0, 127)]; + m->y1_.q_[1] = kAcTable[clip(q, 0, 127)]; + + m->y2_.q_[0] = kDcTable[ clip(q + enc->dq_y2_dc_, 0, 127)] * 2; + m->y2_.q_[1] = kAcTable2[clip(q + enc->dq_y2_ac_, 0, 127)]; + + m->uv_.q_[0] = kDcTable[clip(q + enc->dq_uv_dc_, 0, 117)]; + m->uv_.q_[1] = kAcTable[clip(q + enc->dq_uv_ac_, 0, 127)]; + + q_i4 = ExpandMatrix(&m->y1_, 0); + q_i16 = ExpandMatrix(&m->y2_, 1); + q_uv = ExpandMatrix(&m->uv_, 2); + + m->lambda_i4_ = (3 * q_i4 * q_i4) >> 7; + m->lambda_i16_ = (3 * q_i16 * q_i16); + m->lambda_uv_ = (3 * q_uv * q_uv) >> 6; + m->lambda_mode_ = (1 * q_i4 * q_i4) >> 7; + m->lambda_trellis_i4_ = (7 * q_i4 * q_i4) >> 3; + m->lambda_trellis_i16_ = (q_i16 * q_i16) >> 2; + m->lambda_trellis_uv_ = (q_uv * q_uv) << 1; + m->tlambda_ = (tlambda_scale * q_i4) >> 5; + + // none of these constants should be < 1 + CheckLambdaValue(&m->lambda_i4_); + CheckLambdaValue(&m->lambda_i16_); + CheckLambdaValue(&m->lambda_uv_); + CheckLambdaValue(&m->lambda_mode_); + CheckLambdaValue(&m->lambda_trellis_i4_); + CheckLambdaValue(&m->lambda_trellis_i16_); + CheckLambdaValue(&m->lambda_trellis_uv_); + CheckLambdaValue(&m->tlambda_); + + m->min_disto_ = 20 * m->y1_.q_[0]; // quantization-aware min disto + m->max_edge_ = 0; + + m->i4_penalty_ = 1000 * q_i4 * q_i4; + } +} + +//------------------------------------------------------------------------------ +// Initialize filtering parameters + +// Very small filter-strength values have close to no visual effect. So we can +// save a little decoding-CPU by turning filtering off for these. +#define FSTRENGTH_CUTOFF 2 + +static void SetupFilterStrength(VP8Encoder* const enc) { + int i; + // level0 is in [0..500]. Using '-f 50' as filter_strength is mid-filtering. + const int level0 = 5 * enc->config_->filter_strength; + for (i = 0; i < NUM_MB_SEGMENTS; ++i) { + VP8SegmentInfo* const m = &enc->dqm_[i]; + // We focus on the quantization of AC coeffs. + const int qstep = kAcTable[clip(m->quant_, 0, 127)] >> 2; + const int base_strength = + VP8FilterStrengthFromDelta(enc->filter_hdr_.sharpness_, qstep); + // Segments with lower complexity ('beta') will be less filtered. + const int f = base_strength * level0 / (256 + m->beta_); + m->fstrength_ = (f < FSTRENGTH_CUTOFF) ? 0 : (f > 63) ? 63 : f; + } + // We record the initial strength (mainly for the case of 1-segment only). + enc->filter_hdr_.level_ = enc->dqm_[0].fstrength_; + enc->filter_hdr_.simple_ = (enc->config_->filter_type == 0); + enc->filter_hdr_.sharpness_ = enc->config_->filter_sharpness; +} + +//------------------------------------------------------------------------------ + +// Note: if you change the values below, remember that the max range +// allowed by the syntax for DQ_UV is [-16,16]. +#define MAX_DQ_UV (6) +#define MIN_DQ_UV (-4) + +// We want to emulate jpeg-like behaviour where the expected "good" quality +// is around q=75. Internally, our "good" middle is around c=50. So we +// map accordingly using linear piece-wise function +static double QualityToCompression(double c) { + const double linear_c = (c < 0.75) ? c * (2. / 3.) : 2. * c - 1.; + // The file size roughly scales as pow(quantizer, 3.). Actually, the + // exponent is somewhere between 2.8 and 3.2, but we're mostly interested + // in the mid-quant range. So we scale the compressibility inversely to + // this power-law: quant ~= compression ^ 1/3. This law holds well for + // low quant. Finer modeling for high-quant would make use of kAcTable[] + // more explicitly. + const double v = pow(linear_c, 1 / 3.); + return v; +} + +static double QualityToJPEGCompression(double c, double alpha) { + // We map the complexity 'alpha' and quality setting 'c' to a compression + // exponent empirically matched to the compression curve of libjpeg6b. + // On average, the WebP output size will be roughly similar to that of a + // JPEG file compressed with same quality factor. + const double amin = 0.30; + const double amax = 0.85; + const double exp_min = 0.4; + const double exp_max = 0.9; + const double slope = (exp_min - exp_max) / (amax - amin); + // Linearly interpolate 'expn' from exp_min to exp_max + // in the [amin, amax] range. + const double expn = (alpha > amax) ? exp_min + : (alpha < amin) ? exp_max + : exp_max + slope * (alpha - amin); + const double v = pow(c, expn); + return v; +} + +static int SegmentsAreEquivalent(const VP8SegmentInfo* const S1, + const VP8SegmentInfo* const S2) { + return (S1->quant_ == S2->quant_) && (S1->fstrength_ == S2->fstrength_); +} + +static void SimplifySegments(VP8Encoder* const enc) { + int map[NUM_MB_SEGMENTS] = { 0, 1, 2, 3 }; + // 'num_segments_' is previously validated and <= NUM_MB_SEGMENTS, but an + // explicit check is needed to avoid a spurious warning about 'i' exceeding + // array bounds of 'dqm_' with some compilers (noticed with gcc-4.9). + const int num_segments = (enc->segment_hdr_.num_segments_ < NUM_MB_SEGMENTS) + ? enc->segment_hdr_.num_segments_ + : NUM_MB_SEGMENTS; + int num_final_segments = 1; + int s1, s2; + for (s1 = 1; s1 < num_segments; ++s1) { // find similar segments + const VP8SegmentInfo* const S1 = &enc->dqm_[s1]; + int found = 0; + // check if we already have similar segment + for (s2 = 0; s2 < num_final_segments; ++s2) { + const VP8SegmentInfo* const S2 = &enc->dqm_[s2]; + if (SegmentsAreEquivalent(S1, S2)) { + found = 1; + break; + } + } + map[s1] = s2; + if (!found) { + if (num_final_segments != s1) { + enc->dqm_[num_final_segments] = enc->dqm_[s1]; + } + ++num_final_segments; + } + } + if (num_final_segments < num_segments) { // Remap + int i = enc->mb_w_ * enc->mb_h_; + while (i-- > 0) enc->mb_info_[i].segment_ = map[enc->mb_info_[i].segment_]; + enc->segment_hdr_.num_segments_ = num_final_segments; + // Replicate the trailing segment infos (it's mostly cosmetics) + for (i = num_final_segments; i < num_segments; ++i) { + enc->dqm_[i] = enc->dqm_[num_final_segments - 1]; + } + } +} + +void VP8SetSegmentParams(VP8Encoder* const enc, float quality) { + int i; + int dq_uv_ac, dq_uv_dc; + const int num_segments = enc->segment_hdr_.num_segments_; + const double amp = SNS_TO_DQ * enc->config_->sns_strength / 100. / 128.; + const double Q = quality / 100.; + const double c_base = enc->config_->emulate_jpeg_size ? + QualityToJPEGCompression(Q, enc->alpha_ / 255.) : + QualityToCompression(Q); + for (i = 0; i < num_segments; ++i) { + // We modulate the base coefficient to accommodate for the quantization + // susceptibility and allow denser segments to be quantized more. + const double expn = 1. - amp * enc->dqm_[i].alpha_; + const double c = pow(c_base, expn); + const int q = (int)(127. * (1. - c)); + assert(expn > 0.); + enc->dqm_[i].quant_ = clip(q, 0, 127); + } + + // purely indicative in the bitstream (except for the 1-segment case) + enc->base_quant_ = enc->dqm_[0].quant_; + + // fill-in values for the unused segments (required by the syntax) + for (i = num_segments; i < NUM_MB_SEGMENTS; ++i) { + enc->dqm_[i].quant_ = enc->base_quant_; + } + + // uv_alpha_ is normally spread around ~60. The useful range is + // typically ~30 (quite bad) to ~100 (ok to decimate UV more). + // We map it to the safe maximal range of MAX/MIN_DQ_UV for dq_uv. + dq_uv_ac = (enc->uv_alpha_ - MID_ALPHA) * (MAX_DQ_UV - MIN_DQ_UV) + / (MAX_ALPHA - MIN_ALPHA); + // we rescale by the user-defined strength of adaptation + dq_uv_ac = dq_uv_ac * enc->config_->sns_strength / 100; + // and make it safe. + dq_uv_ac = clip(dq_uv_ac, MIN_DQ_UV, MAX_DQ_UV); + // We also boost the dc-uv-quant a little, based on sns-strength, since + // U/V channels are quite more reactive to high quants (flat DC-blocks + // tend to appear, and are unpleasant). + dq_uv_dc = -4 * enc->config_->sns_strength / 100; + dq_uv_dc = clip(dq_uv_dc, -15, 15); // 4bit-signed max allowed + + enc->dq_y1_dc_ = 0; // TODO(skal): dq-lum + enc->dq_y2_dc_ = 0; + enc->dq_y2_ac_ = 0; + enc->dq_uv_dc_ = dq_uv_dc; + enc->dq_uv_ac_ = dq_uv_ac; + + SetupFilterStrength(enc); // initialize segments' filtering, eventually + + if (num_segments > 1) SimplifySegments(enc); + + SetupMatrices(enc); // finalize quantization matrices +} + +//------------------------------------------------------------------------------ +// Form the predictions in cache + +// Must be ordered using {DC_PRED, TM_PRED, V_PRED, H_PRED} as index +const int VP8I16ModeOffsets[4] = { I16DC16, I16TM16, I16VE16, I16HE16 }; +const int VP8UVModeOffsets[4] = { C8DC8, C8TM8, C8VE8, C8HE8 }; + +// Must be indexed using {B_DC_PRED -> B_HU_PRED} as index +const int VP8I4ModeOffsets[NUM_BMODES] = { + I4DC4, I4TM4, I4VE4, I4HE4, I4RD4, I4VR4, I4LD4, I4VL4, I4HD4, I4HU4 +}; + +void VP8MakeLuma16Preds(const VP8EncIterator* const it) { + const uint8_t* const left = it->x_ ? it->y_left_ : NULL; + const uint8_t* const top = it->y_ ? it->y_top_ : NULL; + VP8EncPredLuma16(it->yuv_p_, left, top); +} + +void VP8MakeChroma8Preds(const VP8EncIterator* const it) { + const uint8_t* const left = it->x_ ? it->u_left_ : NULL; + const uint8_t* const top = it->y_ ? it->uv_top_ : NULL; + VP8EncPredChroma8(it->yuv_p_, left, top); +} + +void VP8MakeIntra4Preds(const VP8EncIterator* const it) { + VP8EncPredLuma4(it->yuv_p_, it->i4_top_); +} + +//------------------------------------------------------------------------------ +// Quantize + +// Layout: +// +----+----+ +// |YYYY|UUVV| 0 +// |YYYY|UUVV| 4 +// |YYYY|....| 8 +// |YYYY|....| 12 +// +----+----+ + +const int VP8Scan[16] = { // Luma + 0 + 0 * BPS, 4 + 0 * BPS, 8 + 0 * BPS, 12 + 0 * BPS, + 0 + 4 * BPS, 4 + 4 * BPS, 8 + 4 * BPS, 12 + 4 * BPS, + 0 + 8 * BPS, 4 + 8 * BPS, 8 + 8 * BPS, 12 + 8 * BPS, + 0 + 12 * BPS, 4 + 12 * BPS, 8 + 12 * BPS, 12 + 12 * BPS, +}; + +static const int VP8ScanUV[4 + 4] = { + 0 + 0 * BPS, 4 + 0 * BPS, 0 + 4 * BPS, 4 + 4 * BPS, // U + 8 + 0 * BPS, 12 + 0 * BPS, 8 + 4 * BPS, 12 + 4 * BPS // V +}; + +//------------------------------------------------------------------------------ +// Distortion measurement + +static const uint16_t kWeightY[16] = { + 38, 32, 20, 9, 32, 28, 17, 7, 20, 17, 10, 4, 9, 7, 4, 2 +}; + +static const uint16_t kWeightTrellis[16] = { +#if USE_TDISTO == 0 + 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16 +#else + 30, 27, 19, 11, + 27, 24, 17, 10, + 19, 17, 12, 8, + 11, 10, 8, 6 +#endif +}; + +// Init/Copy the common fields in score. +static void InitScore(VP8ModeScore* const rd) { + rd->D = 0; + rd->SD = 0; + rd->R = 0; + rd->H = 0; + rd->nz = 0; + rd->score = MAX_COST; +} + +static void CopyScore(VP8ModeScore* const dst, const VP8ModeScore* const src) { + dst->D = src->D; + dst->SD = src->SD; + dst->R = src->R; + dst->H = src->H; + dst->nz = src->nz; // note that nz is not accumulated, but just copied. + dst->score = src->score; +} + +static void AddScore(VP8ModeScore* const dst, const VP8ModeScore* const src) { + dst->D += src->D; + dst->SD += src->SD; + dst->R += src->R; + dst->H += src->H; + dst->nz |= src->nz; // here, new nz bits are accumulated. + dst->score += src->score; +} + +//------------------------------------------------------------------------------ +// Performs trellis-optimized quantization. + +// Trellis node +typedef struct { + int8_t prev; // best previous node + int8_t sign; // sign of coeff_i + int16_t level; // level +} Node; + +// Score state +typedef struct { + score_t score; // partial RD score + const uint16_t* costs; // shortcut to cost tables +} ScoreState; + +// If a coefficient was quantized to a value Q (using a neutral bias), +// we test all alternate possibilities between [Q-MIN_DELTA, Q+MAX_DELTA] +// We don't test negative values though. +#define MIN_DELTA 0 // how much lower level to try +#define MAX_DELTA 1 // how much higher +#define NUM_NODES (MIN_DELTA + 1 + MAX_DELTA) +#define NODE(n, l) (nodes[(n)][(l) + MIN_DELTA]) +#define SCORE_STATE(n, l) (score_states[n][(l) + MIN_DELTA]) + +static WEBP_INLINE void SetRDScore(int lambda, VP8ModeScore* const rd) { + rd->score = (rd->R + rd->H) * lambda + RD_DISTO_MULT * (rd->D + rd->SD); +} + +static WEBP_INLINE score_t RDScoreTrellis(int lambda, score_t rate, + score_t distortion) { + return rate * lambda + RD_DISTO_MULT * distortion; +} + +static int TrellisQuantizeBlock(const VP8Encoder* const enc, + int16_t in[16], int16_t out[16], + int ctx0, int coeff_type, + const VP8Matrix* const mtx, + int lambda) { + const ProbaArray* const probas = enc->proba_.coeffs_[coeff_type]; + CostArrayPtr const costs = + (CostArrayPtr)enc->proba_.remapped_costs_[coeff_type]; + const int first = (coeff_type == 0) ? 1 : 0; + Node nodes[16][NUM_NODES]; + ScoreState score_states[2][NUM_NODES]; + ScoreState* ss_cur = &SCORE_STATE(0, MIN_DELTA); + ScoreState* ss_prev = &SCORE_STATE(1, MIN_DELTA); + int best_path[3] = {-1, -1, -1}; // store best-last/best-level/best-previous + score_t best_score; + int n, m, p, last; + + { + score_t cost; + const int thresh = mtx->q_[1] * mtx->q_[1] / 4; + const int last_proba = probas[VP8EncBands[first]][ctx0][0]; + + // compute the position of the last interesting coefficient + last = first - 1; + for (n = 15; n >= first; --n) { + const int j = kZigzag[n]; + const int err = in[j] * in[j]; + if (err > thresh) { + last = n; + break; + } + } + // we don't need to go inspect up to n = 16 coeffs. We can just go up + // to last + 1 (inclusive) without losing much. + if (last < 15) ++last; + + // compute 'skip' score. This is the max score one can do. + cost = VP8BitCost(0, last_proba); + best_score = RDScoreTrellis(lambda, cost, 0); + + // initialize source node. + for (m = -MIN_DELTA; m <= MAX_DELTA; ++m) { + const score_t rate = (ctx0 == 0) ? VP8BitCost(1, last_proba) : 0; + ss_cur[m].score = RDScoreTrellis(lambda, rate, 0); + ss_cur[m].costs = costs[first][ctx0]; + } + } + + // traverse trellis. + for (n = first; n <= last; ++n) { + const int j = kZigzag[n]; + const uint32_t Q = mtx->q_[j]; + const uint32_t iQ = mtx->iq_[j]; + const uint32_t B = BIAS(0x00); // neutral bias + // note: it's important to take sign of the _original_ coeff, + // so we don't have to consider level < 0 afterward. + const int sign = (in[j] < 0); + const uint32_t coeff0 = (sign ? -in[j] : in[j]) + mtx->sharpen_[j]; + int level0 = QUANTDIV(coeff0, iQ, B); + int thresh_level = QUANTDIV(coeff0, iQ, BIAS(0x80)); + if (thresh_level > MAX_LEVEL) thresh_level = MAX_LEVEL; + if (level0 > MAX_LEVEL) level0 = MAX_LEVEL; + + { // Swap current and previous score states + ScoreState* const tmp = ss_cur; + ss_cur = ss_prev; + ss_prev = tmp; + } + + // test all alternate level values around level0. + for (m = -MIN_DELTA; m <= MAX_DELTA; ++m) { + Node* const cur = &NODE(n, m); + int level = level0 + m; + const int ctx = (level > 2) ? 2 : level; + const int band = VP8EncBands[n + 1]; + score_t base_score; + score_t best_cur_score = MAX_COST; + int best_prev = 0; // default, in case + + ss_cur[m].score = MAX_COST; + ss_cur[m].costs = costs[n + 1][ctx]; + if (level < 0 || level > thresh_level) { + // Node is dead. + continue; + } + + { + // Compute delta_error = how much coding this level will + // subtract to max_error as distortion. + // Here, distortion = sum of (|coeff_i| - level_i * Q_i)^2 + const int new_error = coeff0 - level * Q; + const int delta_error = + kWeightTrellis[j] * (new_error * new_error - coeff0 * coeff0); + base_score = RDScoreTrellis(lambda, 0, delta_error); + } + + // Inspect all possible non-dead predecessors. Retain only the best one. + for (p = -MIN_DELTA; p <= MAX_DELTA; ++p) { + // Dead nodes (with ss_prev[p].score >= MAX_COST) are automatically + // eliminated since their score can't be better than the current best. + const score_t cost = VP8LevelCost(ss_prev[p].costs, level); + // Examine node assuming it's a non-terminal one. + const score_t score = + base_score + ss_prev[p].score + RDScoreTrellis(lambda, cost, 0); + if (score < best_cur_score) { + best_cur_score = score; + best_prev = p; + } + } + // Store best finding in current node. + cur->sign = sign; + cur->level = level; + cur->prev = best_prev; + ss_cur[m].score = best_cur_score; + + // Now, record best terminal node (and thus best entry in the graph). + if (level != 0) { + const score_t last_pos_cost = + (n < 15) ? VP8BitCost(0, probas[band][ctx][0]) : 0; + const score_t last_pos_score = RDScoreTrellis(lambda, last_pos_cost, 0); + const score_t score = best_cur_score + last_pos_score; + if (score < best_score) { + best_score = score; + best_path[0] = n; // best eob position + best_path[1] = m; // best node index + best_path[2] = best_prev; // best predecessor + } + } + } + } + + // Fresh start + memset(in + first, 0, (16 - first) * sizeof(*in)); + memset(out + first, 0, (16 - first) * sizeof(*out)); + if (best_path[0] == -1) { + return 0; // skip! + } + + { + // Unwind the best path. + // Note: best-prev on terminal node is not necessarily equal to the + // best_prev for non-terminal. So we patch best_path[2] in. + int nz = 0; + int best_node = best_path[1]; + n = best_path[0]; + NODE(n, best_node).prev = best_path[2]; // force best-prev for terminal + + for (; n >= first; --n) { + const Node* const node = &NODE(n, best_node); + const int j = kZigzag[n]; + out[n] = node->sign ? -node->level : node->level; + nz |= node->level; + in[j] = out[n] * mtx->q_[j]; + best_node = node->prev; + } + return (nz != 0); + } +} + +#undef NODE + +//------------------------------------------------------------------------------ +// Performs: difference, transform, quantize, back-transform, add +// all at once. Output is the reconstructed block in *yuv_out, and the +// quantized levels in *levels. + +static int ReconstructIntra16(VP8EncIterator* const it, + VP8ModeScore* const rd, + uint8_t* const yuv_out, + int mode) { + const VP8Encoder* const enc = it->enc_; + const uint8_t* const ref = it->yuv_p_ + VP8I16ModeOffsets[mode]; + const uint8_t* const src = it->yuv_in_ + Y_OFF_ENC; + const VP8SegmentInfo* const dqm = &enc->dqm_[it->mb_->segment_]; + int nz = 0; + int n; + int16_t tmp[16][16], dc_tmp[16]; + + for (n = 0; n < 16; n += 2) { + VP8FTransform2(src + VP8Scan[n], ref + VP8Scan[n], tmp[n]); + } + VP8FTransformWHT(tmp[0], dc_tmp); + nz |= VP8EncQuantizeBlockWHT(dc_tmp, rd->y_dc_levels, &dqm->y2_) << 24; + + if (DO_TRELLIS_I16 && it->do_trellis_) { + int x, y; + VP8IteratorNzToBytes(it); + for (y = 0, n = 0; y < 4; ++y) { + for (x = 0; x < 4; ++x, ++n) { + const int ctx = it->top_nz_[x] + it->left_nz_[y]; + const int non_zero = + TrellisQuantizeBlock(enc, tmp[n], rd->y_ac_levels[n], ctx, 0, + &dqm->y1_, dqm->lambda_trellis_i16_); + it->top_nz_[x] = it->left_nz_[y] = non_zero; + rd->y_ac_levels[n][0] = 0; + nz |= non_zero << n; + } + } + } else { + for (n = 0; n < 16; n += 2) { + // Zero-out the first coeff, so that: a) nz is correct below, and + // b) finding 'last' non-zero coeffs in SetResidualCoeffs() is simplified. + tmp[n][0] = tmp[n + 1][0] = 0; + nz |= VP8EncQuantize2Blocks(tmp[n], rd->y_ac_levels[n], &dqm->y1_) << n; + assert(rd->y_ac_levels[n + 0][0] == 0); + assert(rd->y_ac_levels[n + 1][0] == 0); + } + } + + // Transform back + VP8TransformWHT(dc_tmp, tmp[0]); + for (n = 0; n < 16; n += 2) { + VP8ITransform(ref + VP8Scan[n], tmp[n], yuv_out + VP8Scan[n], 1); + } + + return nz; +} + +static int ReconstructIntra4(VP8EncIterator* const it, + int16_t levels[16], + const uint8_t* const src, + uint8_t* const yuv_out, + int mode) { + const VP8Encoder* const enc = it->enc_; + const uint8_t* const ref = it->yuv_p_ + VP8I4ModeOffsets[mode]; + const VP8SegmentInfo* const dqm = &enc->dqm_[it->mb_->segment_]; + int nz = 0; + int16_t tmp[16]; + + VP8FTransform(src, ref, tmp); + if (DO_TRELLIS_I4 && it->do_trellis_) { + const int x = it->i4_ & 3, y = it->i4_ >> 2; + const int ctx = it->top_nz_[x] + it->left_nz_[y]; + nz = TrellisQuantizeBlock(enc, tmp, levels, ctx, 3, &dqm->y1_, + dqm->lambda_trellis_i4_); + } else { + nz = VP8EncQuantizeBlock(tmp, levels, &dqm->y1_); + } + VP8ITransform(ref, tmp, yuv_out, 0); + return nz; +} + +static int ReconstructUV(VP8EncIterator* const it, VP8ModeScore* const rd, + uint8_t* const yuv_out, int mode) { + const VP8Encoder* const enc = it->enc_; + const uint8_t* const ref = it->yuv_p_ + VP8UVModeOffsets[mode]; + const uint8_t* const src = it->yuv_in_ + U_OFF_ENC; + const VP8SegmentInfo* const dqm = &enc->dqm_[it->mb_->segment_]; + int nz = 0; + int n; + int16_t tmp[8][16]; + + for (n = 0; n < 8; n += 2) { + VP8FTransform2(src + VP8ScanUV[n], ref + VP8ScanUV[n], tmp[n]); + } + if (DO_TRELLIS_UV && it->do_trellis_) { + int ch, x, y; + for (ch = 0, n = 0; ch <= 2; ch += 2) { + for (y = 0; y < 2; ++y) { + for (x = 0; x < 2; ++x, ++n) { + const int ctx = it->top_nz_[4 + ch + x] + it->left_nz_[4 + ch + y]; + const int non_zero = + TrellisQuantizeBlock(enc, tmp[n], rd->uv_levels[n], ctx, 2, + &dqm->uv_, dqm->lambda_trellis_uv_); + it->top_nz_[4 + ch + x] = it->left_nz_[4 + ch + y] = non_zero; + nz |= non_zero << n; + } + } + } + } else { + for (n = 0; n < 8; n += 2) { + nz |= VP8EncQuantize2Blocks(tmp[n], rd->uv_levels[n], &dqm->uv_) << n; + } + } + + for (n = 0; n < 8; n += 2) { + VP8ITransform(ref + VP8ScanUV[n], tmp[n], yuv_out + VP8ScanUV[n], 1); + } + return (nz << 16); +} + +//------------------------------------------------------------------------------ +// RD-opt decision. Reconstruct each modes, evalue distortion and bit-cost. +// Pick the mode is lower RD-cost = Rate + lambda * Distortion. + +static void StoreMaxDelta(VP8SegmentInfo* const dqm, const int16_t DCs[16]) { + // We look at the first three AC coefficients to determine what is the average + // delta between each sub-4x4 block. + const int v0 = abs(DCs[1]); + const int v1 = abs(DCs[2]); + const int v2 = abs(DCs[4]); + int max_v = (v1 > v0) ? v1 : v0; + max_v = (v2 > max_v) ? v2 : max_v; + if (max_v > dqm->max_edge_) dqm->max_edge_ = max_v; +} + +static void SwapModeScore(VP8ModeScore** a, VP8ModeScore** b) { + VP8ModeScore* const tmp = *a; + *a = *b; + *b = tmp; +} + +static void SwapPtr(uint8_t** a, uint8_t** b) { + uint8_t* const tmp = *a; + *a = *b; + *b = tmp; +} + +static void SwapOut(VP8EncIterator* const it) { + SwapPtr(&it->yuv_out_, &it->yuv_out2_); +} + +static score_t IsFlat(const int16_t* levels, int num_blocks, score_t thresh) { + score_t score = 0; + while (num_blocks-- > 0) { // TODO(skal): refine positional scoring? + int i; + for (i = 1; i < 16; ++i) { // omit DC, we're only interested in AC + score += (levels[i] != 0); + if (score > thresh) return 0; + } + levels += 16; + } + return 1; +} + +static void PickBestIntra16(VP8EncIterator* const it, VP8ModeScore* rd) { + const int kNumBlocks = 16; + VP8SegmentInfo* const dqm = &it->enc_->dqm_[it->mb_->segment_]; + const int lambda = dqm->lambda_i16_; + const int tlambda = dqm->tlambda_; + const uint8_t* const src = it->yuv_in_ + Y_OFF_ENC; + VP8ModeScore rd_tmp; + VP8ModeScore* rd_cur = &rd_tmp; + VP8ModeScore* rd_best = rd; + int mode; + + rd->mode_i16 = -1; + for (mode = 0; mode < NUM_PRED_MODES; ++mode) { + uint8_t* const tmp_dst = it->yuv_out2_ + Y_OFF_ENC; // scratch buffer + rd_cur->mode_i16 = mode; + + // Reconstruct + rd_cur->nz = ReconstructIntra16(it, rd_cur, tmp_dst, mode); + + // Measure RD-score + rd_cur->D = VP8SSE16x16(src, tmp_dst); + rd_cur->SD = + tlambda ? MULT_8B(tlambda, VP8TDisto16x16(src, tmp_dst, kWeightY)) : 0; + rd_cur->H = VP8FixedCostsI16[mode]; + rd_cur->R = VP8GetCostLuma16(it, rd_cur); + if (mode > 0 && + IsFlat(rd_cur->y_ac_levels[0], kNumBlocks, FLATNESS_LIMIT_I16)) { + // penalty to avoid flat area to be mispredicted by complex mode + rd_cur->R += FLATNESS_PENALTY * kNumBlocks; + } + + // Since we always examine Intra16 first, we can overwrite *rd directly. + SetRDScore(lambda, rd_cur); + if (mode == 0 || rd_cur->score < rd_best->score) { + SwapModeScore(&rd_cur, &rd_best); + SwapOut(it); + } + } + if (rd_best != rd) { + memcpy(rd, rd_best, sizeof(*rd)); + } + SetRDScore(dqm->lambda_mode_, rd); // finalize score for mode decision. + VP8SetIntra16Mode(it, rd->mode_i16); + + // we have a blocky macroblock (only DCs are non-zero) with fairly high + // distortion, record max delta so we can later adjust the minimal filtering + // strength needed to smooth these blocks out. + if ((rd->nz & 0x100ffff) == 0x1000000 && rd->D > dqm->min_disto_) { + StoreMaxDelta(dqm, rd->y_dc_levels); + } +} + +//------------------------------------------------------------------------------ + +// return the cost array corresponding to the surrounding prediction modes. +static const uint16_t* GetCostModeI4(VP8EncIterator* const it, + const uint8_t modes[16]) { + const int preds_w = it->enc_->preds_w_; + const int x = (it->i4_ & 3), y = it->i4_ >> 2; + const int left = (x == 0) ? it->preds_[y * preds_w - 1] : modes[it->i4_ - 1]; + const int top = (y == 0) ? it->preds_[-preds_w + x] : modes[it->i4_ - 4]; + return VP8FixedCostsI4[top][left]; +} + +static int PickBestIntra4(VP8EncIterator* const it, VP8ModeScore* const rd) { + const VP8Encoder* const enc = it->enc_; + const VP8SegmentInfo* const dqm = &enc->dqm_[it->mb_->segment_]; + const int lambda = dqm->lambda_i4_; + const int tlambda = dqm->tlambda_; + const uint8_t* const src0 = it->yuv_in_ + Y_OFF_ENC; + uint8_t* const best_blocks = it->yuv_out2_ + Y_OFF_ENC; + int total_header_bits = 0; + VP8ModeScore rd_best; + + if (enc->max_i4_header_bits_ == 0) { + return 0; + } + + InitScore(&rd_best); + rd_best.H = 211; // '211' is the value of VP8BitCost(0, 145) + SetRDScore(dqm->lambda_mode_, &rd_best); + VP8IteratorStartI4(it); + do { + const int kNumBlocks = 1; + VP8ModeScore rd_i4; + int mode; + int best_mode = -1; + const uint8_t* const src = src0 + VP8Scan[it->i4_]; + const uint16_t* const mode_costs = GetCostModeI4(it, rd->modes_i4); + uint8_t* best_block = best_blocks + VP8Scan[it->i4_]; + uint8_t* tmp_dst = it->yuv_p_ + I4TMP; // scratch buffer. + + InitScore(&rd_i4); + VP8MakeIntra4Preds(it); + for (mode = 0; mode < NUM_BMODES; ++mode) { + VP8ModeScore rd_tmp; + int16_t tmp_levels[16]; + + // Reconstruct + rd_tmp.nz = + ReconstructIntra4(it, tmp_levels, src, tmp_dst, mode) << it->i4_; + + // Compute RD-score + rd_tmp.D = VP8SSE4x4(src, tmp_dst); + rd_tmp.SD = + tlambda ? MULT_8B(tlambda, VP8TDisto4x4(src, tmp_dst, kWeightY)) + : 0; + rd_tmp.H = mode_costs[mode]; + + // Add flatness penalty + if (mode > 0 && IsFlat(tmp_levels, kNumBlocks, FLATNESS_LIMIT_I4)) { + rd_tmp.R = FLATNESS_PENALTY * kNumBlocks; + } else { + rd_tmp.R = 0; + } + + // early-out check + SetRDScore(lambda, &rd_tmp); + if (best_mode >= 0 && rd_tmp.score >= rd_i4.score) continue; + + // finish computing score + rd_tmp.R += VP8GetCostLuma4(it, tmp_levels); + SetRDScore(lambda, &rd_tmp); + + if (best_mode < 0 || rd_tmp.score < rd_i4.score) { + CopyScore(&rd_i4, &rd_tmp); + best_mode = mode; + SwapPtr(&tmp_dst, &best_block); + memcpy(rd_best.y_ac_levels[it->i4_], tmp_levels, + sizeof(rd_best.y_ac_levels[it->i4_])); + } + } + SetRDScore(dqm->lambda_mode_, &rd_i4); + AddScore(&rd_best, &rd_i4); + if (rd_best.score >= rd->score) { + return 0; + } + total_header_bits += (int)rd_i4.H; // <- equal to mode_costs[best_mode]; + if (total_header_bits > enc->max_i4_header_bits_) { + return 0; + } + // Copy selected samples if not in the right place already. + if (best_block != best_blocks + VP8Scan[it->i4_]) { + VP8Copy4x4(best_block, best_blocks + VP8Scan[it->i4_]); + } + rd->modes_i4[it->i4_] = best_mode; + it->top_nz_[it->i4_ & 3] = it->left_nz_[it->i4_ >> 2] = (rd_i4.nz ? 1 : 0); + } while (VP8IteratorRotateI4(it, best_blocks)); + + // finalize state + CopyScore(rd, &rd_best); + VP8SetIntra4Mode(it, rd->modes_i4); + SwapOut(it); + memcpy(rd->y_ac_levels, rd_best.y_ac_levels, sizeof(rd->y_ac_levels)); + return 1; // select intra4x4 over intra16x16 +} + +//------------------------------------------------------------------------------ + +static void PickBestUV(VP8EncIterator* const it, VP8ModeScore* const rd) { + const int kNumBlocks = 8; + const VP8SegmentInfo* const dqm = &it->enc_->dqm_[it->mb_->segment_]; + const int lambda = dqm->lambda_uv_; + const uint8_t* const src = it->yuv_in_ + U_OFF_ENC; + uint8_t* tmp_dst = it->yuv_out2_ + U_OFF_ENC; // scratch buffer + uint8_t* dst0 = it->yuv_out_ + U_OFF_ENC; + uint8_t* dst = dst0; + VP8ModeScore rd_best; + int mode; + + rd->mode_uv = -1; + InitScore(&rd_best); + for (mode = 0; mode < NUM_PRED_MODES; ++mode) { + VP8ModeScore rd_uv; + + // Reconstruct + rd_uv.nz = ReconstructUV(it, &rd_uv, tmp_dst, mode); + + // Compute RD-score + rd_uv.D = VP8SSE16x8(src, tmp_dst); + rd_uv.SD = 0; // not calling TDisto here: it tends to flatten areas. + rd_uv.H = VP8FixedCostsUV[mode]; + rd_uv.R = VP8GetCostUV(it, &rd_uv); + if (mode > 0 && IsFlat(rd_uv.uv_levels[0], kNumBlocks, FLATNESS_LIMIT_UV)) { + rd_uv.R += FLATNESS_PENALTY * kNumBlocks; + } + + SetRDScore(lambda, &rd_uv); + if (mode == 0 || rd_uv.score < rd_best.score) { + CopyScore(&rd_best, &rd_uv); + rd->mode_uv = mode; + memcpy(rd->uv_levels, rd_uv.uv_levels, sizeof(rd->uv_levels)); + SwapPtr(&dst, &tmp_dst); + } + } + VP8SetIntraUVMode(it, rd->mode_uv); + AddScore(rd, &rd_best); + if (dst != dst0) { // copy 16x8 block if needed + VP8Copy16x8(dst, dst0); + } +} + +//------------------------------------------------------------------------------ +// Final reconstruction and quantization. + +static void SimpleQuantize(VP8EncIterator* const it, VP8ModeScore* const rd) { + const VP8Encoder* const enc = it->enc_; + const int is_i16 = (it->mb_->type_ == 1); + int nz = 0; + + if (is_i16) { + nz = ReconstructIntra16(it, rd, it->yuv_out_ + Y_OFF_ENC, it->preds_[0]); + } else { + VP8IteratorStartI4(it); + do { + const int mode = + it->preds_[(it->i4_ & 3) + (it->i4_ >> 2) * enc->preds_w_]; + const uint8_t* const src = it->yuv_in_ + Y_OFF_ENC + VP8Scan[it->i4_]; + uint8_t* const dst = it->yuv_out_ + Y_OFF_ENC + VP8Scan[it->i4_]; + VP8MakeIntra4Preds(it); + nz |= ReconstructIntra4(it, rd->y_ac_levels[it->i4_], + src, dst, mode) << it->i4_; + } while (VP8IteratorRotateI4(it, it->yuv_out_ + Y_OFF_ENC)); + } + + nz |= ReconstructUV(it, rd, it->yuv_out_ + U_OFF_ENC, it->mb_->uv_mode_); + rd->nz = nz; +} + +// Refine intra16/intra4 sub-modes based on distortion only (not rate). +static void RefineUsingDistortion(VP8EncIterator* const it, + int try_both_modes, int refine_uv_mode, + VP8ModeScore* const rd) { + score_t best_score = MAX_COST; + int nz = 0; + int mode; + int is_i16 = try_both_modes || (it->mb_->type_ == 1); + + const VP8SegmentInfo* const dqm = &it->enc_->dqm_[it->mb_->segment_]; + // Some empiric constants, of approximate order of magnitude. + const int lambda_d_i16 = 106; + const int lambda_d_i4 = 11; + const int lambda_d_uv = 120; + score_t score_i4 = dqm->i4_penalty_; + score_t i4_bit_sum = 0; + const score_t bit_limit = try_both_modes ? it->enc_->mb_header_limit_ + : MAX_COST; // no early-out allowed + + if (is_i16) { // First, evaluate Intra16 distortion + int best_mode = -1; + const uint8_t* const src = it->yuv_in_ + Y_OFF_ENC; + for (mode = 0; mode < NUM_PRED_MODES; ++mode) { + const uint8_t* const ref = it->yuv_p_ + VP8I16ModeOffsets[mode]; + const score_t score = VP8SSE16x16(src, ref) * RD_DISTO_MULT + + VP8FixedCostsI16[mode] * lambda_d_i16; + if (mode > 0 && VP8FixedCostsI16[mode] > bit_limit) { + continue; + } + if (score < best_score) { + best_mode = mode; + best_score = score; + } + } + VP8SetIntra16Mode(it, best_mode); + // we'll reconstruct later, if i16 mode actually gets selected + } + + // Next, evaluate Intra4 + if (try_both_modes || !is_i16) { + // We don't evaluate the rate here, but just account for it through a + // constant penalty (i4 mode usually needs more bits compared to i16). + is_i16 = 0; + VP8IteratorStartI4(it); + do { + int best_i4_mode = -1; + score_t best_i4_score = MAX_COST; + const uint8_t* const src = it->yuv_in_ + Y_OFF_ENC + VP8Scan[it->i4_]; + const uint16_t* const mode_costs = GetCostModeI4(it, rd->modes_i4); + + VP8MakeIntra4Preds(it); + for (mode = 0; mode < NUM_BMODES; ++mode) { + const uint8_t* const ref = it->yuv_p_ + VP8I4ModeOffsets[mode]; + const score_t score = VP8SSE4x4(src, ref) * RD_DISTO_MULT + + mode_costs[mode] * lambda_d_i4; + if (score < best_i4_score) { + best_i4_mode = mode; + best_i4_score = score; + } + } + i4_bit_sum += mode_costs[best_i4_mode]; + rd->modes_i4[it->i4_] = best_i4_mode; + score_i4 += best_i4_score; + if (score_i4 >= best_score || i4_bit_sum > bit_limit) { + // Intra4 won't be better than Intra16. Bail out and pick Intra16. + is_i16 = 1; + break; + } else { // reconstruct partial block inside yuv_out2_ buffer + uint8_t* const tmp_dst = it->yuv_out2_ + Y_OFF_ENC + VP8Scan[it->i4_]; + nz |= ReconstructIntra4(it, rd->y_ac_levels[it->i4_], + src, tmp_dst, best_i4_mode) << it->i4_; + } + } while (VP8IteratorRotateI4(it, it->yuv_out2_ + Y_OFF_ENC)); + } + + // Final reconstruction, depending on which mode is selected. + if (!is_i16) { + VP8SetIntra4Mode(it, rd->modes_i4); + SwapOut(it); + best_score = score_i4; + } else { + nz = ReconstructIntra16(it, rd, it->yuv_out_ + Y_OFF_ENC, it->preds_[0]); + } + + // ... and UV! + if (refine_uv_mode) { + int best_mode = -1; + score_t best_uv_score = MAX_COST; + const uint8_t* const src = it->yuv_in_ + U_OFF_ENC; + for (mode = 0; mode < NUM_PRED_MODES; ++mode) { + const uint8_t* const ref = it->yuv_p_ + VP8UVModeOffsets[mode]; + const score_t score = VP8SSE16x8(src, ref) * RD_DISTO_MULT + + VP8FixedCostsUV[mode] * lambda_d_uv; + if (score < best_uv_score) { + best_mode = mode; + best_uv_score = score; + } + } + VP8SetIntraUVMode(it, best_mode); + } + nz |= ReconstructUV(it, rd, it->yuv_out_ + U_OFF_ENC, it->mb_->uv_mode_); + + rd->nz = nz; + rd->score = best_score; +} + +//------------------------------------------------------------------------------ +// Entry point + +int VP8Decimate(VP8EncIterator* const it, VP8ModeScore* const rd, + VP8RDLevel rd_opt) { + int is_skipped; + const int method = it->enc_->method_; + + InitScore(rd); + + // We can perform predictions for Luma16x16 and Chroma8x8 already. + // Luma4x4 predictions needs to be done as-we-go. + VP8MakeLuma16Preds(it); + VP8MakeChroma8Preds(it); + + if (rd_opt > RD_OPT_NONE) { + it->do_trellis_ = (rd_opt >= RD_OPT_TRELLIS_ALL); + PickBestIntra16(it, rd); + if (method >= 2) { + PickBestIntra4(it, rd); + } + PickBestUV(it, rd); + if (rd_opt == RD_OPT_TRELLIS) { // finish off with trellis-optim now + it->do_trellis_ = 1; + SimpleQuantize(it, rd); + } + } else { + // At this point we have heuristically decided intra16 / intra4. + // For method >= 2, pick the best intra4/intra16 based on SSE (~tad slower). + // For method <= 1, we don't re-examine the decision but just go ahead with + // quantization/reconstruction. + RefineUsingDistortion(it, (method >= 2), (method >= 1), rd); + } + is_skipped = (rd->nz == 0); + VP8SetSkip(it, is_skipped); + return is_skipped; +} diff --git a/thirdparty/libwebp/enc/syntax.c b/thirdparty/libwebp/enc/syntax.c deleted file mode 100644 index a0e79ef404..0000000000 --- a/thirdparty/libwebp/enc/syntax.c +++ /dev/null @@ -1,383 +0,0 @@ -// Copyright 2011 Google Inc. All Rights Reserved. -// -// Use of this source code is governed by a BSD-style license -// that can be found in the COPYING file in the root of the source -// tree. An additional intellectual property rights grant can be found -// in the file PATENTS. All contributing project authors may -// be found in the AUTHORS file in the root of the source tree. -// ----------------------------------------------------------------------------- -// -// Header syntax writing -// -// Author: Skal (pascal.massimino@gmail.com) - -#include - -#include "../utils/utils.h" -#include "../webp/format_constants.h" // RIFF constants -#include "../webp/mux_types.h" // ALPHA_FLAG -#include "./vp8enci.h" - -//------------------------------------------------------------------------------ -// Helper functions - -static int IsVP8XNeeded(const VP8Encoder* const enc) { - return !!enc->has_alpha_; // Currently the only case when VP8X is needed. - // This could change in the future. -} - -static int PutPaddingByte(const WebPPicture* const pic) { - const uint8_t pad_byte[1] = { 0 }; - return !!pic->writer(pad_byte, 1, pic); -} - -//------------------------------------------------------------------------------ -// Writers for header's various pieces (in order of appearance) - -static WebPEncodingError PutRIFFHeader(const VP8Encoder* const enc, - size_t riff_size) { - const WebPPicture* const pic = enc->pic_; - uint8_t riff[RIFF_HEADER_SIZE] = { - 'R', 'I', 'F', 'F', 0, 0, 0, 0, 'W', 'E', 'B', 'P' - }; - assert(riff_size == (uint32_t)riff_size); - PutLE32(riff + TAG_SIZE, (uint32_t)riff_size); - if (!pic->writer(riff, sizeof(riff), pic)) { - return VP8_ENC_ERROR_BAD_WRITE; - } - return VP8_ENC_OK; -} - -static WebPEncodingError PutVP8XHeader(const VP8Encoder* const enc) { - const WebPPicture* const pic = enc->pic_; - uint8_t vp8x[CHUNK_HEADER_SIZE + VP8X_CHUNK_SIZE] = { - 'V', 'P', '8', 'X' - }; - uint32_t flags = 0; - - assert(IsVP8XNeeded(enc)); - assert(pic->width >= 1 && pic->height >= 1); - assert(pic->width <= MAX_CANVAS_SIZE && pic->height <= MAX_CANVAS_SIZE); - - if (enc->has_alpha_) { - flags |= ALPHA_FLAG; - } - - PutLE32(vp8x + TAG_SIZE, VP8X_CHUNK_SIZE); - PutLE32(vp8x + CHUNK_HEADER_SIZE, flags); - PutLE24(vp8x + CHUNK_HEADER_SIZE + 4, pic->width - 1); - PutLE24(vp8x + CHUNK_HEADER_SIZE + 7, pic->height - 1); - if (!pic->writer(vp8x, sizeof(vp8x), pic)) { - return VP8_ENC_ERROR_BAD_WRITE; - } - return VP8_ENC_OK; -} - -static WebPEncodingError PutAlphaChunk(const VP8Encoder* const enc) { - const WebPPicture* const pic = enc->pic_; - uint8_t alpha_chunk_hdr[CHUNK_HEADER_SIZE] = { - 'A', 'L', 'P', 'H' - }; - - assert(enc->has_alpha_); - - // Alpha chunk header. - PutLE32(alpha_chunk_hdr + TAG_SIZE, enc->alpha_data_size_); - if (!pic->writer(alpha_chunk_hdr, sizeof(alpha_chunk_hdr), pic)) { - return VP8_ENC_ERROR_BAD_WRITE; - } - - // Alpha chunk data. - if (!pic->writer(enc->alpha_data_, enc->alpha_data_size_, pic)) { - return VP8_ENC_ERROR_BAD_WRITE; - } - - // Padding. - if ((enc->alpha_data_size_ & 1) && !PutPaddingByte(pic)) { - return VP8_ENC_ERROR_BAD_WRITE; - } - return VP8_ENC_OK; -} - -static WebPEncodingError PutVP8Header(const WebPPicture* const pic, - size_t vp8_size) { - uint8_t vp8_chunk_hdr[CHUNK_HEADER_SIZE] = { - 'V', 'P', '8', ' ' - }; - assert(vp8_size == (uint32_t)vp8_size); - PutLE32(vp8_chunk_hdr + TAG_SIZE, (uint32_t)vp8_size); - if (!pic->writer(vp8_chunk_hdr, sizeof(vp8_chunk_hdr), pic)) { - return VP8_ENC_ERROR_BAD_WRITE; - } - return VP8_ENC_OK; -} - -static WebPEncodingError PutVP8FrameHeader(const WebPPicture* const pic, - int profile, size_t size0) { - uint8_t vp8_frm_hdr[VP8_FRAME_HEADER_SIZE]; - uint32_t bits; - - if (size0 >= VP8_MAX_PARTITION0_SIZE) { // partition #0 is too big to fit - return VP8_ENC_ERROR_PARTITION0_OVERFLOW; - } - - // Paragraph 9.1. - bits = 0 // keyframe (1b) - | (profile << 1) // profile (3b) - | (1 << 4) // visible (1b) - | ((uint32_t)size0 << 5); // partition length (19b) - vp8_frm_hdr[0] = (bits >> 0) & 0xff; - vp8_frm_hdr[1] = (bits >> 8) & 0xff; - vp8_frm_hdr[2] = (bits >> 16) & 0xff; - // signature - vp8_frm_hdr[3] = (VP8_SIGNATURE >> 16) & 0xff; - vp8_frm_hdr[4] = (VP8_SIGNATURE >> 8) & 0xff; - vp8_frm_hdr[5] = (VP8_SIGNATURE >> 0) & 0xff; - // dimensions - vp8_frm_hdr[6] = pic->width & 0xff; - vp8_frm_hdr[7] = pic->width >> 8; - vp8_frm_hdr[8] = pic->height & 0xff; - vp8_frm_hdr[9] = pic->height >> 8; - - if (!pic->writer(vp8_frm_hdr, sizeof(vp8_frm_hdr), pic)) { - return VP8_ENC_ERROR_BAD_WRITE; - } - return VP8_ENC_OK; -} - -// WebP Headers. -static int PutWebPHeaders(const VP8Encoder* const enc, size_t size0, - size_t vp8_size, size_t riff_size) { - WebPPicture* const pic = enc->pic_; - WebPEncodingError err = VP8_ENC_OK; - - // RIFF header. - err = PutRIFFHeader(enc, riff_size); - if (err != VP8_ENC_OK) goto Error; - - // VP8X. - if (IsVP8XNeeded(enc)) { - err = PutVP8XHeader(enc); - if (err != VP8_ENC_OK) goto Error; - } - - // Alpha. - if (enc->has_alpha_) { - err = PutAlphaChunk(enc); - if (err != VP8_ENC_OK) goto Error; - } - - // VP8 header. - err = PutVP8Header(pic, vp8_size); - if (err != VP8_ENC_OK) goto Error; - - // VP8 frame header. - err = PutVP8FrameHeader(pic, enc->profile_, size0); - if (err != VP8_ENC_OK) goto Error; - - // All OK. - return 1; - - // Error. - Error: - return WebPEncodingSetError(pic, err); -} - -// Segmentation header -static void PutSegmentHeader(VP8BitWriter* const bw, - const VP8Encoder* const enc) { - const VP8EncSegmentHeader* const hdr = &enc->segment_hdr_; - const VP8EncProba* const proba = &enc->proba_; - if (VP8PutBitUniform(bw, (hdr->num_segments_ > 1))) { - // We always 'update' the quant and filter strength values - const int update_data = 1; - int s; - VP8PutBitUniform(bw, hdr->update_map_); - if (VP8PutBitUniform(bw, update_data)) { - // we always use absolute values, not relative ones - VP8PutBitUniform(bw, 1); // (segment_feature_mode = 1. Paragraph 9.3.) - for (s = 0; s < NUM_MB_SEGMENTS; ++s) { - VP8PutSignedBits(bw, enc->dqm_[s].quant_, 7); - } - for (s = 0; s < NUM_MB_SEGMENTS; ++s) { - VP8PutSignedBits(bw, enc->dqm_[s].fstrength_, 6); - } - } - if (hdr->update_map_) { - for (s = 0; s < 3; ++s) { - if (VP8PutBitUniform(bw, (proba->segments_[s] != 255u))) { - VP8PutBits(bw, proba->segments_[s], 8); - } - } - } - } -} - -// Filtering parameters header -static void PutFilterHeader(VP8BitWriter* const bw, - const VP8EncFilterHeader* const hdr) { - const int use_lf_delta = (hdr->i4x4_lf_delta_ != 0); - VP8PutBitUniform(bw, hdr->simple_); - VP8PutBits(bw, hdr->level_, 6); - VP8PutBits(bw, hdr->sharpness_, 3); - if (VP8PutBitUniform(bw, use_lf_delta)) { - // '0' is the default value for i4x4_lf_delta_ at frame #0. - const int need_update = (hdr->i4x4_lf_delta_ != 0); - if (VP8PutBitUniform(bw, need_update)) { - // we don't use ref_lf_delta => emit four 0 bits - VP8PutBits(bw, 0, 4); - // we use mode_lf_delta for i4x4 - VP8PutSignedBits(bw, hdr->i4x4_lf_delta_, 6); - VP8PutBits(bw, 0, 3); // all others unused - } - } -} - -// Nominal quantization parameters -static void PutQuant(VP8BitWriter* const bw, - const VP8Encoder* const enc) { - VP8PutBits(bw, enc->base_quant_, 7); - VP8PutSignedBits(bw, enc->dq_y1_dc_, 4); - VP8PutSignedBits(bw, enc->dq_y2_dc_, 4); - VP8PutSignedBits(bw, enc->dq_y2_ac_, 4); - VP8PutSignedBits(bw, enc->dq_uv_dc_, 4); - VP8PutSignedBits(bw, enc->dq_uv_ac_, 4); -} - -// Partition sizes -static int EmitPartitionsSize(const VP8Encoder* const enc, - WebPPicture* const pic) { - uint8_t buf[3 * (MAX_NUM_PARTITIONS - 1)]; - int p; - for (p = 0; p < enc->num_parts_ - 1; ++p) { - const size_t part_size = VP8BitWriterSize(enc->parts_ + p); - if (part_size >= VP8_MAX_PARTITION_SIZE) { - return WebPEncodingSetError(pic, VP8_ENC_ERROR_PARTITION_OVERFLOW); - } - buf[3 * p + 0] = (part_size >> 0) & 0xff; - buf[3 * p + 1] = (part_size >> 8) & 0xff; - buf[3 * p + 2] = (part_size >> 16) & 0xff; - } - return p ? pic->writer(buf, 3 * p, pic) : 1; -} - -//------------------------------------------------------------------------------ - -static int GeneratePartition0(VP8Encoder* const enc) { - VP8BitWriter* const bw = &enc->bw_; - const int mb_size = enc->mb_w_ * enc->mb_h_; - uint64_t pos1, pos2, pos3; - - pos1 = VP8BitWriterPos(bw); - if (!VP8BitWriterInit(bw, mb_size * 7 / 8)) { // ~7 bits per macroblock - return WebPEncodingSetError(enc->pic_, VP8_ENC_ERROR_OUT_OF_MEMORY); - } - VP8PutBitUniform(bw, 0); // colorspace - VP8PutBitUniform(bw, 0); // clamp type - - PutSegmentHeader(bw, enc); - PutFilterHeader(bw, &enc->filter_hdr_); - VP8PutBits(bw, enc->num_parts_ == 8 ? 3 : - enc->num_parts_ == 4 ? 2 : - enc->num_parts_ == 2 ? 1 : 0, 2); - PutQuant(bw, enc); - VP8PutBitUniform(bw, 0); // no proba update - VP8WriteProbas(bw, &enc->proba_); - pos2 = VP8BitWriterPos(bw); - VP8CodeIntraModes(enc); - VP8BitWriterFinish(bw); - - pos3 = VP8BitWriterPos(bw); - - if (enc->pic_->stats) { - enc->pic_->stats->header_bytes[0] = (int)((pos2 - pos1 + 7) >> 3); - enc->pic_->stats->header_bytes[1] = (int)((pos3 - pos2 + 7) >> 3); - enc->pic_->stats->alpha_data_size = (int)enc->alpha_data_size_; - } - if (bw->error_) { - return WebPEncodingSetError(enc->pic_, VP8_ENC_ERROR_OUT_OF_MEMORY); - } - return 1; -} - -void VP8EncFreeBitWriters(VP8Encoder* const enc) { - int p; - VP8BitWriterWipeOut(&enc->bw_); - for (p = 0; p < enc->num_parts_; ++p) { - VP8BitWriterWipeOut(enc->parts_ + p); - } -} - -int VP8EncWrite(VP8Encoder* const enc) { - WebPPicture* const pic = enc->pic_; - VP8BitWriter* const bw = &enc->bw_; - const int task_percent = 19; - const int percent_per_part = task_percent / enc->num_parts_; - const int final_percent = enc->percent_ + task_percent; - int ok = 0; - size_t vp8_size, pad, riff_size; - int p; - - // Partition #0 with header and partition sizes - ok = GeneratePartition0(enc); - if (!ok) return 0; - - // Compute VP8 size - vp8_size = VP8_FRAME_HEADER_SIZE + - VP8BitWriterSize(bw) + - 3 * (enc->num_parts_ - 1); - for (p = 0; p < enc->num_parts_; ++p) { - vp8_size += VP8BitWriterSize(enc->parts_ + p); - } - pad = vp8_size & 1; - vp8_size += pad; - - // Compute RIFF size - // At the minimum it is: "WEBPVP8 nnnn" + VP8 data size. - riff_size = TAG_SIZE + CHUNK_HEADER_SIZE + vp8_size; - if (IsVP8XNeeded(enc)) { // Add size for: VP8X header + data. - riff_size += CHUNK_HEADER_SIZE + VP8X_CHUNK_SIZE; - } - if (enc->has_alpha_) { // Add size for: ALPH header + data. - const uint32_t padded_alpha_size = enc->alpha_data_size_ + - (enc->alpha_data_size_ & 1); - riff_size += CHUNK_HEADER_SIZE + padded_alpha_size; - } - // Sanity check. - if (riff_size > 0xfffffffeU) { - return WebPEncodingSetError(pic, VP8_ENC_ERROR_FILE_TOO_BIG); - } - - // Emit headers and partition #0 - { - const uint8_t* const part0 = VP8BitWriterBuf(bw); - const size_t size0 = VP8BitWriterSize(bw); - ok = ok && PutWebPHeaders(enc, size0, vp8_size, riff_size) - && pic->writer(part0, size0, pic) - && EmitPartitionsSize(enc, pic); - VP8BitWriterWipeOut(bw); // will free the internal buffer. - } - - // Token partitions - for (p = 0; p < enc->num_parts_; ++p) { - const uint8_t* const buf = VP8BitWriterBuf(enc->parts_ + p); - const size_t size = VP8BitWriterSize(enc->parts_ + p); - if (size) - ok = ok && pic->writer(buf, size, pic); - VP8BitWriterWipeOut(enc->parts_ + p); // will free the internal buffer. - ok = ok && WebPReportProgress(pic, enc->percent_ + percent_per_part, - &enc->percent_); - } - - // Padding byte - if (ok && pad) { - ok = PutPaddingByte(pic); - } - - enc->coded_size_ = (int)(CHUNK_HEADER_SIZE + riff_size); - ok = ok && WebPReportProgress(pic, final_percent, &enc->percent_); - return ok; -} - -//------------------------------------------------------------------------------ - diff --git a/thirdparty/libwebp/enc/syntax_enc.c b/thirdparty/libwebp/enc/syntax_enc.c new file mode 100644 index 0000000000..90665bd7e5 --- /dev/null +++ b/thirdparty/libwebp/enc/syntax_enc.c @@ -0,0 +1,382 @@ +// Copyright 2011 Google Inc. All Rights Reserved. +// +// Use of this source code is governed by a BSD-style license +// that can be found in the COPYING file in the root of the source +// tree. An additional intellectual property rights grant can be found +// in the file PATENTS. All contributing project authors may +// be found in the AUTHORS file in the root of the source tree. +// ----------------------------------------------------------------------------- +// +// Header syntax writing +// +// Author: Skal (pascal.massimino@gmail.com) + +#include + +#include "../utils/utils.h" +#include "../webp/format_constants.h" // RIFF constants +#include "../webp/mux_types.h" // ALPHA_FLAG +#include "./vp8i_enc.h" + +//------------------------------------------------------------------------------ +// Helper functions + +static int IsVP8XNeeded(const VP8Encoder* const enc) { + return !!enc->has_alpha_; // Currently the only case when VP8X is needed. + // This could change in the future. +} + +static int PutPaddingByte(const WebPPicture* const pic) { + const uint8_t pad_byte[1] = { 0 }; + return !!pic->writer(pad_byte, 1, pic); +} + +//------------------------------------------------------------------------------ +// Writers for header's various pieces (in order of appearance) + +static WebPEncodingError PutRIFFHeader(const VP8Encoder* const enc, + size_t riff_size) { + const WebPPicture* const pic = enc->pic_; + uint8_t riff[RIFF_HEADER_SIZE] = { + 'R', 'I', 'F', 'F', 0, 0, 0, 0, 'W', 'E', 'B', 'P' + }; + assert(riff_size == (uint32_t)riff_size); + PutLE32(riff + TAG_SIZE, (uint32_t)riff_size); + if (!pic->writer(riff, sizeof(riff), pic)) { + return VP8_ENC_ERROR_BAD_WRITE; + } + return VP8_ENC_OK; +} + +static WebPEncodingError PutVP8XHeader(const VP8Encoder* const enc) { + const WebPPicture* const pic = enc->pic_; + uint8_t vp8x[CHUNK_HEADER_SIZE + VP8X_CHUNK_SIZE] = { + 'V', 'P', '8', 'X' + }; + uint32_t flags = 0; + + assert(IsVP8XNeeded(enc)); + assert(pic->width >= 1 && pic->height >= 1); + assert(pic->width <= MAX_CANVAS_SIZE && pic->height <= MAX_CANVAS_SIZE); + + if (enc->has_alpha_) { + flags |= ALPHA_FLAG; + } + + PutLE32(vp8x + TAG_SIZE, VP8X_CHUNK_SIZE); + PutLE32(vp8x + CHUNK_HEADER_SIZE, flags); + PutLE24(vp8x + CHUNK_HEADER_SIZE + 4, pic->width - 1); + PutLE24(vp8x + CHUNK_HEADER_SIZE + 7, pic->height - 1); + if (!pic->writer(vp8x, sizeof(vp8x), pic)) { + return VP8_ENC_ERROR_BAD_WRITE; + } + return VP8_ENC_OK; +} + +static WebPEncodingError PutAlphaChunk(const VP8Encoder* const enc) { + const WebPPicture* const pic = enc->pic_; + uint8_t alpha_chunk_hdr[CHUNK_HEADER_SIZE] = { + 'A', 'L', 'P', 'H' + }; + + assert(enc->has_alpha_); + + // Alpha chunk header. + PutLE32(alpha_chunk_hdr + TAG_SIZE, enc->alpha_data_size_); + if (!pic->writer(alpha_chunk_hdr, sizeof(alpha_chunk_hdr), pic)) { + return VP8_ENC_ERROR_BAD_WRITE; + } + + // Alpha chunk data. + if (!pic->writer(enc->alpha_data_, enc->alpha_data_size_, pic)) { + return VP8_ENC_ERROR_BAD_WRITE; + } + + // Padding. + if ((enc->alpha_data_size_ & 1) && !PutPaddingByte(pic)) { + return VP8_ENC_ERROR_BAD_WRITE; + } + return VP8_ENC_OK; +} + +static WebPEncodingError PutVP8Header(const WebPPicture* const pic, + size_t vp8_size) { + uint8_t vp8_chunk_hdr[CHUNK_HEADER_SIZE] = { + 'V', 'P', '8', ' ' + }; + assert(vp8_size == (uint32_t)vp8_size); + PutLE32(vp8_chunk_hdr + TAG_SIZE, (uint32_t)vp8_size); + if (!pic->writer(vp8_chunk_hdr, sizeof(vp8_chunk_hdr), pic)) { + return VP8_ENC_ERROR_BAD_WRITE; + } + return VP8_ENC_OK; +} + +static WebPEncodingError PutVP8FrameHeader(const WebPPicture* const pic, + int profile, size_t size0) { + uint8_t vp8_frm_hdr[VP8_FRAME_HEADER_SIZE]; + uint32_t bits; + + if (size0 >= VP8_MAX_PARTITION0_SIZE) { // partition #0 is too big to fit + return VP8_ENC_ERROR_PARTITION0_OVERFLOW; + } + + // Paragraph 9.1. + bits = 0 // keyframe (1b) + | (profile << 1) // profile (3b) + | (1 << 4) // visible (1b) + | ((uint32_t)size0 << 5); // partition length (19b) + vp8_frm_hdr[0] = (bits >> 0) & 0xff; + vp8_frm_hdr[1] = (bits >> 8) & 0xff; + vp8_frm_hdr[2] = (bits >> 16) & 0xff; + // signature + vp8_frm_hdr[3] = (VP8_SIGNATURE >> 16) & 0xff; + vp8_frm_hdr[4] = (VP8_SIGNATURE >> 8) & 0xff; + vp8_frm_hdr[5] = (VP8_SIGNATURE >> 0) & 0xff; + // dimensions + vp8_frm_hdr[6] = pic->width & 0xff; + vp8_frm_hdr[7] = pic->width >> 8; + vp8_frm_hdr[8] = pic->height & 0xff; + vp8_frm_hdr[9] = pic->height >> 8; + + if (!pic->writer(vp8_frm_hdr, sizeof(vp8_frm_hdr), pic)) { + return VP8_ENC_ERROR_BAD_WRITE; + } + return VP8_ENC_OK; +} + +// WebP Headers. +static int PutWebPHeaders(const VP8Encoder* const enc, size_t size0, + size_t vp8_size, size_t riff_size) { + WebPPicture* const pic = enc->pic_; + WebPEncodingError err = VP8_ENC_OK; + + // RIFF header. + err = PutRIFFHeader(enc, riff_size); + if (err != VP8_ENC_OK) goto Error; + + // VP8X. + if (IsVP8XNeeded(enc)) { + err = PutVP8XHeader(enc); + if (err != VP8_ENC_OK) goto Error; + } + + // Alpha. + if (enc->has_alpha_) { + err = PutAlphaChunk(enc); + if (err != VP8_ENC_OK) goto Error; + } + + // VP8 header. + err = PutVP8Header(pic, vp8_size); + if (err != VP8_ENC_OK) goto Error; + + // VP8 frame header. + err = PutVP8FrameHeader(pic, enc->profile_, size0); + if (err != VP8_ENC_OK) goto Error; + + // All OK. + return 1; + + // Error. + Error: + return WebPEncodingSetError(pic, err); +} + +// Segmentation header +static void PutSegmentHeader(VP8BitWriter* const bw, + const VP8Encoder* const enc) { + const VP8EncSegmentHeader* const hdr = &enc->segment_hdr_; + const VP8EncProba* const proba = &enc->proba_; + if (VP8PutBitUniform(bw, (hdr->num_segments_ > 1))) { + // We always 'update' the quant and filter strength values + const int update_data = 1; + int s; + VP8PutBitUniform(bw, hdr->update_map_); + if (VP8PutBitUniform(bw, update_data)) { + // we always use absolute values, not relative ones + VP8PutBitUniform(bw, 1); // (segment_feature_mode = 1. Paragraph 9.3.) + for (s = 0; s < NUM_MB_SEGMENTS; ++s) { + VP8PutSignedBits(bw, enc->dqm_[s].quant_, 7); + } + for (s = 0; s < NUM_MB_SEGMENTS; ++s) { + VP8PutSignedBits(bw, enc->dqm_[s].fstrength_, 6); + } + } + if (hdr->update_map_) { + for (s = 0; s < 3; ++s) { + if (VP8PutBitUniform(bw, (proba->segments_[s] != 255u))) { + VP8PutBits(bw, proba->segments_[s], 8); + } + } + } + } +} + +// Filtering parameters header +static void PutFilterHeader(VP8BitWriter* const bw, + const VP8EncFilterHeader* const hdr) { + const int use_lf_delta = (hdr->i4x4_lf_delta_ != 0); + VP8PutBitUniform(bw, hdr->simple_); + VP8PutBits(bw, hdr->level_, 6); + VP8PutBits(bw, hdr->sharpness_, 3); + if (VP8PutBitUniform(bw, use_lf_delta)) { + // '0' is the default value for i4x4_lf_delta_ at frame #0. + const int need_update = (hdr->i4x4_lf_delta_ != 0); + if (VP8PutBitUniform(bw, need_update)) { + // we don't use ref_lf_delta => emit four 0 bits + VP8PutBits(bw, 0, 4); + // we use mode_lf_delta for i4x4 + VP8PutSignedBits(bw, hdr->i4x4_lf_delta_, 6); + VP8PutBits(bw, 0, 3); // all others unused + } + } +} + +// Nominal quantization parameters +static void PutQuant(VP8BitWriter* const bw, + const VP8Encoder* const enc) { + VP8PutBits(bw, enc->base_quant_, 7); + VP8PutSignedBits(bw, enc->dq_y1_dc_, 4); + VP8PutSignedBits(bw, enc->dq_y2_dc_, 4); + VP8PutSignedBits(bw, enc->dq_y2_ac_, 4); + VP8PutSignedBits(bw, enc->dq_uv_dc_, 4); + VP8PutSignedBits(bw, enc->dq_uv_ac_, 4); +} + +// Partition sizes +static int EmitPartitionsSize(const VP8Encoder* const enc, + WebPPicture* const pic) { + uint8_t buf[3 * (MAX_NUM_PARTITIONS - 1)]; + int p; + for (p = 0; p < enc->num_parts_ - 1; ++p) { + const size_t part_size = VP8BitWriterSize(enc->parts_ + p); + if (part_size >= VP8_MAX_PARTITION_SIZE) { + return WebPEncodingSetError(pic, VP8_ENC_ERROR_PARTITION_OVERFLOW); + } + buf[3 * p + 0] = (part_size >> 0) & 0xff; + buf[3 * p + 1] = (part_size >> 8) & 0xff; + buf[3 * p + 2] = (part_size >> 16) & 0xff; + } + return p ? pic->writer(buf, 3 * p, pic) : 1; +} + +//------------------------------------------------------------------------------ + +static int GeneratePartition0(VP8Encoder* const enc) { + VP8BitWriter* const bw = &enc->bw_; + const int mb_size = enc->mb_w_ * enc->mb_h_; + uint64_t pos1, pos2, pos3; + + pos1 = VP8BitWriterPos(bw); + if (!VP8BitWriterInit(bw, mb_size * 7 / 8)) { // ~7 bits per macroblock + return WebPEncodingSetError(enc->pic_, VP8_ENC_ERROR_OUT_OF_MEMORY); + } + VP8PutBitUniform(bw, 0); // colorspace + VP8PutBitUniform(bw, 0); // clamp type + + PutSegmentHeader(bw, enc); + PutFilterHeader(bw, &enc->filter_hdr_); + VP8PutBits(bw, enc->num_parts_ == 8 ? 3 : + enc->num_parts_ == 4 ? 2 : + enc->num_parts_ == 2 ? 1 : 0, 2); + PutQuant(bw, enc); + VP8PutBitUniform(bw, 0); // no proba update + VP8WriteProbas(bw, &enc->proba_); + pos2 = VP8BitWriterPos(bw); + VP8CodeIntraModes(enc); + VP8BitWriterFinish(bw); + + pos3 = VP8BitWriterPos(bw); + + if (enc->pic_->stats) { + enc->pic_->stats->header_bytes[0] = (int)((pos2 - pos1 + 7) >> 3); + enc->pic_->stats->header_bytes[1] = (int)((pos3 - pos2 + 7) >> 3); + enc->pic_->stats->alpha_data_size = (int)enc->alpha_data_size_; + } + if (bw->error_) { + return WebPEncodingSetError(enc->pic_, VP8_ENC_ERROR_OUT_OF_MEMORY); + } + return 1; +} + +void VP8EncFreeBitWriters(VP8Encoder* const enc) { + int p; + VP8BitWriterWipeOut(&enc->bw_); + for (p = 0; p < enc->num_parts_; ++p) { + VP8BitWriterWipeOut(enc->parts_ + p); + } +} + +int VP8EncWrite(VP8Encoder* const enc) { + WebPPicture* const pic = enc->pic_; + VP8BitWriter* const bw = &enc->bw_; + const int task_percent = 19; + const int percent_per_part = task_percent / enc->num_parts_; + const int final_percent = enc->percent_ + task_percent; + int ok = 0; + size_t vp8_size, pad, riff_size; + int p; + + // Partition #0 with header and partition sizes + ok = GeneratePartition0(enc); + if (!ok) return 0; + + // Compute VP8 size + vp8_size = VP8_FRAME_HEADER_SIZE + + VP8BitWriterSize(bw) + + 3 * (enc->num_parts_ - 1); + for (p = 0; p < enc->num_parts_; ++p) { + vp8_size += VP8BitWriterSize(enc->parts_ + p); + } + pad = vp8_size & 1; + vp8_size += pad; + + // Compute RIFF size + // At the minimum it is: "WEBPVP8 nnnn" + VP8 data size. + riff_size = TAG_SIZE + CHUNK_HEADER_SIZE + vp8_size; + if (IsVP8XNeeded(enc)) { // Add size for: VP8X header + data. + riff_size += CHUNK_HEADER_SIZE + VP8X_CHUNK_SIZE; + } + if (enc->has_alpha_) { // Add size for: ALPH header + data. + const uint32_t padded_alpha_size = enc->alpha_data_size_ + + (enc->alpha_data_size_ & 1); + riff_size += CHUNK_HEADER_SIZE + padded_alpha_size; + } + // Sanity check. + if (riff_size > 0xfffffffeU) { + return WebPEncodingSetError(pic, VP8_ENC_ERROR_FILE_TOO_BIG); + } + + // Emit headers and partition #0 + { + const uint8_t* const part0 = VP8BitWriterBuf(bw); + const size_t size0 = VP8BitWriterSize(bw); + ok = ok && PutWebPHeaders(enc, size0, vp8_size, riff_size) + && pic->writer(part0, size0, pic) + && EmitPartitionsSize(enc, pic); + VP8BitWriterWipeOut(bw); // will free the internal buffer. + } + + // Token partitions + for (p = 0; p < enc->num_parts_; ++p) { + const uint8_t* const buf = VP8BitWriterBuf(enc->parts_ + p); + const size_t size = VP8BitWriterSize(enc->parts_ + p); + if (size) ok = ok && pic->writer(buf, size, pic); + VP8BitWriterWipeOut(enc->parts_ + p); // will free the internal buffer. + ok = ok && WebPReportProgress(pic, enc->percent_ + percent_per_part, + &enc->percent_); + } + + // Padding byte + if (ok && pad) { + ok = PutPaddingByte(pic); + } + + enc->coded_size_ = (int)(CHUNK_HEADER_SIZE + riff_size); + ok = ok && WebPReportProgress(pic, final_percent, &enc->percent_); + return ok; +} + +//------------------------------------------------------------------------------ + diff --git a/thirdparty/libwebp/enc/token.c b/thirdparty/libwebp/enc/token.c deleted file mode 100644 index 087940e5ff..0000000000 --- a/thirdparty/libwebp/enc/token.c +++ /dev/null @@ -1,293 +0,0 @@ -// Copyright 2011 Google Inc. All Rights Reserved. -// -// Use of this source code is governed by a BSD-style license -// that can be found in the COPYING file in the root of the source -// tree. An additional intellectual property rights grant can be found -// in the file PATENTS. All contributing project authors may -// be found in the AUTHORS file in the root of the source tree. -// ----------------------------------------------------------------------------- -// -// Paginated token buffer -// -// A 'token' is a bit value associated with a probability, either fixed -// or a later-to-be-determined after statistics have been collected. -// For dynamic probability, we just record the slot id (idx) for the probability -// value in the final probability array (uint8_t* probas in VP8EmitTokens). -// -// Author: Skal (pascal.massimino@gmail.com) - -#include -#include -#include - -#include "./cost.h" -#include "./vp8enci.h" -#include "../utils/utils.h" - -#if !defined(DISABLE_TOKEN_BUFFER) - -// we use pages to reduce the number of memcpy() -#define MIN_PAGE_SIZE 8192 // minimum number of token per page -#define FIXED_PROBA_BIT (1u << 14) - -typedef uint16_t token_t; // bit #15: bit value - // bit #14: flags for constant proba or idx - // bits #0..13: slot or constant proba -struct VP8Tokens { - VP8Tokens* next_; // pointer to next page -}; -// Token data is located in memory just after the next_ field. -// This macro is used to return their address and hide the trick. -#define TOKEN_DATA(p) ((const token_t*)&(p)[1]) - -//------------------------------------------------------------------------------ - -void VP8TBufferInit(VP8TBuffer* const b, int page_size) { - b->tokens_ = NULL; - b->pages_ = NULL; - b->last_page_ = &b->pages_; - b->left_ = 0; - b->page_size_ = (page_size < MIN_PAGE_SIZE) ? MIN_PAGE_SIZE : page_size; - b->error_ = 0; -} - -void VP8TBufferClear(VP8TBuffer* const b) { - if (b != NULL) { - VP8Tokens* p = b->pages_; - while (p != NULL) { - VP8Tokens* const next = p->next_; - WebPSafeFree(p); - p = next; - } - VP8TBufferInit(b, b->page_size_); - } -} - -static int TBufferNewPage(VP8TBuffer* const b) { - VP8Tokens* page = NULL; - if (!b->error_) { - const size_t size = sizeof(*page) + b->page_size_ * sizeof(token_t); - page = (VP8Tokens*)WebPSafeMalloc(1ULL, size); - } - if (page == NULL) { - b->error_ = 1; - return 0; - } - page->next_ = NULL; - - *b->last_page_ = page; - b->last_page_ = &page->next_; - b->left_ = b->page_size_; - b->tokens_ = (token_t*)TOKEN_DATA(page); - return 1; -} - -//------------------------------------------------------------------------------ - -#define TOKEN_ID(t, b, ctx) \ - (NUM_PROBAS * ((ctx) + NUM_CTX * ((b) + NUM_BANDS * (t)))) - -static WEBP_INLINE uint32_t AddToken(VP8TBuffer* const b, uint32_t bit, - uint32_t proba_idx, - proba_t* const stats) { - assert(proba_idx < FIXED_PROBA_BIT); - assert(bit <= 1); - if (b->left_ > 0 || TBufferNewPage(b)) { - const int slot = --b->left_; - b->tokens_[slot] = (bit << 15) | proba_idx; - } - VP8RecordStats(bit, stats); - return bit; -} - -static WEBP_INLINE void AddConstantToken(VP8TBuffer* const b, - uint32_t bit, uint32_t proba) { - assert(proba < 256); - assert(bit <= 1); - if (b->left_ > 0 || TBufferNewPage(b)) { - const int slot = --b->left_; - b->tokens_[slot] = (bit << 15) | FIXED_PROBA_BIT | proba; - } -} - -int VP8RecordCoeffTokens(int ctx, const struct VP8Residual* const res, - VP8TBuffer* const tokens) { - const int16_t* const coeffs = res->coeffs; - const int coeff_type = res->coeff_type; - const int last = res->last; - int n = res->first; - uint32_t base_id = TOKEN_ID(coeff_type, n, ctx); - // should be stats[VP8EncBands[n]], but it's equivalent for n=0 or 1 - proba_t* s = res->stats[n][ctx]; - if (!AddToken(tokens, last >= 0, base_id + 0, s + 0)) { - return 0; - } - - while (n < 16) { - const int c = coeffs[n++]; - const int sign = c < 0; - const uint32_t v = sign ? -c : c; - if (!AddToken(tokens, v != 0, base_id + 1, s + 1)) { - base_id = TOKEN_ID(coeff_type, VP8EncBands[n], 0); // ctx=0 - s = res->stats[VP8EncBands[n]][0]; - continue; - } - if (!AddToken(tokens, v > 1, base_id + 2, s + 2)) { - base_id = TOKEN_ID(coeff_type, VP8EncBands[n], 1); // ctx=1 - s = res->stats[VP8EncBands[n]][1]; - } else { - if (!AddToken(tokens, v > 4, base_id + 3, s + 3)) { - if (AddToken(tokens, v != 2, base_id + 4, s + 4)) - AddToken(tokens, v == 4, base_id + 5, s + 5); - } else if (!AddToken(tokens, v > 10, base_id + 6, s + 6)) { - if (!AddToken(tokens, v > 6, base_id + 7, s + 7)) { - AddConstantToken(tokens, v == 6, 159); - } else { - AddConstantToken(tokens, v >= 9, 165); - AddConstantToken(tokens, !(v & 1), 145); - } - } else { - int mask; - const uint8_t* tab; - uint32_t residue = v - 3; - if (residue < (8 << 1)) { // VP8Cat3 (3b) - AddToken(tokens, 0, base_id + 8, s + 8); - AddToken(tokens, 0, base_id + 9, s + 9); - residue -= (8 << 0); - mask = 1 << 2; - tab = VP8Cat3; - } else if (residue < (8 << 2)) { // VP8Cat4 (4b) - AddToken(tokens, 0, base_id + 8, s + 8); - AddToken(tokens, 1, base_id + 9, s + 9); - residue -= (8 << 1); - mask = 1 << 3; - tab = VP8Cat4; - } else if (residue < (8 << 3)) { // VP8Cat5 (5b) - AddToken(tokens, 1, base_id + 8, s + 8); - AddToken(tokens, 0, base_id + 10, s + 9); - residue -= (8 << 2); - mask = 1 << 4; - tab = VP8Cat5; - } else { // VP8Cat6 (11b) - AddToken(tokens, 1, base_id + 8, s + 8); - AddToken(tokens, 1, base_id + 10, s + 9); - residue -= (8 << 3); - mask = 1 << 10; - tab = VP8Cat6; - } - while (mask) { - AddConstantToken(tokens, !!(residue & mask), *tab++); - mask >>= 1; - } - } - base_id = TOKEN_ID(coeff_type, VP8EncBands[n], 2); // ctx=2 - s = res->stats[VP8EncBands[n]][2]; - } - AddConstantToken(tokens, sign, 128); - if (n == 16 || !AddToken(tokens, n <= last, base_id + 0, s + 0)) { - return 1; // EOB - } - } - return 1; -} - -#undef TOKEN_ID - -//------------------------------------------------------------------------------ -// This function works, but isn't currently used. Saved for later. - -#if 0 - -static void Record(int bit, proba_t* const stats) { - proba_t p = *stats; - if (p >= 0xffff0000u) { // an overflow is inbound. - p = ((p + 1u) >> 1) & 0x7fff7fffu; // -> divide the stats by 2. - } - // record bit count (lower 16 bits) and increment total count (upper 16 bits). - p += 0x00010000u + bit; - *stats = p; -} - -void VP8TokenToStats(const VP8TBuffer* const b, proba_t* const stats) { - const VP8Tokens* p = b->pages_; - while (p != NULL) { - const int N = (p->next_ == NULL) ? b->left_ : 0; - int n = MAX_NUM_TOKEN; - const token_t* const tokens = TOKEN_DATA(p); - while (n-- > N) { - const token_t token = tokens[n]; - if (!(token & FIXED_PROBA_BIT)) { - Record((token >> 15) & 1, stats + (token & 0x3fffu)); - } - } - p = p->next_; - } -} - -#endif // 0 - -//------------------------------------------------------------------------------ -// Final coding pass, with known probabilities - -int VP8EmitTokens(VP8TBuffer* const b, VP8BitWriter* const bw, - const uint8_t* const probas, int final_pass) { - const VP8Tokens* p = b->pages_; - assert(!b->error_); - while (p != NULL) { - const VP8Tokens* const next = p->next_; - const int N = (next == NULL) ? b->left_ : 0; - int n = b->page_size_; - const token_t* const tokens = TOKEN_DATA(p); - while (n-- > N) { - const token_t token = tokens[n]; - const int bit = (token >> 15) & 1; - if (token & FIXED_PROBA_BIT) { - VP8PutBit(bw, bit, token & 0xffu); // constant proba - } else { - VP8PutBit(bw, bit, probas[token & 0x3fffu]); - } - } - if (final_pass) WebPSafeFree((void*)p); - p = next; - } - if (final_pass) b->pages_ = NULL; - return 1; -} - -// Size estimation -size_t VP8EstimateTokenSize(VP8TBuffer* const b, const uint8_t* const probas) { - size_t size = 0; - const VP8Tokens* p = b->pages_; - assert(!b->error_); - while (p != NULL) { - const VP8Tokens* const next = p->next_; - const int N = (next == NULL) ? b->left_ : 0; - int n = b->page_size_; - const token_t* const tokens = TOKEN_DATA(p); - while (n-- > N) { - const token_t token = tokens[n]; - const int bit = token & (1 << 15); - if (token & FIXED_PROBA_BIT) { - size += VP8BitCost(bit, token & 0xffu); - } else { - size += VP8BitCost(bit, probas[token & 0x3fffu]); - } - } - p = next; - } - return size; -} - -//------------------------------------------------------------------------------ - -#else // DISABLE_TOKEN_BUFFER - -void VP8TBufferInit(VP8TBuffer* const b) { - (void)b; -} -void VP8TBufferClear(VP8TBuffer* const b) { - (void)b; -} - -#endif // !DISABLE_TOKEN_BUFFER - diff --git a/thirdparty/libwebp/enc/token_enc.c b/thirdparty/libwebp/enc/token_enc.c new file mode 100644 index 0000000000..02a0d72cc6 --- /dev/null +++ b/thirdparty/libwebp/enc/token_enc.c @@ -0,0 +1,294 @@ +// Copyright 2011 Google Inc. All Rights Reserved. +// +// Use of this source code is governed by a BSD-style license +// that can be found in the COPYING file in the root of the source +// tree. An additional intellectual property rights grant can be found +// in the file PATENTS. All contributing project authors may +// be found in the AUTHORS file in the root of the source tree. +// ----------------------------------------------------------------------------- +// +// Paginated token buffer +// +// A 'token' is a bit value associated with a probability, either fixed +// or a later-to-be-determined after statistics have been collected. +// For dynamic probability, we just record the slot id (idx) for the probability +// value in the final probability array (uint8_t* probas in VP8EmitTokens). +// +// Author: Skal (pascal.massimino@gmail.com) + +#include +#include +#include + +#include "./cost_enc.h" +#include "./vp8i_enc.h" +#include "../utils/utils.h" + +#if !defined(DISABLE_TOKEN_BUFFER) + +// we use pages to reduce the number of memcpy() +#define MIN_PAGE_SIZE 8192 // minimum number of token per page +#define FIXED_PROBA_BIT (1u << 14) + +typedef uint16_t token_t; // bit #15: bit value + // bit #14: flags for constant proba or idx + // bits #0..13: slot or constant proba +struct VP8Tokens { + VP8Tokens* next_; // pointer to next page +}; +// Token data is located in memory just after the next_ field. +// This macro is used to return their address and hide the trick. +#define TOKEN_DATA(p) ((const token_t*)&(p)[1]) + +//------------------------------------------------------------------------------ + +void VP8TBufferInit(VP8TBuffer* const b, int page_size) { + b->tokens_ = NULL; + b->pages_ = NULL; + b->last_page_ = &b->pages_; + b->left_ = 0; + b->page_size_ = (page_size < MIN_PAGE_SIZE) ? MIN_PAGE_SIZE : page_size; + b->error_ = 0; +} + +void VP8TBufferClear(VP8TBuffer* const b) { + if (b != NULL) { + VP8Tokens* p = b->pages_; + while (p != NULL) { + VP8Tokens* const next = p->next_; + WebPSafeFree(p); + p = next; + } + VP8TBufferInit(b, b->page_size_); + } +} + +static int TBufferNewPage(VP8TBuffer* const b) { + VP8Tokens* page = NULL; + if (!b->error_) { + const size_t size = sizeof(*page) + b->page_size_ * sizeof(token_t); + page = (VP8Tokens*)WebPSafeMalloc(1ULL, size); + } + if (page == NULL) { + b->error_ = 1; + return 0; + } + page->next_ = NULL; + + *b->last_page_ = page; + b->last_page_ = &page->next_; + b->left_ = b->page_size_; + b->tokens_ = (token_t*)TOKEN_DATA(page); + return 1; +} + +//------------------------------------------------------------------------------ + +#define TOKEN_ID(t, b, ctx) \ + (NUM_PROBAS * ((ctx) + NUM_CTX * ((b) + NUM_BANDS * (t)))) + +static WEBP_INLINE uint32_t AddToken(VP8TBuffer* const b, uint32_t bit, + uint32_t proba_idx, + proba_t* const stats) { + assert(proba_idx < FIXED_PROBA_BIT); + assert(bit <= 1); + if (b->left_ > 0 || TBufferNewPage(b)) { + const int slot = --b->left_; + b->tokens_[slot] = (bit << 15) | proba_idx; + } + VP8RecordStats(bit, stats); + return bit; +} + +static WEBP_INLINE void AddConstantToken(VP8TBuffer* const b, + uint32_t bit, uint32_t proba) { + assert(proba < 256); + assert(bit <= 1); + if (b->left_ > 0 || TBufferNewPage(b)) { + const int slot = --b->left_; + b->tokens_[slot] = (bit << 15) | FIXED_PROBA_BIT | proba; + } +} + +int VP8RecordCoeffTokens(int ctx, const struct VP8Residual* const res, + VP8TBuffer* const tokens) { + const int16_t* const coeffs = res->coeffs; + const int coeff_type = res->coeff_type; + const int last = res->last; + int n = res->first; + uint32_t base_id = TOKEN_ID(coeff_type, n, ctx); + // should be stats[VP8EncBands[n]], but it's equivalent for n=0 or 1 + proba_t* s = res->stats[n][ctx]; + if (!AddToken(tokens, last >= 0, base_id + 0, s + 0)) { + return 0; + } + + while (n < 16) { + const int c = coeffs[n++]; + const int sign = c < 0; + const uint32_t v = sign ? -c : c; + if (!AddToken(tokens, v != 0, base_id + 1, s + 1)) { + base_id = TOKEN_ID(coeff_type, VP8EncBands[n], 0); // ctx=0 + s = res->stats[VP8EncBands[n]][0]; + continue; + } + if (!AddToken(tokens, v > 1, base_id + 2, s + 2)) { + base_id = TOKEN_ID(coeff_type, VP8EncBands[n], 1); // ctx=1 + s = res->stats[VP8EncBands[n]][1]; + } else { + if (!AddToken(tokens, v > 4, base_id + 3, s + 3)) { + if (AddToken(tokens, v != 2, base_id + 4, s + 4)) { + AddToken(tokens, v == 4, base_id + 5, s + 5); + } + } else if (!AddToken(tokens, v > 10, base_id + 6, s + 6)) { + if (!AddToken(tokens, v > 6, base_id + 7, s + 7)) { + AddConstantToken(tokens, v == 6, 159); + } else { + AddConstantToken(tokens, v >= 9, 165); + AddConstantToken(tokens, !(v & 1), 145); + } + } else { + int mask; + const uint8_t* tab; + uint32_t residue = v - 3; + if (residue < (8 << 1)) { // VP8Cat3 (3b) + AddToken(tokens, 0, base_id + 8, s + 8); + AddToken(tokens, 0, base_id + 9, s + 9); + residue -= (8 << 0); + mask = 1 << 2; + tab = VP8Cat3; + } else if (residue < (8 << 2)) { // VP8Cat4 (4b) + AddToken(tokens, 0, base_id + 8, s + 8); + AddToken(tokens, 1, base_id + 9, s + 9); + residue -= (8 << 1); + mask = 1 << 3; + tab = VP8Cat4; + } else if (residue < (8 << 3)) { // VP8Cat5 (5b) + AddToken(tokens, 1, base_id + 8, s + 8); + AddToken(tokens, 0, base_id + 10, s + 9); + residue -= (8 << 2); + mask = 1 << 4; + tab = VP8Cat5; + } else { // VP8Cat6 (11b) + AddToken(tokens, 1, base_id + 8, s + 8); + AddToken(tokens, 1, base_id + 10, s + 9); + residue -= (8 << 3); + mask = 1 << 10; + tab = VP8Cat6; + } + while (mask) { + AddConstantToken(tokens, !!(residue & mask), *tab++); + mask >>= 1; + } + } + base_id = TOKEN_ID(coeff_type, VP8EncBands[n], 2); // ctx=2 + s = res->stats[VP8EncBands[n]][2]; + } + AddConstantToken(tokens, sign, 128); + if (n == 16 || !AddToken(tokens, n <= last, base_id + 0, s + 0)) { + return 1; // EOB + } + } + return 1; +} + +#undef TOKEN_ID + +//------------------------------------------------------------------------------ +// This function works, but isn't currently used. Saved for later. + +#if 0 + +static void Record(int bit, proba_t* const stats) { + proba_t p = *stats; + if (p >= 0xffff0000u) { // an overflow is inbound. + p = ((p + 1u) >> 1) & 0x7fff7fffu; // -> divide the stats by 2. + } + // record bit count (lower 16 bits) and increment total count (upper 16 bits). + p += 0x00010000u + bit; + *stats = p; +} + +void VP8TokenToStats(const VP8TBuffer* const b, proba_t* const stats) { + const VP8Tokens* p = b->pages_; + while (p != NULL) { + const int N = (p->next_ == NULL) ? b->left_ : 0; + int n = MAX_NUM_TOKEN; + const token_t* const tokens = TOKEN_DATA(p); + while (n-- > N) { + const token_t token = tokens[n]; + if (!(token & FIXED_PROBA_BIT)) { + Record((token >> 15) & 1, stats + (token & 0x3fffu)); + } + } + p = p->next_; + } +} + +#endif // 0 + +//------------------------------------------------------------------------------ +// Final coding pass, with known probabilities + +int VP8EmitTokens(VP8TBuffer* const b, VP8BitWriter* const bw, + const uint8_t* const probas, int final_pass) { + const VP8Tokens* p = b->pages_; + assert(!b->error_); + while (p != NULL) { + const VP8Tokens* const next = p->next_; + const int N = (next == NULL) ? b->left_ : 0; + int n = b->page_size_; + const token_t* const tokens = TOKEN_DATA(p); + while (n-- > N) { + const token_t token = tokens[n]; + const int bit = (token >> 15) & 1; + if (token & FIXED_PROBA_BIT) { + VP8PutBit(bw, bit, token & 0xffu); // constant proba + } else { + VP8PutBit(bw, bit, probas[token & 0x3fffu]); + } + } + if (final_pass) WebPSafeFree((void*)p); + p = next; + } + if (final_pass) b->pages_ = NULL; + return 1; +} + +// Size estimation +size_t VP8EstimateTokenSize(VP8TBuffer* const b, const uint8_t* const probas) { + size_t size = 0; + const VP8Tokens* p = b->pages_; + assert(!b->error_); + while (p != NULL) { + const VP8Tokens* const next = p->next_; + const int N = (next == NULL) ? b->left_ : 0; + int n = b->page_size_; + const token_t* const tokens = TOKEN_DATA(p); + while (n-- > N) { + const token_t token = tokens[n]; + const int bit = token & (1 << 15); + if (token & FIXED_PROBA_BIT) { + size += VP8BitCost(bit, token & 0xffu); + } else { + size += VP8BitCost(bit, probas[token & 0x3fffu]); + } + } + p = next; + } + return size; +} + +//------------------------------------------------------------------------------ + +#else // DISABLE_TOKEN_BUFFER + +void VP8TBufferInit(VP8TBuffer* const b) { + (void)b; +} +void VP8TBufferClear(VP8TBuffer* const b) { + (void)b; +} + +#endif // !DISABLE_TOKEN_BUFFER + diff --git a/thirdparty/libwebp/enc/tree.c b/thirdparty/libwebp/enc/tree.c deleted file mode 100644 index f141006d19..0000000000 --- a/thirdparty/libwebp/enc/tree.c +++ /dev/null @@ -1,504 +0,0 @@ -// Copyright 2011 Google Inc. All Rights Reserved. -// -// Use of this source code is governed by a BSD-style license -// that can be found in the COPYING file in the root of the source -// tree. An additional intellectual property rights grant can be found -// in the file PATENTS. All contributing project authors may -// be found in the AUTHORS file in the root of the source tree. -// ----------------------------------------------------------------------------- -// -// Coding of token probabilities, intra modes and segments. -// -// Author: Skal (pascal.massimino@gmail.com) - -#include "./vp8enci.h" - -//------------------------------------------------------------------------------ -// Default probabilities - -// Paragraph 13.5 -const uint8_t - VP8CoeffsProba0[NUM_TYPES][NUM_BANDS][NUM_CTX][NUM_PROBAS] = { - { { { 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128 }, - { 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128 }, - { 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128 } - }, - { { 253, 136, 254, 255, 228, 219, 128, 128, 128, 128, 128 }, - { 189, 129, 242, 255, 227, 213, 255, 219, 128, 128, 128 }, - { 106, 126, 227, 252, 214, 209, 255, 255, 128, 128, 128 } - }, - { { 1, 98, 248, 255, 236, 226, 255, 255, 128, 128, 128 }, - { 181, 133, 238, 254, 221, 234, 255, 154, 128, 128, 128 }, - { 78, 134, 202, 247, 198, 180, 255, 219, 128, 128, 128 }, - }, - { { 1, 185, 249, 255, 243, 255, 128, 128, 128, 128, 128 }, - { 184, 150, 247, 255, 236, 224, 128, 128, 128, 128, 128 }, - { 77, 110, 216, 255, 236, 230, 128, 128, 128, 128, 128 }, - }, - { { 1, 101, 251, 255, 241, 255, 128, 128, 128, 128, 128 }, - { 170, 139, 241, 252, 236, 209, 255, 255, 128, 128, 128 }, - { 37, 116, 196, 243, 228, 255, 255, 255, 128, 128, 128 } - }, - { { 1, 204, 254, 255, 245, 255, 128, 128, 128, 128, 128 }, - { 207, 160, 250, 255, 238, 128, 128, 128, 128, 128, 128 }, - { 102, 103, 231, 255, 211, 171, 128, 128, 128, 128, 128 } - }, - { { 1, 152, 252, 255, 240, 255, 128, 128, 128, 128, 128 }, - { 177, 135, 243, 255, 234, 225, 128, 128, 128, 128, 128 }, - { 80, 129, 211, 255, 194, 224, 128, 128, 128, 128, 128 } - }, - { { 1, 1, 255, 128, 128, 128, 128, 128, 128, 128, 128 }, - { 246, 1, 255, 128, 128, 128, 128, 128, 128, 128, 128 }, - { 255, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128 } - } - }, - { { { 198, 35, 237, 223, 193, 187, 162, 160, 145, 155, 62 }, - { 131, 45, 198, 221, 172, 176, 220, 157, 252, 221, 1 }, - { 68, 47, 146, 208, 149, 167, 221, 162, 255, 223, 128 } - }, - { { 1, 149, 241, 255, 221, 224, 255, 255, 128, 128, 128 }, - { 184, 141, 234, 253, 222, 220, 255, 199, 128, 128, 128 }, - { 81, 99, 181, 242, 176, 190, 249, 202, 255, 255, 128 } - }, - { { 1, 129, 232, 253, 214, 197, 242, 196, 255, 255, 128 }, - { 99, 121, 210, 250, 201, 198, 255, 202, 128, 128, 128 }, - { 23, 91, 163, 242, 170, 187, 247, 210, 255, 255, 128 } - }, - { { 1, 200, 246, 255, 234, 255, 128, 128, 128, 128, 128 }, - { 109, 178, 241, 255, 231, 245, 255, 255, 128, 128, 128 }, - { 44, 130, 201, 253, 205, 192, 255, 255, 128, 128, 128 } - }, - { { 1, 132, 239, 251, 219, 209, 255, 165, 128, 128, 128 }, - { 94, 136, 225, 251, 218, 190, 255, 255, 128, 128, 128 }, - { 22, 100, 174, 245, 186, 161, 255, 199, 128, 128, 128 } - }, - { { 1, 182, 249, 255, 232, 235, 128, 128, 128, 128, 128 }, - { 124, 143, 241, 255, 227, 234, 128, 128, 128, 128, 128 }, - { 35, 77, 181, 251, 193, 211, 255, 205, 128, 128, 128 } - }, - { { 1, 157, 247, 255, 236, 231, 255, 255, 128, 128, 128 }, - { 121, 141, 235, 255, 225, 227, 255, 255, 128, 128, 128 }, - { 45, 99, 188, 251, 195, 217, 255, 224, 128, 128, 128 } - }, - { { 1, 1, 251, 255, 213, 255, 128, 128, 128, 128, 128 }, - { 203, 1, 248, 255, 255, 128, 128, 128, 128, 128, 128 }, - { 137, 1, 177, 255, 224, 255, 128, 128, 128, 128, 128 } - } - }, - { { { 253, 9, 248, 251, 207, 208, 255, 192, 128, 128, 128 }, - { 175, 13, 224, 243, 193, 185, 249, 198, 255, 255, 128 }, - { 73, 17, 171, 221, 161, 179, 236, 167, 255, 234, 128 } - }, - { { 1, 95, 247, 253, 212, 183, 255, 255, 128, 128, 128 }, - { 239, 90, 244, 250, 211, 209, 255, 255, 128, 128, 128 }, - { 155, 77, 195, 248, 188, 195, 255, 255, 128, 128, 128 } - }, - { { 1, 24, 239, 251, 218, 219, 255, 205, 128, 128, 128 }, - { 201, 51, 219, 255, 196, 186, 128, 128, 128, 128, 128 }, - { 69, 46, 190, 239, 201, 218, 255, 228, 128, 128, 128 } - }, - { { 1, 191, 251, 255, 255, 128, 128, 128, 128, 128, 128 }, - { 223, 165, 249, 255, 213, 255, 128, 128, 128, 128, 128 }, - { 141, 124, 248, 255, 255, 128, 128, 128, 128, 128, 128 } - }, - { { 1, 16, 248, 255, 255, 128, 128, 128, 128, 128, 128 }, - { 190, 36, 230, 255, 236, 255, 128, 128, 128, 128, 128 }, - { 149, 1, 255, 128, 128, 128, 128, 128, 128, 128, 128 } - }, - { { 1, 226, 255, 128, 128, 128, 128, 128, 128, 128, 128 }, - { 247, 192, 255, 128, 128, 128, 128, 128, 128, 128, 128 }, - { 240, 128, 255, 128, 128, 128, 128, 128, 128, 128, 128 } - }, - { { 1, 134, 252, 255, 255, 128, 128, 128, 128, 128, 128 }, - { 213, 62, 250, 255, 255, 128, 128, 128, 128, 128, 128 }, - { 55, 93, 255, 128, 128, 128, 128, 128, 128, 128, 128 } - }, - { { 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128 }, - { 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128 }, - { 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128 } - } - }, - { { { 202, 24, 213, 235, 186, 191, 220, 160, 240, 175, 255 }, - { 126, 38, 182, 232, 169, 184, 228, 174, 255, 187, 128 }, - { 61, 46, 138, 219, 151, 178, 240, 170, 255, 216, 128 } - }, - { { 1, 112, 230, 250, 199, 191, 247, 159, 255, 255, 128 }, - { 166, 109, 228, 252, 211, 215, 255, 174, 128, 128, 128 }, - { 39, 77, 162, 232, 172, 180, 245, 178, 255, 255, 128 } - }, - { { 1, 52, 220, 246, 198, 199, 249, 220, 255, 255, 128 }, - { 124, 74, 191, 243, 183, 193, 250, 221, 255, 255, 128 }, - { 24, 71, 130, 219, 154, 170, 243, 182, 255, 255, 128 } - }, - { { 1, 182, 225, 249, 219, 240, 255, 224, 128, 128, 128 }, - { 149, 150, 226, 252, 216, 205, 255, 171, 128, 128, 128 }, - { 28, 108, 170, 242, 183, 194, 254, 223, 255, 255, 128 } - }, - { { 1, 81, 230, 252, 204, 203, 255, 192, 128, 128, 128 }, - { 123, 102, 209, 247, 188, 196, 255, 233, 128, 128, 128 }, - { 20, 95, 153, 243, 164, 173, 255, 203, 128, 128, 128 } - }, - { { 1, 222, 248, 255, 216, 213, 128, 128, 128, 128, 128 }, - { 168, 175, 246, 252, 235, 205, 255, 255, 128, 128, 128 }, - { 47, 116, 215, 255, 211, 212, 255, 255, 128, 128, 128 } - }, - { { 1, 121, 236, 253, 212, 214, 255, 255, 128, 128, 128 }, - { 141, 84, 213, 252, 201, 202, 255, 219, 128, 128, 128 }, - { 42, 80, 160, 240, 162, 185, 255, 205, 128, 128, 128 } - }, - { { 1, 1, 255, 128, 128, 128, 128, 128, 128, 128, 128 }, - { 244, 1, 255, 128, 128, 128, 128, 128, 128, 128, 128 }, - { 238, 1, 255, 128, 128, 128, 128, 128, 128, 128, 128 } - } - } -}; - -void VP8DefaultProbas(VP8Encoder* const enc) { - VP8EncProba* const probas = &enc->proba_; - probas->use_skip_proba_ = 0; - memset(probas->segments_, 255u, sizeof(probas->segments_)); - memcpy(probas->coeffs_, VP8CoeffsProba0, sizeof(VP8CoeffsProba0)); - // Note: we could hard-code the level_costs_ corresponding to VP8CoeffsProba0, - // but that's ~11k of static data. Better call VP8CalculateLevelCosts() later. - probas->dirty_ = 1; -} - -// Paragraph 11.5. 900bytes. -static const uint8_t kBModesProba[NUM_BMODES][NUM_BMODES][NUM_BMODES - 1] = { - { { 231, 120, 48, 89, 115, 113, 120, 152, 112 }, - { 152, 179, 64, 126, 170, 118, 46, 70, 95 }, - { 175, 69, 143, 80, 85, 82, 72, 155, 103 }, - { 56, 58, 10, 171, 218, 189, 17, 13, 152 }, - { 114, 26, 17, 163, 44, 195, 21, 10, 173 }, - { 121, 24, 80, 195, 26, 62, 44, 64, 85 }, - { 144, 71, 10, 38, 171, 213, 144, 34, 26 }, - { 170, 46, 55, 19, 136, 160, 33, 206, 71 }, - { 63, 20, 8, 114, 114, 208, 12, 9, 226 }, - { 81, 40, 11, 96, 182, 84, 29, 16, 36 } }, - { { 134, 183, 89, 137, 98, 101, 106, 165, 148 }, - { 72, 187, 100, 130, 157, 111, 32, 75, 80 }, - { 66, 102, 167, 99, 74, 62, 40, 234, 128 }, - { 41, 53, 9, 178, 241, 141, 26, 8, 107 }, - { 74, 43, 26, 146, 73, 166, 49, 23, 157 }, - { 65, 38, 105, 160, 51, 52, 31, 115, 128 }, - { 104, 79, 12, 27, 217, 255, 87, 17, 7 }, - { 87, 68, 71, 44, 114, 51, 15, 186, 23 }, - { 47, 41, 14, 110, 182, 183, 21, 17, 194 }, - { 66, 45, 25, 102, 197, 189, 23, 18, 22 } }, - { { 88, 88, 147, 150, 42, 46, 45, 196, 205 }, - { 43, 97, 183, 117, 85, 38, 35, 179, 61 }, - { 39, 53, 200, 87, 26, 21, 43, 232, 171 }, - { 56, 34, 51, 104, 114, 102, 29, 93, 77 }, - { 39, 28, 85, 171, 58, 165, 90, 98, 64 }, - { 34, 22, 116, 206, 23, 34, 43, 166, 73 }, - { 107, 54, 32, 26, 51, 1, 81, 43, 31 }, - { 68, 25, 106, 22, 64, 171, 36, 225, 114 }, - { 34, 19, 21, 102, 132, 188, 16, 76, 124 }, - { 62, 18, 78, 95, 85, 57, 50, 48, 51 } }, - { { 193, 101, 35, 159, 215, 111, 89, 46, 111 }, - { 60, 148, 31, 172, 219, 228, 21, 18, 111 }, - { 112, 113, 77, 85, 179, 255, 38, 120, 114 }, - { 40, 42, 1, 196, 245, 209, 10, 25, 109 }, - { 88, 43, 29, 140, 166, 213, 37, 43, 154 }, - { 61, 63, 30, 155, 67, 45, 68, 1, 209 }, - { 100, 80, 8, 43, 154, 1, 51, 26, 71 }, - { 142, 78, 78, 16, 255, 128, 34, 197, 171 }, - { 41, 40, 5, 102, 211, 183, 4, 1, 221 }, - { 51, 50, 17, 168, 209, 192, 23, 25, 82 } }, - { { 138, 31, 36, 171, 27, 166, 38, 44, 229 }, - { 67, 87, 58, 169, 82, 115, 26, 59, 179 }, - { 63, 59, 90, 180, 59, 166, 93, 73, 154 }, - { 40, 40, 21, 116, 143, 209, 34, 39, 175 }, - { 47, 15, 16, 183, 34, 223, 49, 45, 183 }, - { 46, 17, 33, 183, 6, 98, 15, 32, 183 }, - { 57, 46, 22, 24, 128, 1, 54, 17, 37 }, - { 65, 32, 73, 115, 28, 128, 23, 128, 205 }, - { 40, 3, 9, 115, 51, 192, 18, 6, 223 }, - { 87, 37, 9, 115, 59, 77, 64, 21, 47 } }, - { { 104, 55, 44, 218, 9, 54, 53, 130, 226 }, - { 64, 90, 70, 205, 40, 41, 23, 26, 57 }, - { 54, 57, 112, 184, 5, 41, 38, 166, 213 }, - { 30, 34, 26, 133, 152, 116, 10, 32, 134 }, - { 39, 19, 53, 221, 26, 114, 32, 73, 255 }, - { 31, 9, 65, 234, 2, 15, 1, 118, 73 }, - { 75, 32, 12, 51, 192, 255, 160, 43, 51 }, - { 88, 31, 35, 67, 102, 85, 55, 186, 85 }, - { 56, 21, 23, 111, 59, 205, 45, 37, 192 }, - { 55, 38, 70, 124, 73, 102, 1, 34, 98 } }, - { { 125, 98, 42, 88, 104, 85, 117, 175, 82 }, - { 95, 84, 53, 89, 128, 100, 113, 101, 45 }, - { 75, 79, 123, 47, 51, 128, 81, 171, 1 }, - { 57, 17, 5, 71, 102, 57, 53, 41, 49 }, - { 38, 33, 13, 121, 57, 73, 26, 1, 85 }, - { 41, 10, 67, 138, 77, 110, 90, 47, 114 }, - { 115, 21, 2, 10, 102, 255, 166, 23, 6 }, - { 101, 29, 16, 10, 85, 128, 101, 196, 26 }, - { 57, 18, 10, 102, 102, 213, 34, 20, 43 }, - { 117, 20, 15, 36, 163, 128, 68, 1, 26 } }, - { { 102, 61, 71, 37, 34, 53, 31, 243, 192 }, - { 69, 60, 71, 38, 73, 119, 28, 222, 37 }, - { 68, 45, 128, 34, 1, 47, 11, 245, 171 }, - { 62, 17, 19, 70, 146, 85, 55, 62, 70 }, - { 37, 43, 37, 154, 100, 163, 85, 160, 1 }, - { 63, 9, 92, 136, 28, 64, 32, 201, 85 }, - { 75, 15, 9, 9, 64, 255, 184, 119, 16 }, - { 86, 6, 28, 5, 64, 255, 25, 248, 1 }, - { 56, 8, 17, 132, 137, 255, 55, 116, 128 }, - { 58, 15, 20, 82, 135, 57, 26, 121, 40 } }, - { { 164, 50, 31, 137, 154, 133, 25, 35, 218 }, - { 51, 103, 44, 131, 131, 123, 31, 6, 158 }, - { 86, 40, 64, 135, 148, 224, 45, 183, 128 }, - { 22, 26, 17, 131, 240, 154, 14, 1, 209 }, - { 45, 16, 21, 91, 64, 222, 7, 1, 197 }, - { 56, 21, 39, 155, 60, 138, 23, 102, 213 }, - { 83, 12, 13, 54, 192, 255, 68, 47, 28 }, - { 85, 26, 85, 85, 128, 128, 32, 146, 171 }, - { 18, 11, 7, 63, 144, 171, 4, 4, 246 }, - { 35, 27, 10, 146, 174, 171, 12, 26, 128 } }, - { { 190, 80, 35, 99, 180, 80, 126, 54, 45 }, - { 85, 126, 47, 87, 176, 51, 41, 20, 32 }, - { 101, 75, 128, 139, 118, 146, 116, 128, 85 }, - { 56, 41, 15, 176, 236, 85, 37, 9, 62 }, - { 71, 30, 17, 119, 118, 255, 17, 18, 138 }, - { 101, 38, 60, 138, 55, 70, 43, 26, 142 }, - { 146, 36, 19, 30, 171, 255, 97, 27, 20 }, - { 138, 45, 61, 62, 219, 1, 81, 188, 64 }, - { 32, 41, 20, 117, 151, 142, 20, 21, 163 }, - { 112, 19, 12, 61, 195, 128, 48, 4, 24 } } -}; - -static int PutI4Mode(VP8BitWriter* const bw, int mode, - const uint8_t* const prob) { - if (VP8PutBit(bw, mode != B_DC_PRED, prob[0])) { - if (VP8PutBit(bw, mode != B_TM_PRED, prob[1])) { - if (VP8PutBit(bw, mode != B_VE_PRED, prob[2])) { - if (!VP8PutBit(bw, mode >= B_LD_PRED, prob[3])) { - if (VP8PutBit(bw, mode != B_HE_PRED, prob[4])) { - VP8PutBit(bw, mode != B_RD_PRED, prob[5]); - } - } else { - if (VP8PutBit(bw, mode != B_LD_PRED, prob[6])) { - if (VP8PutBit(bw, mode != B_VL_PRED, prob[7])) { - VP8PutBit(bw, mode != B_HD_PRED, prob[8]); - } - } - } - } - } - } - return mode; -} - -static void PutI16Mode(VP8BitWriter* const bw, int mode) { - if (VP8PutBit(bw, (mode == TM_PRED || mode == H_PRED), 156)) { - VP8PutBit(bw, mode == TM_PRED, 128); // TM or HE - } else { - VP8PutBit(bw, mode == V_PRED, 163); // VE or DC - } -} - -static void PutUVMode(VP8BitWriter* const bw, int uv_mode) { - if (VP8PutBit(bw, uv_mode != DC_PRED, 142)) { - if (VP8PutBit(bw, uv_mode != V_PRED, 114)) { - VP8PutBit(bw, uv_mode != H_PRED, 183); // else: TM_PRED - } - } -} - -static void PutSegment(VP8BitWriter* const bw, int s, const uint8_t* p) { - if (VP8PutBit(bw, s >= 2, p[0])) p += 1; - VP8PutBit(bw, s & 1, p[1]); -} - -void VP8CodeIntraModes(VP8Encoder* const enc) { - VP8BitWriter* const bw = &enc->bw_; - VP8EncIterator it; - VP8IteratorInit(enc, &it); - do { - const VP8MBInfo* const mb = it.mb_; - const uint8_t* preds = it.preds_; - if (enc->segment_hdr_.update_map_) { - PutSegment(bw, mb->segment_, enc->proba_.segments_); - } - if (enc->proba_.use_skip_proba_) { - VP8PutBit(bw, mb->skip_, enc->proba_.skip_proba_); - } - if (VP8PutBit(bw, (mb->type_ != 0), 145)) { // i16x16 - PutI16Mode(bw, preds[0]); - } else { - const int preds_w = enc->preds_w_; - const uint8_t* top_pred = preds - preds_w; - int x, y; - for (y = 0; y < 4; ++y) { - int left = preds[-1]; - for (x = 0; x < 4; ++x) { - const uint8_t* const probas = kBModesProba[top_pred[x]][left]; - left = PutI4Mode(bw, preds[x], probas); - } - top_pred = preds; - preds += preds_w; - } - } - PutUVMode(bw, mb->uv_mode_); - } while (VP8IteratorNext(&it)); -} - -//------------------------------------------------------------------------------ -// Paragraph 13 - -const uint8_t - VP8CoeffsUpdateProba[NUM_TYPES][NUM_BANDS][NUM_CTX][NUM_PROBAS] = { - { { { 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 }, - { 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 }, - { 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 } - }, - { { 176, 246, 255, 255, 255, 255, 255, 255, 255, 255, 255 }, - { 223, 241, 252, 255, 255, 255, 255, 255, 255, 255, 255 }, - { 249, 253, 253, 255, 255, 255, 255, 255, 255, 255, 255 } - }, - { { 255, 244, 252, 255, 255, 255, 255, 255, 255, 255, 255 }, - { 234, 254, 254, 255, 255, 255, 255, 255, 255, 255, 255 }, - { 253, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 } - }, - { { 255, 246, 254, 255, 255, 255, 255, 255, 255, 255, 255 }, - { 239, 253, 254, 255, 255, 255, 255, 255, 255, 255, 255 }, - { 254, 255, 254, 255, 255, 255, 255, 255, 255, 255, 255 } - }, - { { 255, 248, 254, 255, 255, 255, 255, 255, 255, 255, 255 }, - { 251, 255, 254, 255, 255, 255, 255, 255, 255, 255, 255 }, - { 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 } - }, - { { 255, 253, 254, 255, 255, 255, 255, 255, 255, 255, 255 }, - { 251, 254, 254, 255, 255, 255, 255, 255, 255, 255, 255 }, - { 254, 255, 254, 255, 255, 255, 255, 255, 255, 255, 255 } - }, - { { 255, 254, 253, 255, 254, 255, 255, 255, 255, 255, 255 }, - { 250, 255, 254, 255, 254, 255, 255, 255, 255, 255, 255 }, - { 254, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 } - }, - { { 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 }, - { 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 }, - { 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 } - } - }, - { { { 217, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 }, - { 225, 252, 241, 253, 255, 255, 254, 255, 255, 255, 255 }, - { 234, 250, 241, 250, 253, 255, 253, 254, 255, 255, 255 } - }, - { { 255, 254, 255, 255, 255, 255, 255, 255, 255, 255, 255 }, - { 223, 254, 254, 255, 255, 255, 255, 255, 255, 255, 255 }, - { 238, 253, 254, 254, 255, 255, 255, 255, 255, 255, 255 } - }, - { { 255, 248, 254, 255, 255, 255, 255, 255, 255, 255, 255 }, - { 249, 254, 255, 255, 255, 255, 255, 255, 255, 255, 255 }, - { 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 } - }, - { { 255, 253, 255, 255, 255, 255, 255, 255, 255, 255, 255 }, - { 247, 254, 255, 255, 255, 255, 255, 255, 255, 255, 255 }, - { 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 } - }, - { { 255, 253, 254, 255, 255, 255, 255, 255, 255, 255, 255 }, - { 252, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 }, - { 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 } - }, - { { 255, 254, 254, 255, 255, 255, 255, 255, 255, 255, 255 }, - { 253, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 }, - { 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 } - }, - { { 255, 254, 253, 255, 255, 255, 255, 255, 255, 255, 255 }, - { 250, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 }, - { 254, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 } - }, - { { 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 }, - { 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 }, - { 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 } - } - }, - { { { 186, 251, 250, 255, 255, 255, 255, 255, 255, 255, 255 }, - { 234, 251, 244, 254, 255, 255, 255, 255, 255, 255, 255 }, - { 251, 251, 243, 253, 254, 255, 254, 255, 255, 255, 255 } - }, - { { 255, 253, 254, 255, 255, 255, 255, 255, 255, 255, 255 }, - { 236, 253, 254, 255, 255, 255, 255, 255, 255, 255, 255 }, - { 251, 253, 253, 254, 254, 255, 255, 255, 255, 255, 255 } - }, - { { 255, 254, 254, 255, 255, 255, 255, 255, 255, 255, 255 }, - { 254, 254, 254, 255, 255, 255, 255, 255, 255, 255, 255 }, - { 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 } - }, - { { 255, 254, 255, 255, 255, 255, 255, 255, 255, 255, 255 }, - { 254, 254, 255, 255, 255, 255, 255, 255, 255, 255, 255 }, - { 254, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 } - }, - { { 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 }, - { 254, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 }, - { 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 } - }, - { { 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 }, - { 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 }, - { 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 } - }, - { { 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 }, - { 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 }, - { 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 } - }, - { { 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 }, - { 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 }, - { 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 } - } - }, - { { { 248, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 }, - { 250, 254, 252, 254, 255, 255, 255, 255, 255, 255, 255 }, - { 248, 254, 249, 253, 255, 255, 255, 255, 255, 255, 255 } - }, - { { 255, 253, 253, 255, 255, 255, 255, 255, 255, 255, 255 }, - { 246, 253, 253, 255, 255, 255, 255, 255, 255, 255, 255 }, - { 252, 254, 251, 254, 254, 255, 255, 255, 255, 255, 255 } - }, - { { 255, 254, 252, 255, 255, 255, 255, 255, 255, 255, 255 }, - { 248, 254, 253, 255, 255, 255, 255, 255, 255, 255, 255 }, - { 253, 255, 254, 254, 255, 255, 255, 255, 255, 255, 255 } - }, - { { 255, 251, 254, 255, 255, 255, 255, 255, 255, 255, 255 }, - { 245, 251, 254, 255, 255, 255, 255, 255, 255, 255, 255 }, - { 253, 253, 254, 255, 255, 255, 255, 255, 255, 255, 255 } - }, - { { 255, 251, 253, 255, 255, 255, 255, 255, 255, 255, 255 }, - { 252, 253, 254, 255, 255, 255, 255, 255, 255, 255, 255 }, - { 255, 254, 255, 255, 255, 255, 255, 255, 255, 255, 255 } - }, - { { 255, 252, 255, 255, 255, 255, 255, 255, 255, 255, 255 }, - { 249, 255, 254, 255, 255, 255, 255, 255, 255, 255, 255 }, - { 255, 255, 254, 255, 255, 255, 255, 255, 255, 255, 255 } - }, - { { 255, 255, 253, 255, 255, 255, 255, 255, 255, 255, 255 }, - { 250, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 }, - { 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 } - }, - { { 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 }, - { 254, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 }, - { 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 } - } - } -}; - -void VP8WriteProbas(VP8BitWriter* const bw, const VP8EncProba* const probas) { - int t, b, c, p; - for (t = 0; t < NUM_TYPES; ++t) { - for (b = 0; b < NUM_BANDS; ++b) { - for (c = 0; c < NUM_CTX; ++c) { - for (p = 0; p < NUM_PROBAS; ++p) { - const uint8_t p0 = probas->coeffs_[t][b][c][p]; - const int update = (p0 != VP8CoeffsProba0[t][b][c][p]); - if (VP8PutBit(bw, update, VP8CoeffsUpdateProba[t][b][c][p])) { - VP8PutBits(bw, p0, 8); - } - } - } - } - } - if (VP8PutBitUniform(bw, probas->use_skip_proba_)) { - VP8PutBits(bw, probas->skip_proba_, 8); - } -} - diff --git a/thirdparty/libwebp/enc/tree_enc.c b/thirdparty/libwebp/enc/tree_enc.c new file mode 100644 index 0000000000..2c40fe7f3d --- /dev/null +++ b/thirdparty/libwebp/enc/tree_enc.c @@ -0,0 +1,504 @@ +// Copyright 2011 Google Inc. All Rights Reserved. +// +// Use of this source code is governed by a BSD-style license +// that can be found in the COPYING file in the root of the source +// tree. An additional intellectual property rights grant can be found +// in the file PATENTS. All contributing project authors may +// be found in the AUTHORS file in the root of the source tree. +// ----------------------------------------------------------------------------- +// +// Coding of token probabilities, intra modes and segments. +// +// Author: Skal (pascal.massimino@gmail.com) + +#include "./vp8i_enc.h" + +//------------------------------------------------------------------------------ +// Default probabilities + +// Paragraph 13.5 +const uint8_t + VP8CoeffsProba0[NUM_TYPES][NUM_BANDS][NUM_CTX][NUM_PROBAS] = { + { { { 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128 }, + { 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128 }, + { 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128 } + }, + { { 253, 136, 254, 255, 228, 219, 128, 128, 128, 128, 128 }, + { 189, 129, 242, 255, 227, 213, 255, 219, 128, 128, 128 }, + { 106, 126, 227, 252, 214, 209, 255, 255, 128, 128, 128 } + }, + { { 1, 98, 248, 255, 236, 226, 255, 255, 128, 128, 128 }, + { 181, 133, 238, 254, 221, 234, 255, 154, 128, 128, 128 }, + { 78, 134, 202, 247, 198, 180, 255, 219, 128, 128, 128 }, + }, + { { 1, 185, 249, 255, 243, 255, 128, 128, 128, 128, 128 }, + { 184, 150, 247, 255, 236, 224, 128, 128, 128, 128, 128 }, + { 77, 110, 216, 255, 236, 230, 128, 128, 128, 128, 128 }, + }, + { { 1, 101, 251, 255, 241, 255, 128, 128, 128, 128, 128 }, + { 170, 139, 241, 252, 236, 209, 255, 255, 128, 128, 128 }, + { 37, 116, 196, 243, 228, 255, 255, 255, 128, 128, 128 } + }, + { { 1, 204, 254, 255, 245, 255, 128, 128, 128, 128, 128 }, + { 207, 160, 250, 255, 238, 128, 128, 128, 128, 128, 128 }, + { 102, 103, 231, 255, 211, 171, 128, 128, 128, 128, 128 } + }, + { { 1, 152, 252, 255, 240, 255, 128, 128, 128, 128, 128 }, + { 177, 135, 243, 255, 234, 225, 128, 128, 128, 128, 128 }, + { 80, 129, 211, 255, 194, 224, 128, 128, 128, 128, 128 } + }, + { { 1, 1, 255, 128, 128, 128, 128, 128, 128, 128, 128 }, + { 246, 1, 255, 128, 128, 128, 128, 128, 128, 128, 128 }, + { 255, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128 } + } + }, + { { { 198, 35, 237, 223, 193, 187, 162, 160, 145, 155, 62 }, + { 131, 45, 198, 221, 172, 176, 220, 157, 252, 221, 1 }, + { 68, 47, 146, 208, 149, 167, 221, 162, 255, 223, 128 } + }, + { { 1, 149, 241, 255, 221, 224, 255, 255, 128, 128, 128 }, + { 184, 141, 234, 253, 222, 220, 255, 199, 128, 128, 128 }, + { 81, 99, 181, 242, 176, 190, 249, 202, 255, 255, 128 } + }, + { { 1, 129, 232, 253, 214, 197, 242, 196, 255, 255, 128 }, + { 99, 121, 210, 250, 201, 198, 255, 202, 128, 128, 128 }, + { 23, 91, 163, 242, 170, 187, 247, 210, 255, 255, 128 } + }, + { { 1, 200, 246, 255, 234, 255, 128, 128, 128, 128, 128 }, + { 109, 178, 241, 255, 231, 245, 255, 255, 128, 128, 128 }, + { 44, 130, 201, 253, 205, 192, 255, 255, 128, 128, 128 } + }, + { { 1, 132, 239, 251, 219, 209, 255, 165, 128, 128, 128 }, + { 94, 136, 225, 251, 218, 190, 255, 255, 128, 128, 128 }, + { 22, 100, 174, 245, 186, 161, 255, 199, 128, 128, 128 } + }, + { { 1, 182, 249, 255, 232, 235, 128, 128, 128, 128, 128 }, + { 124, 143, 241, 255, 227, 234, 128, 128, 128, 128, 128 }, + { 35, 77, 181, 251, 193, 211, 255, 205, 128, 128, 128 } + }, + { { 1, 157, 247, 255, 236, 231, 255, 255, 128, 128, 128 }, + { 121, 141, 235, 255, 225, 227, 255, 255, 128, 128, 128 }, + { 45, 99, 188, 251, 195, 217, 255, 224, 128, 128, 128 } + }, + { { 1, 1, 251, 255, 213, 255, 128, 128, 128, 128, 128 }, + { 203, 1, 248, 255, 255, 128, 128, 128, 128, 128, 128 }, + { 137, 1, 177, 255, 224, 255, 128, 128, 128, 128, 128 } + } + }, + { { { 253, 9, 248, 251, 207, 208, 255, 192, 128, 128, 128 }, + { 175, 13, 224, 243, 193, 185, 249, 198, 255, 255, 128 }, + { 73, 17, 171, 221, 161, 179, 236, 167, 255, 234, 128 } + }, + { { 1, 95, 247, 253, 212, 183, 255, 255, 128, 128, 128 }, + { 239, 90, 244, 250, 211, 209, 255, 255, 128, 128, 128 }, + { 155, 77, 195, 248, 188, 195, 255, 255, 128, 128, 128 } + }, + { { 1, 24, 239, 251, 218, 219, 255, 205, 128, 128, 128 }, + { 201, 51, 219, 255, 196, 186, 128, 128, 128, 128, 128 }, + { 69, 46, 190, 239, 201, 218, 255, 228, 128, 128, 128 } + }, + { { 1, 191, 251, 255, 255, 128, 128, 128, 128, 128, 128 }, + { 223, 165, 249, 255, 213, 255, 128, 128, 128, 128, 128 }, + { 141, 124, 248, 255, 255, 128, 128, 128, 128, 128, 128 } + }, + { { 1, 16, 248, 255, 255, 128, 128, 128, 128, 128, 128 }, + { 190, 36, 230, 255, 236, 255, 128, 128, 128, 128, 128 }, + { 149, 1, 255, 128, 128, 128, 128, 128, 128, 128, 128 } + }, + { { 1, 226, 255, 128, 128, 128, 128, 128, 128, 128, 128 }, + { 247, 192, 255, 128, 128, 128, 128, 128, 128, 128, 128 }, + { 240, 128, 255, 128, 128, 128, 128, 128, 128, 128, 128 } + }, + { { 1, 134, 252, 255, 255, 128, 128, 128, 128, 128, 128 }, + { 213, 62, 250, 255, 255, 128, 128, 128, 128, 128, 128 }, + { 55, 93, 255, 128, 128, 128, 128, 128, 128, 128, 128 } + }, + { { 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128 }, + { 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128 }, + { 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128 } + } + }, + { { { 202, 24, 213, 235, 186, 191, 220, 160, 240, 175, 255 }, + { 126, 38, 182, 232, 169, 184, 228, 174, 255, 187, 128 }, + { 61, 46, 138, 219, 151, 178, 240, 170, 255, 216, 128 } + }, + { { 1, 112, 230, 250, 199, 191, 247, 159, 255, 255, 128 }, + { 166, 109, 228, 252, 211, 215, 255, 174, 128, 128, 128 }, + { 39, 77, 162, 232, 172, 180, 245, 178, 255, 255, 128 } + }, + { { 1, 52, 220, 246, 198, 199, 249, 220, 255, 255, 128 }, + { 124, 74, 191, 243, 183, 193, 250, 221, 255, 255, 128 }, + { 24, 71, 130, 219, 154, 170, 243, 182, 255, 255, 128 } + }, + { { 1, 182, 225, 249, 219, 240, 255, 224, 128, 128, 128 }, + { 149, 150, 226, 252, 216, 205, 255, 171, 128, 128, 128 }, + { 28, 108, 170, 242, 183, 194, 254, 223, 255, 255, 128 } + }, + { { 1, 81, 230, 252, 204, 203, 255, 192, 128, 128, 128 }, + { 123, 102, 209, 247, 188, 196, 255, 233, 128, 128, 128 }, + { 20, 95, 153, 243, 164, 173, 255, 203, 128, 128, 128 } + }, + { { 1, 222, 248, 255, 216, 213, 128, 128, 128, 128, 128 }, + { 168, 175, 246, 252, 235, 205, 255, 255, 128, 128, 128 }, + { 47, 116, 215, 255, 211, 212, 255, 255, 128, 128, 128 } + }, + { { 1, 121, 236, 253, 212, 214, 255, 255, 128, 128, 128 }, + { 141, 84, 213, 252, 201, 202, 255, 219, 128, 128, 128 }, + { 42, 80, 160, 240, 162, 185, 255, 205, 128, 128, 128 } + }, + { { 1, 1, 255, 128, 128, 128, 128, 128, 128, 128, 128 }, + { 244, 1, 255, 128, 128, 128, 128, 128, 128, 128, 128 }, + { 238, 1, 255, 128, 128, 128, 128, 128, 128, 128, 128 } + } + } +}; + +void VP8DefaultProbas(VP8Encoder* const enc) { + VP8EncProba* const probas = &enc->proba_; + probas->use_skip_proba_ = 0; + memset(probas->segments_, 255u, sizeof(probas->segments_)); + memcpy(probas->coeffs_, VP8CoeffsProba0, sizeof(VP8CoeffsProba0)); + // Note: we could hard-code the level_costs_ corresponding to VP8CoeffsProba0, + // but that's ~11k of static data. Better call VP8CalculateLevelCosts() later. + probas->dirty_ = 1; +} + +// Paragraph 11.5. 900bytes. +static const uint8_t kBModesProba[NUM_BMODES][NUM_BMODES][NUM_BMODES - 1] = { + { { 231, 120, 48, 89, 115, 113, 120, 152, 112 }, + { 152, 179, 64, 126, 170, 118, 46, 70, 95 }, + { 175, 69, 143, 80, 85, 82, 72, 155, 103 }, + { 56, 58, 10, 171, 218, 189, 17, 13, 152 }, + { 114, 26, 17, 163, 44, 195, 21, 10, 173 }, + { 121, 24, 80, 195, 26, 62, 44, 64, 85 }, + { 144, 71, 10, 38, 171, 213, 144, 34, 26 }, + { 170, 46, 55, 19, 136, 160, 33, 206, 71 }, + { 63, 20, 8, 114, 114, 208, 12, 9, 226 }, + { 81, 40, 11, 96, 182, 84, 29, 16, 36 } }, + { { 134, 183, 89, 137, 98, 101, 106, 165, 148 }, + { 72, 187, 100, 130, 157, 111, 32, 75, 80 }, + { 66, 102, 167, 99, 74, 62, 40, 234, 128 }, + { 41, 53, 9, 178, 241, 141, 26, 8, 107 }, + { 74, 43, 26, 146, 73, 166, 49, 23, 157 }, + { 65, 38, 105, 160, 51, 52, 31, 115, 128 }, + { 104, 79, 12, 27, 217, 255, 87, 17, 7 }, + { 87, 68, 71, 44, 114, 51, 15, 186, 23 }, + { 47, 41, 14, 110, 182, 183, 21, 17, 194 }, + { 66, 45, 25, 102, 197, 189, 23, 18, 22 } }, + { { 88, 88, 147, 150, 42, 46, 45, 196, 205 }, + { 43, 97, 183, 117, 85, 38, 35, 179, 61 }, + { 39, 53, 200, 87, 26, 21, 43, 232, 171 }, + { 56, 34, 51, 104, 114, 102, 29, 93, 77 }, + { 39, 28, 85, 171, 58, 165, 90, 98, 64 }, + { 34, 22, 116, 206, 23, 34, 43, 166, 73 }, + { 107, 54, 32, 26, 51, 1, 81, 43, 31 }, + { 68, 25, 106, 22, 64, 171, 36, 225, 114 }, + { 34, 19, 21, 102, 132, 188, 16, 76, 124 }, + { 62, 18, 78, 95, 85, 57, 50, 48, 51 } }, + { { 193, 101, 35, 159, 215, 111, 89, 46, 111 }, + { 60, 148, 31, 172, 219, 228, 21, 18, 111 }, + { 112, 113, 77, 85, 179, 255, 38, 120, 114 }, + { 40, 42, 1, 196, 245, 209, 10, 25, 109 }, + { 88, 43, 29, 140, 166, 213, 37, 43, 154 }, + { 61, 63, 30, 155, 67, 45, 68, 1, 209 }, + { 100, 80, 8, 43, 154, 1, 51, 26, 71 }, + { 142, 78, 78, 16, 255, 128, 34, 197, 171 }, + { 41, 40, 5, 102, 211, 183, 4, 1, 221 }, + { 51, 50, 17, 168, 209, 192, 23, 25, 82 } }, + { { 138, 31, 36, 171, 27, 166, 38, 44, 229 }, + { 67, 87, 58, 169, 82, 115, 26, 59, 179 }, + { 63, 59, 90, 180, 59, 166, 93, 73, 154 }, + { 40, 40, 21, 116, 143, 209, 34, 39, 175 }, + { 47, 15, 16, 183, 34, 223, 49, 45, 183 }, + { 46, 17, 33, 183, 6, 98, 15, 32, 183 }, + { 57, 46, 22, 24, 128, 1, 54, 17, 37 }, + { 65, 32, 73, 115, 28, 128, 23, 128, 205 }, + { 40, 3, 9, 115, 51, 192, 18, 6, 223 }, + { 87, 37, 9, 115, 59, 77, 64, 21, 47 } }, + { { 104, 55, 44, 218, 9, 54, 53, 130, 226 }, + { 64, 90, 70, 205, 40, 41, 23, 26, 57 }, + { 54, 57, 112, 184, 5, 41, 38, 166, 213 }, + { 30, 34, 26, 133, 152, 116, 10, 32, 134 }, + { 39, 19, 53, 221, 26, 114, 32, 73, 255 }, + { 31, 9, 65, 234, 2, 15, 1, 118, 73 }, + { 75, 32, 12, 51, 192, 255, 160, 43, 51 }, + { 88, 31, 35, 67, 102, 85, 55, 186, 85 }, + { 56, 21, 23, 111, 59, 205, 45, 37, 192 }, + { 55, 38, 70, 124, 73, 102, 1, 34, 98 } }, + { { 125, 98, 42, 88, 104, 85, 117, 175, 82 }, + { 95, 84, 53, 89, 128, 100, 113, 101, 45 }, + { 75, 79, 123, 47, 51, 128, 81, 171, 1 }, + { 57, 17, 5, 71, 102, 57, 53, 41, 49 }, + { 38, 33, 13, 121, 57, 73, 26, 1, 85 }, + { 41, 10, 67, 138, 77, 110, 90, 47, 114 }, + { 115, 21, 2, 10, 102, 255, 166, 23, 6 }, + { 101, 29, 16, 10, 85, 128, 101, 196, 26 }, + { 57, 18, 10, 102, 102, 213, 34, 20, 43 }, + { 117, 20, 15, 36, 163, 128, 68, 1, 26 } }, + { { 102, 61, 71, 37, 34, 53, 31, 243, 192 }, + { 69, 60, 71, 38, 73, 119, 28, 222, 37 }, + { 68, 45, 128, 34, 1, 47, 11, 245, 171 }, + { 62, 17, 19, 70, 146, 85, 55, 62, 70 }, + { 37, 43, 37, 154, 100, 163, 85, 160, 1 }, + { 63, 9, 92, 136, 28, 64, 32, 201, 85 }, + { 75, 15, 9, 9, 64, 255, 184, 119, 16 }, + { 86, 6, 28, 5, 64, 255, 25, 248, 1 }, + { 56, 8, 17, 132, 137, 255, 55, 116, 128 }, + { 58, 15, 20, 82, 135, 57, 26, 121, 40 } }, + { { 164, 50, 31, 137, 154, 133, 25, 35, 218 }, + { 51, 103, 44, 131, 131, 123, 31, 6, 158 }, + { 86, 40, 64, 135, 148, 224, 45, 183, 128 }, + { 22, 26, 17, 131, 240, 154, 14, 1, 209 }, + { 45, 16, 21, 91, 64, 222, 7, 1, 197 }, + { 56, 21, 39, 155, 60, 138, 23, 102, 213 }, + { 83, 12, 13, 54, 192, 255, 68, 47, 28 }, + { 85, 26, 85, 85, 128, 128, 32, 146, 171 }, + { 18, 11, 7, 63, 144, 171, 4, 4, 246 }, + { 35, 27, 10, 146, 174, 171, 12, 26, 128 } }, + { { 190, 80, 35, 99, 180, 80, 126, 54, 45 }, + { 85, 126, 47, 87, 176, 51, 41, 20, 32 }, + { 101, 75, 128, 139, 118, 146, 116, 128, 85 }, + { 56, 41, 15, 176, 236, 85, 37, 9, 62 }, + { 71, 30, 17, 119, 118, 255, 17, 18, 138 }, + { 101, 38, 60, 138, 55, 70, 43, 26, 142 }, + { 146, 36, 19, 30, 171, 255, 97, 27, 20 }, + { 138, 45, 61, 62, 219, 1, 81, 188, 64 }, + { 32, 41, 20, 117, 151, 142, 20, 21, 163 }, + { 112, 19, 12, 61, 195, 128, 48, 4, 24 } } +}; + +static int PutI4Mode(VP8BitWriter* const bw, int mode, + const uint8_t* const prob) { + if (VP8PutBit(bw, mode != B_DC_PRED, prob[0])) { + if (VP8PutBit(bw, mode != B_TM_PRED, prob[1])) { + if (VP8PutBit(bw, mode != B_VE_PRED, prob[2])) { + if (!VP8PutBit(bw, mode >= B_LD_PRED, prob[3])) { + if (VP8PutBit(bw, mode != B_HE_PRED, prob[4])) { + VP8PutBit(bw, mode != B_RD_PRED, prob[5]); + } + } else { + if (VP8PutBit(bw, mode != B_LD_PRED, prob[6])) { + if (VP8PutBit(bw, mode != B_VL_PRED, prob[7])) { + VP8PutBit(bw, mode != B_HD_PRED, prob[8]); + } + } + } + } + } + } + return mode; +} + +static void PutI16Mode(VP8BitWriter* const bw, int mode) { + if (VP8PutBit(bw, (mode == TM_PRED || mode == H_PRED), 156)) { + VP8PutBit(bw, mode == TM_PRED, 128); // TM or HE + } else { + VP8PutBit(bw, mode == V_PRED, 163); // VE or DC + } +} + +static void PutUVMode(VP8BitWriter* const bw, int uv_mode) { + if (VP8PutBit(bw, uv_mode != DC_PRED, 142)) { + if (VP8PutBit(bw, uv_mode != V_PRED, 114)) { + VP8PutBit(bw, uv_mode != H_PRED, 183); // else: TM_PRED + } + } +} + +static void PutSegment(VP8BitWriter* const bw, int s, const uint8_t* p) { + if (VP8PutBit(bw, s >= 2, p[0])) p += 1; + VP8PutBit(bw, s & 1, p[1]); +} + +void VP8CodeIntraModes(VP8Encoder* const enc) { + VP8BitWriter* const bw = &enc->bw_; + VP8EncIterator it; + VP8IteratorInit(enc, &it); + do { + const VP8MBInfo* const mb = it.mb_; + const uint8_t* preds = it.preds_; + if (enc->segment_hdr_.update_map_) { + PutSegment(bw, mb->segment_, enc->proba_.segments_); + } + if (enc->proba_.use_skip_proba_) { + VP8PutBit(bw, mb->skip_, enc->proba_.skip_proba_); + } + if (VP8PutBit(bw, (mb->type_ != 0), 145)) { // i16x16 + PutI16Mode(bw, preds[0]); + } else { + const int preds_w = enc->preds_w_; + const uint8_t* top_pred = preds - preds_w; + int x, y; + for (y = 0; y < 4; ++y) { + int left = preds[-1]; + for (x = 0; x < 4; ++x) { + const uint8_t* const probas = kBModesProba[top_pred[x]][left]; + left = PutI4Mode(bw, preds[x], probas); + } + top_pred = preds; + preds += preds_w; + } + } + PutUVMode(bw, mb->uv_mode_); + } while (VP8IteratorNext(&it)); +} + +//------------------------------------------------------------------------------ +// Paragraph 13 + +const uint8_t + VP8CoeffsUpdateProba[NUM_TYPES][NUM_BANDS][NUM_CTX][NUM_PROBAS] = { + { { { 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 }, + { 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 }, + { 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 } + }, + { { 176, 246, 255, 255, 255, 255, 255, 255, 255, 255, 255 }, + { 223, 241, 252, 255, 255, 255, 255, 255, 255, 255, 255 }, + { 249, 253, 253, 255, 255, 255, 255, 255, 255, 255, 255 } + }, + { { 255, 244, 252, 255, 255, 255, 255, 255, 255, 255, 255 }, + { 234, 254, 254, 255, 255, 255, 255, 255, 255, 255, 255 }, + { 253, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 } + }, + { { 255, 246, 254, 255, 255, 255, 255, 255, 255, 255, 255 }, + { 239, 253, 254, 255, 255, 255, 255, 255, 255, 255, 255 }, + { 254, 255, 254, 255, 255, 255, 255, 255, 255, 255, 255 } + }, + { { 255, 248, 254, 255, 255, 255, 255, 255, 255, 255, 255 }, + { 251, 255, 254, 255, 255, 255, 255, 255, 255, 255, 255 }, + { 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 } + }, + { { 255, 253, 254, 255, 255, 255, 255, 255, 255, 255, 255 }, + { 251, 254, 254, 255, 255, 255, 255, 255, 255, 255, 255 }, + { 254, 255, 254, 255, 255, 255, 255, 255, 255, 255, 255 } + }, + { { 255, 254, 253, 255, 254, 255, 255, 255, 255, 255, 255 }, + { 250, 255, 254, 255, 254, 255, 255, 255, 255, 255, 255 }, + { 254, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 } + }, + { { 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 }, + { 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 }, + { 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 } + } + }, + { { { 217, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 }, + { 225, 252, 241, 253, 255, 255, 254, 255, 255, 255, 255 }, + { 234, 250, 241, 250, 253, 255, 253, 254, 255, 255, 255 } + }, + { { 255, 254, 255, 255, 255, 255, 255, 255, 255, 255, 255 }, + { 223, 254, 254, 255, 255, 255, 255, 255, 255, 255, 255 }, + { 238, 253, 254, 254, 255, 255, 255, 255, 255, 255, 255 } + }, + { { 255, 248, 254, 255, 255, 255, 255, 255, 255, 255, 255 }, + { 249, 254, 255, 255, 255, 255, 255, 255, 255, 255, 255 }, + { 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 } + }, + { { 255, 253, 255, 255, 255, 255, 255, 255, 255, 255, 255 }, + { 247, 254, 255, 255, 255, 255, 255, 255, 255, 255, 255 }, + { 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 } + }, + { { 255, 253, 254, 255, 255, 255, 255, 255, 255, 255, 255 }, + { 252, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 }, + { 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 } + }, + { { 255, 254, 254, 255, 255, 255, 255, 255, 255, 255, 255 }, + { 253, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 }, + { 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 } + }, + { { 255, 254, 253, 255, 255, 255, 255, 255, 255, 255, 255 }, + { 250, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 }, + { 254, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 } + }, + { { 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 }, + { 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 }, + { 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 } + } + }, + { { { 186, 251, 250, 255, 255, 255, 255, 255, 255, 255, 255 }, + { 234, 251, 244, 254, 255, 255, 255, 255, 255, 255, 255 }, + { 251, 251, 243, 253, 254, 255, 254, 255, 255, 255, 255 } + }, + { { 255, 253, 254, 255, 255, 255, 255, 255, 255, 255, 255 }, + { 236, 253, 254, 255, 255, 255, 255, 255, 255, 255, 255 }, + { 251, 253, 253, 254, 254, 255, 255, 255, 255, 255, 255 } + }, + { { 255, 254, 254, 255, 255, 255, 255, 255, 255, 255, 255 }, + { 254, 254, 254, 255, 255, 255, 255, 255, 255, 255, 255 }, + { 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 } + }, + { { 255, 254, 255, 255, 255, 255, 255, 255, 255, 255, 255 }, + { 254, 254, 255, 255, 255, 255, 255, 255, 255, 255, 255 }, + { 254, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 } + }, + { { 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 }, + { 254, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 }, + { 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 } + }, + { { 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 }, + { 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 }, + { 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 } + }, + { { 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 }, + { 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 }, + { 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 } + }, + { { 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 }, + { 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 }, + { 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 } + } + }, + { { { 248, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 }, + { 250, 254, 252, 254, 255, 255, 255, 255, 255, 255, 255 }, + { 248, 254, 249, 253, 255, 255, 255, 255, 255, 255, 255 } + }, + { { 255, 253, 253, 255, 255, 255, 255, 255, 255, 255, 255 }, + { 246, 253, 253, 255, 255, 255, 255, 255, 255, 255, 255 }, + { 252, 254, 251, 254, 254, 255, 255, 255, 255, 255, 255 } + }, + { { 255, 254, 252, 255, 255, 255, 255, 255, 255, 255, 255 }, + { 248, 254, 253, 255, 255, 255, 255, 255, 255, 255, 255 }, + { 253, 255, 254, 254, 255, 255, 255, 255, 255, 255, 255 } + }, + { { 255, 251, 254, 255, 255, 255, 255, 255, 255, 255, 255 }, + { 245, 251, 254, 255, 255, 255, 255, 255, 255, 255, 255 }, + { 253, 253, 254, 255, 255, 255, 255, 255, 255, 255, 255 } + }, + { { 255, 251, 253, 255, 255, 255, 255, 255, 255, 255, 255 }, + { 252, 253, 254, 255, 255, 255, 255, 255, 255, 255, 255 }, + { 255, 254, 255, 255, 255, 255, 255, 255, 255, 255, 255 } + }, + { { 255, 252, 255, 255, 255, 255, 255, 255, 255, 255, 255 }, + { 249, 255, 254, 255, 255, 255, 255, 255, 255, 255, 255 }, + { 255, 255, 254, 255, 255, 255, 255, 255, 255, 255, 255 } + }, + { { 255, 255, 253, 255, 255, 255, 255, 255, 255, 255, 255 }, + { 250, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 }, + { 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 } + }, + { { 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 }, + { 254, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 }, + { 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 } + } + } +}; + +void VP8WriteProbas(VP8BitWriter* const bw, const VP8EncProba* const probas) { + int t, b, c, p; + for (t = 0; t < NUM_TYPES; ++t) { + for (b = 0; b < NUM_BANDS; ++b) { + for (c = 0; c < NUM_CTX; ++c) { + for (p = 0; p < NUM_PROBAS; ++p) { + const uint8_t p0 = probas->coeffs_[t][b][c][p]; + const int update = (p0 != VP8CoeffsProba0[t][b][c][p]); + if (VP8PutBit(bw, update, VP8CoeffsUpdateProba[t][b][c][p])) { + VP8PutBits(bw, p0, 8); + } + } + } + } + } + if (VP8PutBitUniform(bw, probas->use_skip_proba_)) { + VP8PutBits(bw, probas->skip_proba_, 8); + } +} + diff --git a/thirdparty/libwebp/enc/vp8enci.h b/thirdparty/libwebp/enc/vp8enci.h deleted file mode 100644 index 5b4e162a58..0000000000 --- a/thirdparty/libwebp/enc/vp8enci.h +++ /dev/null @@ -1,529 +0,0 @@ -// Copyright 2011 Google Inc. All Rights Reserved. -// -// Use of this source code is governed by a BSD-style license -// that can be found in the COPYING file in the root of the source -// tree. An additional intellectual property rights grant can be found -// in the file PATENTS. All contributing project authors may -// be found in the AUTHORS file in the root of the source tree. -// ----------------------------------------------------------------------------- -// -// WebP encoder: internal header. -// -// Author: Skal (pascal.massimino@gmail.com) - -#ifndef WEBP_ENC_VP8ENCI_H_ -#define WEBP_ENC_VP8ENCI_H_ - -#include // for memcpy() -#include "../dec/common.h" -#include "../dsp/dsp.h" -#include "../utils/bit_writer.h" -#include "../utils/thread.h" -#include "../utils/utils.h" -#include "../webp/encode.h" - -#ifdef __cplusplus -extern "C" { -#endif - -//------------------------------------------------------------------------------ -// Various defines and enums - -// version numbers -#define ENC_MAJ_VERSION 0 -#define ENC_MIN_VERSION 5 -#define ENC_REV_VERSION 2 - -enum { MAX_LF_LEVELS = 64, // Maximum loop filter level - MAX_VARIABLE_LEVEL = 67, // last (inclusive) level with variable cost - MAX_LEVEL = 2047 // max level (note: max codable is 2047 + 67) - }; - -typedef enum { // Rate-distortion optimization levels - RD_OPT_NONE = 0, // no rd-opt - RD_OPT_BASIC = 1, // basic scoring (no trellis) - RD_OPT_TRELLIS = 2, // perform trellis-quant on the final decision only - RD_OPT_TRELLIS_ALL = 3 // trellis-quant for every scoring (much slower) -} VP8RDLevel; - -// YUV-cache parameters. Cache is 32-bytes wide (= one cacheline). -// The original or reconstructed samples can be accessed using VP8Scan[]. -// The predicted blocks can be accessed using offsets to yuv_p_ and -// the arrays VP8*ModeOffsets[]. -// * YUV Samples area (yuv_in_/yuv_out_/yuv_out2_) -// (see VP8Scan[] for accessing the blocks, along with -// Y_OFF_ENC/U_OFF_ENC/V_OFF_ENC): -// +----+----+ -// Y_OFF_ENC |YYYY|UUVV| -// U_OFF_ENC |YYYY|UUVV| -// V_OFF_ENC |YYYY|....| <- 25% wasted U/V area -// |YYYY|....| -// +----+----+ -// * Prediction area ('yuv_p_', size = PRED_SIZE_ENC) -// Intra16 predictions (16x16 block each, two per row): -// |I16DC16|I16TM16| -// |I16VE16|I16HE16| -// Chroma U/V predictions (16x8 block each, two per row): -// |C8DC8|C8TM8| -// |C8VE8|C8HE8| -// Intra 4x4 predictions (4x4 block each) -// |I4DC4 I4TM4 I4VE4 I4HE4|I4RD4 I4VR4 I4LD4 I4VL4| -// |I4HD4 I4HU4 I4TMP .....|.......................| <- ~31% wasted -#define YUV_SIZE_ENC (BPS * 16) -#define PRED_SIZE_ENC (32 * BPS + 16 * BPS + 8 * BPS) // I16+Chroma+I4 preds -#define Y_OFF_ENC (0) -#define U_OFF_ENC (16) -#define V_OFF_ENC (16 + 8) - -extern const int VP8Scan[16]; // in quant.c -extern const int VP8UVModeOffsets[4]; // in analyze.c -extern const int VP8I16ModeOffsets[4]; -extern const int VP8I4ModeOffsets[NUM_BMODES]; - -// Layout of prediction blocks -// intra 16x16 -#define I16DC16 (0 * 16 * BPS) -#define I16TM16 (I16DC16 + 16) -#define I16VE16 (1 * 16 * BPS) -#define I16HE16 (I16VE16 + 16) -// chroma 8x8, two U/V blocks side by side (hence: 16x8 each) -#define C8DC8 (2 * 16 * BPS) -#define C8TM8 (C8DC8 + 1 * 16) -#define C8VE8 (2 * 16 * BPS + 8 * BPS) -#define C8HE8 (C8VE8 + 1 * 16) -// intra 4x4 -#define I4DC4 (3 * 16 * BPS + 0) -#define I4TM4 (I4DC4 + 4) -#define I4VE4 (I4DC4 + 8) -#define I4HE4 (I4DC4 + 12) -#define I4RD4 (I4DC4 + 16) -#define I4VR4 (I4DC4 + 20) -#define I4LD4 (I4DC4 + 24) -#define I4VL4 (I4DC4 + 28) -#define I4HD4 (3 * 16 * BPS + 4 * BPS) -#define I4HU4 (I4HD4 + 4) -#define I4TMP (I4HD4 + 8) - -typedef int64_t score_t; // type used for scores, rate, distortion -// Note that MAX_COST is not the maximum allowed by sizeof(score_t), -// in order to allow overflowing computations. -#define MAX_COST ((score_t)0x7fffffffffffffLL) - -#define QFIX 17 -#define BIAS(b) ((b) << (QFIX - 8)) -// Fun fact: this is the _only_ line where we're actually being lossy and -// discarding bits. -static WEBP_INLINE int QUANTDIV(uint32_t n, uint32_t iQ, uint32_t B) { - return (int)((n * iQ + B) >> QFIX); -} - -// Uncomment the following to remove token-buffer code: -// #define DISABLE_TOKEN_BUFFER - -//------------------------------------------------------------------------------ -// Headers - -typedef uint32_t proba_t; // 16b + 16b -typedef uint8_t ProbaArray[NUM_CTX][NUM_PROBAS]; -typedef proba_t StatsArray[NUM_CTX][NUM_PROBAS]; -typedef uint16_t CostArray[NUM_CTX][MAX_VARIABLE_LEVEL + 1]; -typedef const uint16_t* (*CostArrayPtr)[NUM_CTX]; // for easy casting -typedef const uint16_t* CostArrayMap[16][NUM_CTX]; -typedef double LFStats[NUM_MB_SEGMENTS][MAX_LF_LEVELS]; // filter stats - -typedef struct VP8Encoder VP8Encoder; - -// segment features -typedef struct { - int num_segments_; // Actual number of segments. 1 segment only = unused. - int update_map_; // whether to update the segment map or not. - // must be 0 if there's only 1 segment. - int size_; // bit-cost for transmitting the segment map -} VP8EncSegmentHeader; - -// Struct collecting all frame-persistent probabilities. -typedef struct { - uint8_t segments_[3]; // probabilities for segment tree - uint8_t skip_proba_; // final probability of being skipped. - ProbaArray coeffs_[NUM_TYPES][NUM_BANDS]; // 1056 bytes - StatsArray stats_[NUM_TYPES][NUM_BANDS]; // 4224 bytes - CostArray level_cost_[NUM_TYPES][NUM_BANDS]; // 13056 bytes - CostArrayMap remapped_costs_[NUM_TYPES]; // 1536 bytes - int dirty_; // if true, need to call VP8CalculateLevelCosts() - int use_skip_proba_; // Note: we always use skip_proba for now. - int nb_skip_; // number of skipped blocks -} VP8EncProba; - -// Filter parameters. Not actually used in the code (we don't perform -// the in-loop filtering), but filled from user's config -typedef struct { - int simple_; // filtering type: 0=complex, 1=simple - int level_; // base filter level [0..63] - int sharpness_; // [0..7] - int i4x4_lf_delta_; // delta filter level for i4x4 relative to i16x16 -} VP8EncFilterHeader; - -//------------------------------------------------------------------------------ -// Informations about the macroblocks. - -typedef struct { - // block type - unsigned int type_:2; // 0=i4x4, 1=i16x16 - unsigned int uv_mode_:2; - unsigned int skip_:1; - unsigned int segment_:2; - uint8_t alpha_; // quantization-susceptibility -} VP8MBInfo; - -typedef struct VP8Matrix { - uint16_t q_[16]; // quantizer steps - uint16_t iq_[16]; // reciprocals, fixed point. - uint32_t bias_[16]; // rounding bias - uint32_t zthresh_[16]; // value below which a coefficient is zeroed - uint16_t sharpen_[16]; // frequency boosters for slight sharpening -} VP8Matrix; - -typedef struct { - VP8Matrix y1_, y2_, uv_; // quantization matrices - int alpha_; // quant-susceptibility, range [-127,127]. Zero is neutral. - // Lower values indicate a lower risk of blurriness. - int beta_; // filter-susceptibility, range [0,255]. - int quant_; // final segment quantizer. - int fstrength_; // final in-loop filtering strength - int max_edge_; // max edge delta (for filtering strength) - int min_disto_; // minimum distortion required to trigger filtering record - // reactivities - int lambda_i16_, lambda_i4_, lambda_uv_; - int lambda_mode_, lambda_trellis_, tlambda_; - int lambda_trellis_i16_, lambda_trellis_i4_, lambda_trellis_uv_; - - // lambda values for distortion-based evaluation - score_t i4_penalty_; // penalty for using Intra4 -} VP8SegmentInfo; - -// Handy transient struct to accumulate score and info during RD-optimization -// and mode evaluation. -typedef struct { - score_t D, SD; // Distortion, spectral distortion - score_t H, R, score; // header bits, rate, score. - int16_t y_dc_levels[16]; // Quantized levels for luma-DC, luma-AC, chroma. - int16_t y_ac_levels[16][16]; - int16_t uv_levels[4 + 4][16]; - int mode_i16; // mode number for intra16 prediction - uint8_t modes_i4[16]; // mode numbers for intra4 predictions - int mode_uv; // mode number of chroma prediction - uint32_t nz; // non-zero blocks -} VP8ModeScore; - -// Iterator structure to iterate through macroblocks, pointing to the -// right neighbouring data (samples, predictions, contexts, ...) -typedef struct { - int x_, y_; // current macroblock - int y_stride_, uv_stride_; // respective strides - uint8_t* yuv_in_; // input samples - uint8_t* yuv_out_; // output samples - uint8_t* yuv_out2_; // secondary buffer swapped with yuv_out_. - uint8_t* yuv_p_; // scratch buffer for prediction - VP8Encoder* enc_; // back-pointer - VP8MBInfo* mb_; // current macroblock - VP8BitWriter* bw_; // current bit-writer - uint8_t* preds_; // intra mode predictors (4x4 blocks) - uint32_t* nz_; // non-zero pattern - uint8_t i4_boundary_[37]; // 32+5 boundary samples needed by intra4x4 - uint8_t* i4_top_; // pointer to the current top boundary sample - int i4_; // current intra4x4 mode being tested - int top_nz_[9]; // top-non-zero context. - int left_nz_[9]; // left-non-zero. left_nz[8] is independent. - uint64_t bit_count_[4][3]; // bit counters for coded levels. - uint64_t luma_bits_; // macroblock bit-cost for luma - uint64_t uv_bits_; // macroblock bit-cost for chroma - LFStats* lf_stats_; // filter stats (borrowed from enc_) - int do_trellis_; // if true, perform extra level optimisation - int count_down_; // number of mb still to be processed - int count_down0_; // starting counter value (for progress) - int percent0_; // saved initial progress percent - - uint8_t* y_left_; // left luma samples (addressable from index -1 to 15). - uint8_t* u_left_; // left u samples (addressable from index -1 to 7) - uint8_t* v_left_; // left v samples (addressable from index -1 to 7) - - uint8_t* y_top_; // top luma samples at position 'x_' - uint8_t* uv_top_; // top u/v samples at position 'x_', packed as 16 bytes - - // memory for storing y/u/v_left_ - uint8_t yuv_left_mem_[17 + 16 + 16 + 8 + WEBP_ALIGN_CST]; - // memory for yuv_* - uint8_t yuv_mem_[3 * YUV_SIZE_ENC + PRED_SIZE_ENC + WEBP_ALIGN_CST]; -} VP8EncIterator; - - // in iterator.c -// must be called first -void VP8IteratorInit(VP8Encoder* const enc, VP8EncIterator* const it); -// restart a scan -void VP8IteratorReset(VP8EncIterator* const it); -// reset iterator position to row 'y' -void VP8IteratorSetRow(VP8EncIterator* const it, int y); -// set count down (=number of iterations to go) -void VP8IteratorSetCountDown(VP8EncIterator* const it, int count_down); -// return true if iteration is finished -int VP8IteratorIsDone(const VP8EncIterator* const it); -// Import uncompressed samples from source. -// If tmp_32 is not NULL, import boundary samples too. -// tmp_32 is a 32-bytes scratch buffer that must be aligned in memory. -void VP8IteratorImport(VP8EncIterator* const it, uint8_t* tmp_32); -// export decimated samples -void VP8IteratorExport(const VP8EncIterator* const it); -// go to next macroblock. Returns false if not finished. -int VP8IteratorNext(VP8EncIterator* const it); -// save the yuv_out_ boundary values to top_/left_ arrays for next iterations. -void VP8IteratorSaveBoundary(VP8EncIterator* const it); -// Report progression based on macroblock rows. Return 0 for user-abort request. -int VP8IteratorProgress(const VP8EncIterator* const it, - int final_delta_percent); -// Intra4x4 iterations -void VP8IteratorStartI4(VP8EncIterator* const it); -// returns true if not done. -int VP8IteratorRotateI4(VP8EncIterator* const it, - const uint8_t* const yuv_out); - -// Non-zero context setup/teardown -void VP8IteratorNzToBytes(VP8EncIterator* const it); -void VP8IteratorBytesToNz(VP8EncIterator* const it); - -// Helper functions to set mode properties -void VP8SetIntra16Mode(const VP8EncIterator* const it, int mode); -void VP8SetIntra4Mode(const VP8EncIterator* const it, const uint8_t* modes); -void VP8SetIntraUVMode(const VP8EncIterator* const it, int mode); -void VP8SetSkip(const VP8EncIterator* const it, int skip); -void VP8SetSegment(const VP8EncIterator* const it, int segment); - -//------------------------------------------------------------------------------ -// Paginated token buffer - -typedef struct VP8Tokens VP8Tokens; // struct details in token.c - -typedef struct { -#if !defined(DISABLE_TOKEN_BUFFER) - VP8Tokens* pages_; // first page - VP8Tokens** last_page_; // last page - uint16_t* tokens_; // set to (*last_page_)->tokens_ - int left_; // how many free tokens left before the page is full - int page_size_; // number of tokens per page -#endif - int error_; // true in case of malloc error -} VP8TBuffer; - -// initialize an empty buffer -void VP8TBufferInit(VP8TBuffer* const b, int page_size); -void VP8TBufferClear(VP8TBuffer* const b); // de-allocate pages memory - -#if !defined(DISABLE_TOKEN_BUFFER) - -// Finalizes bitstream when probabilities are known. -// Deletes the allocated token memory if final_pass is true. -int VP8EmitTokens(VP8TBuffer* const b, VP8BitWriter* const bw, - const uint8_t* const probas, int final_pass); - -// record the coding of coefficients without knowing the probabilities yet -int VP8RecordCoeffTokens(int ctx, const struct VP8Residual* const res, - VP8TBuffer* const tokens); - -// Estimate the final coded size given a set of 'probas'. -size_t VP8EstimateTokenSize(VP8TBuffer* const b, const uint8_t* const probas); - -// unused for now -void VP8TokenToStats(const VP8TBuffer* const b, proba_t* const stats); - -#endif // !DISABLE_TOKEN_BUFFER - -//------------------------------------------------------------------------------ -// VP8Encoder - -struct VP8Encoder { - const WebPConfig* config_; // user configuration and parameters - WebPPicture* pic_; // input / output picture - - // headers - VP8EncFilterHeader filter_hdr_; // filtering information - VP8EncSegmentHeader segment_hdr_; // segment information - - int profile_; // VP8's profile, deduced from Config. - - // dimension, in macroblock units. - int mb_w_, mb_h_; - int preds_w_; // stride of the *preds_ prediction plane (=4*mb_w + 1) - - // number of partitions (1, 2, 4 or 8 = MAX_NUM_PARTITIONS) - int num_parts_; - - // per-partition boolean decoders. - VP8BitWriter bw_; // part0 - VP8BitWriter parts_[MAX_NUM_PARTITIONS]; // token partitions - VP8TBuffer tokens_; // token buffer - - int percent_; // for progress - - // transparency blob - int has_alpha_; - uint8_t* alpha_data_; // non-NULL if transparency is present - uint32_t alpha_data_size_; - WebPWorker alpha_worker_; - - // quantization info (one set of DC/AC dequant factor per segment) - VP8SegmentInfo dqm_[NUM_MB_SEGMENTS]; - int base_quant_; // nominal quantizer value. Only used - // for relative coding of segments' quant. - int alpha_; // global susceptibility (<=> complexity) - int uv_alpha_; // U/V quantization susceptibility - // global offset of quantizers, shared by all segments - int dq_y1_dc_; - int dq_y2_dc_, dq_y2_ac_; - int dq_uv_dc_, dq_uv_ac_; - - // probabilities and statistics - VP8EncProba proba_; - uint64_t sse_[4]; // sum of Y/U/V/A squared errors for all macroblocks - uint64_t sse_count_; // pixel count for the sse_[] stats - int coded_size_; - int residual_bytes_[3][4]; - int block_count_[3]; - - // quality/speed settings - int method_; // 0=fastest, 6=best/slowest. - VP8RDLevel rd_opt_level_; // Deduced from method_. - int max_i4_header_bits_; // partition #0 safeness factor - int mb_header_limit_; // rough limit for header bits per MB - int thread_level_; // derived from config->thread_level - int do_search_; // derived from config->target_XXX - int use_tokens_; // if true, use token buffer - - // Memory - VP8MBInfo* mb_info_; // contextual macroblock infos (mb_w_ + 1) - uint8_t* preds_; // predictions modes: (4*mb_w+1) * (4*mb_h+1) - uint32_t* nz_; // non-zero bit context: mb_w+1 - uint8_t* y_top_; // top luma samples. - uint8_t* uv_top_; // top u/v samples. - // U and V are packed into 16 bytes (8 U + 8 V) - LFStats* lf_stats_; // autofilter stats (if NULL, autofilter is off) -}; - -//------------------------------------------------------------------------------ -// internal functions. Not public. - - // in tree.c -extern const uint8_t VP8CoeffsProba0[NUM_TYPES][NUM_BANDS][NUM_CTX][NUM_PROBAS]; -extern const uint8_t - VP8CoeffsUpdateProba[NUM_TYPES][NUM_BANDS][NUM_CTX][NUM_PROBAS]; -// Reset the token probabilities to their initial (default) values -void VP8DefaultProbas(VP8Encoder* const enc); -// Write the token probabilities -void VP8WriteProbas(VP8BitWriter* const bw, const VP8EncProba* const probas); -// Writes the partition #0 modes (that is: all intra modes) -void VP8CodeIntraModes(VP8Encoder* const enc); - - // in syntax.c -// Generates the final bitstream by coding the partition0 and headers, -// and appending an assembly of all the pre-coded token partitions. -// Return true if everything is ok. -int VP8EncWrite(VP8Encoder* const enc); -// Release memory allocated for bit-writing in VP8EncLoop & seq. -void VP8EncFreeBitWriters(VP8Encoder* const enc); - - // in frame.c -extern const uint8_t VP8Cat3[]; -extern const uint8_t VP8Cat4[]; -extern const uint8_t VP8Cat5[]; -extern const uint8_t VP8Cat6[]; - -// Form all the four Intra16x16 predictions in the yuv_p_ cache -void VP8MakeLuma16Preds(const VP8EncIterator* const it); -// Form all the four Chroma8x8 predictions in the yuv_p_ cache -void VP8MakeChroma8Preds(const VP8EncIterator* const it); -// Form all the ten Intra4x4 predictions in the yuv_p_ cache -// for the 4x4 block it->i4_ -void VP8MakeIntra4Preds(const VP8EncIterator* const it); -// Rate calculation -int VP8GetCostLuma16(VP8EncIterator* const it, const VP8ModeScore* const rd); -int VP8GetCostLuma4(VP8EncIterator* const it, const int16_t levels[16]); -int VP8GetCostUV(VP8EncIterator* const it, const VP8ModeScore* const rd); -// Main coding calls -int VP8EncLoop(VP8Encoder* const enc); -int VP8EncTokenLoop(VP8Encoder* const enc); - - // in webpenc.c -// Assign an error code to a picture. Return false for convenience. -int WebPEncodingSetError(const WebPPicture* const pic, WebPEncodingError error); -int WebPReportProgress(const WebPPicture* const pic, - int percent, int* const percent_store); - - // in analysis.c -// Main analysis loop. Decides the segmentations and complexity. -// Assigns a first guess for Intra16 and uvmode_ prediction modes. -int VP8EncAnalyze(VP8Encoder* const enc); - - // in quant.c -// Sets up segment's quantization values, base_quant_ and filter strengths. -void VP8SetSegmentParams(VP8Encoder* const enc, float quality); -// Pick best modes and fills the levels. Returns true if skipped. -int VP8Decimate(VP8EncIterator* const it, VP8ModeScore* const rd, - VP8RDLevel rd_opt); - - // in alpha.c -void VP8EncInitAlpha(VP8Encoder* const enc); // initialize alpha compression -int VP8EncStartAlpha(VP8Encoder* const enc); // start alpha coding process -int VP8EncFinishAlpha(VP8Encoder* const enc); // finalize compressed data -int VP8EncDeleteAlpha(VP8Encoder* const enc); // delete compressed data - - // in filter.c -void VP8SSIMAddStats(const VP8DistoStats* const src, VP8DistoStats* const dst); -void VP8SSIMAccumulatePlane(const uint8_t* src1, int stride1, - const uint8_t* src2, int stride2, - int W, int H, VP8DistoStats* const stats); -double VP8SSIMGet(const VP8DistoStats* const stats); -double VP8SSIMGetSquaredError(const VP8DistoStats* const stats); - -// autofilter -void VP8InitFilter(VP8EncIterator* const it); -void VP8StoreFilterStats(VP8EncIterator* const it); -void VP8AdjustFilterStrength(VP8EncIterator* const it); - -// returns the approximate filtering strength needed to smooth a edge -// step of 'delta', given a sharpness parameter 'sharpness'. -int VP8FilterStrengthFromDelta(int sharpness, int delta); - - // misc utils for picture_*.c: - -// Remove reference to the ARGB/YUVA buffer (doesn't free anything). -void WebPPictureResetBuffers(WebPPicture* const picture); - -// Allocates ARGB buffer of given dimension (previous one is always free'd). -// Preserves the YUV(A) buffer. Returns false in case of error (invalid param, -// out-of-memory). -int WebPPictureAllocARGB(WebPPicture* const picture, int width, int height); - -// Allocates YUVA buffer of given dimension (previous one is always free'd). -// Uses picture->csp to determine whether an alpha buffer is needed. -// Preserves the ARGB buffer. -// Returns false in case of error (invalid param, out-of-memory). -int WebPPictureAllocYUVA(WebPPicture* const picture, int width, int height); - -// Clean-up the RGB samples under fully transparent area, to help lossless -// compressibility (no guarantee, though). Assumes that pic->use_argb is true. -void WebPCleanupTransparentAreaLossless(WebPPicture* const pic); - - // in near_lossless.c -// Near lossless preprocessing in RGB color-space. -int VP8ApplyNearLossless(int xsize, int ysize, uint32_t* argb, int quality); -// Near lossless adjustment for predictors. -void VP8ApplyNearLosslessPredict(int xsize, int ysize, int pred_bits, - const uint32_t* argb_orig, - uint32_t* argb, uint32_t* argb_scratch, - const uint32_t* const transform_data, - int quality, int subtract_green); -//------------------------------------------------------------------------------ - -#ifdef __cplusplus -} // extern "C" -#endif - -#endif /* WEBP_ENC_VP8ENCI_H_ */ diff --git a/thirdparty/libwebp/enc/vp8i_enc.h b/thirdparty/libwebp/enc/vp8i_enc.h new file mode 100644 index 0000000000..93c95ecbfb --- /dev/null +++ b/thirdparty/libwebp/enc/vp8i_enc.h @@ -0,0 +1,520 @@ +// Copyright 2011 Google Inc. All Rights Reserved. +// +// Use of this source code is governed by a BSD-style license +// that can be found in the COPYING file in the root of the source +// tree. An additional intellectual property rights grant can be found +// in the file PATENTS. All contributing project authors may +// be found in the AUTHORS file in the root of the source tree. +// ----------------------------------------------------------------------------- +// +// WebP encoder: internal header. +// +// Author: Skal (pascal.massimino@gmail.com) + +#ifndef WEBP_ENC_VP8ENCI_H_ +#define WEBP_ENC_VP8ENCI_H_ + +#include // for memcpy() +#include "../dec/common_dec.h" +#include "../dsp/dsp.h" +#include "../utils/bit_writer_utils.h" +#include "../utils/thread_utils.h" +#include "../utils/utils.h" +#include "../webp/encode.h" + +#ifdef __cplusplus +extern "C" { +#endif + +//------------------------------------------------------------------------------ +// Various defines and enums + +// version numbers +#define ENC_MAJ_VERSION 0 +#define ENC_MIN_VERSION 6 +#define ENC_REV_VERSION 0 + +enum { MAX_LF_LEVELS = 64, // Maximum loop filter level + MAX_VARIABLE_LEVEL = 67, // last (inclusive) level with variable cost + MAX_LEVEL = 2047 // max level (note: max codable is 2047 + 67) + }; + +typedef enum { // Rate-distortion optimization levels + RD_OPT_NONE = 0, // no rd-opt + RD_OPT_BASIC = 1, // basic scoring (no trellis) + RD_OPT_TRELLIS = 2, // perform trellis-quant on the final decision only + RD_OPT_TRELLIS_ALL = 3 // trellis-quant for every scoring (much slower) +} VP8RDLevel; + +// YUV-cache parameters. Cache is 32-bytes wide (= one cacheline). +// The original or reconstructed samples can be accessed using VP8Scan[]. +// The predicted blocks can be accessed using offsets to yuv_p_ and +// the arrays VP8*ModeOffsets[]. +// * YUV Samples area (yuv_in_/yuv_out_/yuv_out2_) +// (see VP8Scan[] for accessing the blocks, along with +// Y_OFF_ENC/U_OFF_ENC/V_OFF_ENC): +// +----+----+ +// Y_OFF_ENC |YYYY|UUVV| +// U_OFF_ENC |YYYY|UUVV| +// V_OFF_ENC |YYYY|....| <- 25% wasted U/V area +// |YYYY|....| +// +----+----+ +// * Prediction area ('yuv_p_', size = PRED_SIZE_ENC) +// Intra16 predictions (16x16 block each, two per row): +// |I16DC16|I16TM16| +// |I16VE16|I16HE16| +// Chroma U/V predictions (16x8 block each, two per row): +// |C8DC8|C8TM8| +// |C8VE8|C8HE8| +// Intra 4x4 predictions (4x4 block each) +// |I4DC4 I4TM4 I4VE4 I4HE4|I4RD4 I4VR4 I4LD4 I4VL4| +// |I4HD4 I4HU4 I4TMP .....|.......................| <- ~31% wasted +#define YUV_SIZE_ENC (BPS * 16) +#define PRED_SIZE_ENC (32 * BPS + 16 * BPS + 8 * BPS) // I16+Chroma+I4 preds +#define Y_OFF_ENC (0) +#define U_OFF_ENC (16) +#define V_OFF_ENC (16 + 8) + +extern const int VP8Scan[16]; // in quant.c +extern const int VP8UVModeOffsets[4]; // in analyze.c +extern const int VP8I16ModeOffsets[4]; +extern const int VP8I4ModeOffsets[NUM_BMODES]; + +// Layout of prediction blocks +// intra 16x16 +#define I16DC16 (0 * 16 * BPS) +#define I16TM16 (I16DC16 + 16) +#define I16VE16 (1 * 16 * BPS) +#define I16HE16 (I16VE16 + 16) +// chroma 8x8, two U/V blocks side by side (hence: 16x8 each) +#define C8DC8 (2 * 16 * BPS) +#define C8TM8 (C8DC8 + 1 * 16) +#define C8VE8 (2 * 16 * BPS + 8 * BPS) +#define C8HE8 (C8VE8 + 1 * 16) +// intra 4x4 +#define I4DC4 (3 * 16 * BPS + 0) +#define I4TM4 (I4DC4 + 4) +#define I4VE4 (I4DC4 + 8) +#define I4HE4 (I4DC4 + 12) +#define I4RD4 (I4DC4 + 16) +#define I4VR4 (I4DC4 + 20) +#define I4LD4 (I4DC4 + 24) +#define I4VL4 (I4DC4 + 28) +#define I4HD4 (3 * 16 * BPS + 4 * BPS) +#define I4HU4 (I4HD4 + 4) +#define I4TMP (I4HD4 + 8) + +typedef int64_t score_t; // type used for scores, rate, distortion +// Note that MAX_COST is not the maximum allowed by sizeof(score_t), +// in order to allow overflowing computations. +#define MAX_COST ((score_t)0x7fffffffffffffLL) + +#define QFIX 17 +#define BIAS(b) ((b) << (QFIX - 8)) +// Fun fact: this is the _only_ line where we're actually being lossy and +// discarding bits. +static WEBP_INLINE int QUANTDIV(uint32_t n, uint32_t iQ, uint32_t B) { + return (int)((n * iQ + B) >> QFIX); +} + +// Uncomment the following to remove token-buffer code: +// #define DISABLE_TOKEN_BUFFER + +//------------------------------------------------------------------------------ +// Headers + +typedef uint32_t proba_t; // 16b + 16b +typedef uint8_t ProbaArray[NUM_CTX][NUM_PROBAS]; +typedef proba_t StatsArray[NUM_CTX][NUM_PROBAS]; +typedef uint16_t CostArray[NUM_CTX][MAX_VARIABLE_LEVEL + 1]; +typedef const uint16_t* (*CostArrayPtr)[NUM_CTX]; // for easy casting +typedef const uint16_t* CostArrayMap[16][NUM_CTX]; +typedef double LFStats[NUM_MB_SEGMENTS][MAX_LF_LEVELS]; // filter stats + +typedef struct VP8Encoder VP8Encoder; + +// segment features +typedef struct { + int num_segments_; // Actual number of segments. 1 segment only = unused. + int update_map_; // whether to update the segment map or not. + // must be 0 if there's only 1 segment. + int size_; // bit-cost for transmitting the segment map +} VP8EncSegmentHeader; + +// Struct collecting all frame-persistent probabilities. +typedef struct { + uint8_t segments_[3]; // probabilities for segment tree + uint8_t skip_proba_; // final probability of being skipped. + ProbaArray coeffs_[NUM_TYPES][NUM_BANDS]; // 1056 bytes + StatsArray stats_[NUM_TYPES][NUM_BANDS]; // 4224 bytes + CostArray level_cost_[NUM_TYPES][NUM_BANDS]; // 13056 bytes + CostArrayMap remapped_costs_[NUM_TYPES]; // 1536 bytes + int dirty_; // if true, need to call VP8CalculateLevelCosts() + int use_skip_proba_; // Note: we always use skip_proba for now. + int nb_skip_; // number of skipped blocks +} VP8EncProba; + +// Filter parameters. Not actually used in the code (we don't perform +// the in-loop filtering), but filled from user's config +typedef struct { + int simple_; // filtering type: 0=complex, 1=simple + int level_; // base filter level [0..63] + int sharpness_; // [0..7] + int i4x4_lf_delta_; // delta filter level for i4x4 relative to i16x16 +} VP8EncFilterHeader; + +//------------------------------------------------------------------------------ +// Informations about the macroblocks. + +typedef struct { + // block type + unsigned int type_:2; // 0=i4x4, 1=i16x16 + unsigned int uv_mode_:2; + unsigned int skip_:1; + unsigned int segment_:2; + uint8_t alpha_; // quantization-susceptibility +} VP8MBInfo; + +typedef struct VP8Matrix { + uint16_t q_[16]; // quantizer steps + uint16_t iq_[16]; // reciprocals, fixed point. + uint32_t bias_[16]; // rounding bias + uint32_t zthresh_[16]; // value below which a coefficient is zeroed + uint16_t sharpen_[16]; // frequency boosters for slight sharpening +} VP8Matrix; + +typedef struct { + VP8Matrix y1_, y2_, uv_; // quantization matrices + int alpha_; // quant-susceptibility, range [-127,127]. Zero is neutral. + // Lower values indicate a lower risk of blurriness. + int beta_; // filter-susceptibility, range [0,255]. + int quant_; // final segment quantizer. + int fstrength_; // final in-loop filtering strength + int max_edge_; // max edge delta (for filtering strength) + int min_disto_; // minimum distortion required to trigger filtering record + // reactivities + int lambda_i16_, lambda_i4_, lambda_uv_; + int lambda_mode_, lambda_trellis_, tlambda_; + int lambda_trellis_i16_, lambda_trellis_i4_, lambda_trellis_uv_; + + // lambda values for distortion-based evaluation + score_t i4_penalty_; // penalty for using Intra4 +} VP8SegmentInfo; + +// Handy transient struct to accumulate score and info during RD-optimization +// and mode evaluation. +typedef struct { + score_t D, SD; // Distortion, spectral distortion + score_t H, R, score; // header bits, rate, score. + int16_t y_dc_levels[16]; // Quantized levels for luma-DC, luma-AC, chroma. + int16_t y_ac_levels[16][16]; + int16_t uv_levels[4 + 4][16]; + int mode_i16; // mode number for intra16 prediction + uint8_t modes_i4[16]; // mode numbers for intra4 predictions + int mode_uv; // mode number of chroma prediction + uint32_t nz; // non-zero blocks +} VP8ModeScore; + +// Iterator structure to iterate through macroblocks, pointing to the +// right neighbouring data (samples, predictions, contexts, ...) +typedef struct { + int x_, y_; // current macroblock + uint8_t* yuv_in_; // input samples + uint8_t* yuv_out_; // output samples + uint8_t* yuv_out2_; // secondary buffer swapped with yuv_out_. + uint8_t* yuv_p_; // scratch buffer for prediction + VP8Encoder* enc_; // back-pointer + VP8MBInfo* mb_; // current macroblock + VP8BitWriter* bw_; // current bit-writer + uint8_t* preds_; // intra mode predictors (4x4 blocks) + uint32_t* nz_; // non-zero pattern + uint8_t i4_boundary_[37]; // 32+5 boundary samples needed by intra4x4 + uint8_t* i4_top_; // pointer to the current top boundary sample + int i4_; // current intra4x4 mode being tested + int top_nz_[9]; // top-non-zero context. + int left_nz_[9]; // left-non-zero. left_nz[8] is independent. + uint64_t bit_count_[4][3]; // bit counters for coded levels. + uint64_t luma_bits_; // macroblock bit-cost for luma + uint64_t uv_bits_; // macroblock bit-cost for chroma + LFStats* lf_stats_; // filter stats (borrowed from enc_) + int do_trellis_; // if true, perform extra level optimisation + int count_down_; // number of mb still to be processed + int count_down0_; // starting counter value (for progress) + int percent0_; // saved initial progress percent + + uint8_t* y_left_; // left luma samples (addressable from index -1 to 15). + uint8_t* u_left_; // left u samples (addressable from index -1 to 7) + uint8_t* v_left_; // left v samples (addressable from index -1 to 7) + + uint8_t* y_top_; // top luma samples at position 'x_' + uint8_t* uv_top_; // top u/v samples at position 'x_', packed as 16 bytes + + // memory for storing y/u/v_left_ + uint8_t yuv_left_mem_[17 + 16 + 16 + 8 + WEBP_ALIGN_CST]; + // memory for yuv_* + uint8_t yuv_mem_[3 * YUV_SIZE_ENC + PRED_SIZE_ENC + WEBP_ALIGN_CST]; +} VP8EncIterator; + + // in iterator.c +// must be called first +void VP8IteratorInit(VP8Encoder* const enc, VP8EncIterator* const it); +// restart a scan +void VP8IteratorReset(VP8EncIterator* const it); +// reset iterator position to row 'y' +void VP8IteratorSetRow(VP8EncIterator* const it, int y); +// set count down (=number of iterations to go) +void VP8IteratorSetCountDown(VP8EncIterator* const it, int count_down); +// return true if iteration is finished +int VP8IteratorIsDone(const VP8EncIterator* const it); +// Import uncompressed samples from source. +// If tmp_32 is not NULL, import boundary samples too. +// tmp_32 is a 32-bytes scratch buffer that must be aligned in memory. +void VP8IteratorImport(VP8EncIterator* const it, uint8_t* tmp_32); +// export decimated samples +void VP8IteratorExport(const VP8EncIterator* const it); +// go to next macroblock. Returns false if not finished. +int VP8IteratorNext(VP8EncIterator* const it); +// save the yuv_out_ boundary values to top_/left_ arrays for next iterations. +void VP8IteratorSaveBoundary(VP8EncIterator* const it); +// Report progression based on macroblock rows. Return 0 for user-abort request. +int VP8IteratorProgress(const VP8EncIterator* const it, + int final_delta_percent); +// Intra4x4 iterations +void VP8IteratorStartI4(VP8EncIterator* const it); +// returns true if not done. +int VP8IteratorRotateI4(VP8EncIterator* const it, + const uint8_t* const yuv_out); + +// Non-zero context setup/teardown +void VP8IteratorNzToBytes(VP8EncIterator* const it); +void VP8IteratorBytesToNz(VP8EncIterator* const it); + +// Helper functions to set mode properties +void VP8SetIntra16Mode(const VP8EncIterator* const it, int mode); +void VP8SetIntra4Mode(const VP8EncIterator* const it, const uint8_t* modes); +void VP8SetIntraUVMode(const VP8EncIterator* const it, int mode); +void VP8SetSkip(const VP8EncIterator* const it, int skip); +void VP8SetSegment(const VP8EncIterator* const it, int segment); + +//------------------------------------------------------------------------------ +// Paginated token buffer + +typedef struct VP8Tokens VP8Tokens; // struct details in token.c + +typedef struct { +#if !defined(DISABLE_TOKEN_BUFFER) + VP8Tokens* pages_; // first page + VP8Tokens** last_page_; // last page + uint16_t* tokens_; // set to (*last_page_)->tokens_ + int left_; // how many free tokens left before the page is full + int page_size_; // number of tokens per page +#endif + int error_; // true in case of malloc error +} VP8TBuffer; + +// initialize an empty buffer +void VP8TBufferInit(VP8TBuffer* const b, int page_size); +void VP8TBufferClear(VP8TBuffer* const b); // de-allocate pages memory + +#if !defined(DISABLE_TOKEN_BUFFER) + +// Finalizes bitstream when probabilities are known. +// Deletes the allocated token memory if final_pass is true. +int VP8EmitTokens(VP8TBuffer* const b, VP8BitWriter* const bw, + const uint8_t* const probas, int final_pass); + +// record the coding of coefficients without knowing the probabilities yet +int VP8RecordCoeffTokens(int ctx, const struct VP8Residual* const res, + VP8TBuffer* const tokens); + +// Estimate the final coded size given a set of 'probas'. +size_t VP8EstimateTokenSize(VP8TBuffer* const b, const uint8_t* const probas); + +// unused for now +void VP8TokenToStats(const VP8TBuffer* const b, proba_t* const stats); + +#endif // !DISABLE_TOKEN_BUFFER + +//------------------------------------------------------------------------------ +// VP8Encoder + +struct VP8Encoder { + const WebPConfig* config_; // user configuration and parameters + WebPPicture* pic_; // input / output picture + + // headers + VP8EncFilterHeader filter_hdr_; // filtering information + VP8EncSegmentHeader segment_hdr_; // segment information + + int profile_; // VP8's profile, deduced from Config. + + // dimension, in macroblock units. + int mb_w_, mb_h_; + int preds_w_; // stride of the *preds_ prediction plane (=4*mb_w + 1) + + // number of partitions (1, 2, 4 or 8 = MAX_NUM_PARTITIONS) + int num_parts_; + + // per-partition boolean decoders. + VP8BitWriter bw_; // part0 + VP8BitWriter parts_[MAX_NUM_PARTITIONS]; // token partitions + VP8TBuffer tokens_; // token buffer + + int percent_; // for progress + + // transparency blob + int has_alpha_; + uint8_t* alpha_data_; // non-NULL if transparency is present + uint32_t alpha_data_size_; + WebPWorker alpha_worker_; + + // quantization info (one set of DC/AC dequant factor per segment) + VP8SegmentInfo dqm_[NUM_MB_SEGMENTS]; + int base_quant_; // nominal quantizer value. Only used + // for relative coding of segments' quant. + int alpha_; // global susceptibility (<=> complexity) + int uv_alpha_; // U/V quantization susceptibility + // global offset of quantizers, shared by all segments + int dq_y1_dc_; + int dq_y2_dc_, dq_y2_ac_; + int dq_uv_dc_, dq_uv_ac_; + + // probabilities and statistics + VP8EncProba proba_; + uint64_t sse_[4]; // sum of Y/U/V/A squared errors for all macroblocks + uint64_t sse_count_; // pixel count for the sse_[] stats + int coded_size_; + int residual_bytes_[3][4]; + int block_count_[3]; + + // quality/speed settings + int method_; // 0=fastest, 6=best/slowest. + VP8RDLevel rd_opt_level_; // Deduced from method_. + int max_i4_header_bits_; // partition #0 safeness factor + int mb_header_limit_; // rough limit for header bits per MB + int thread_level_; // derived from config->thread_level + int do_search_; // derived from config->target_XXX + int use_tokens_; // if true, use token buffer + + // Memory + VP8MBInfo* mb_info_; // contextual macroblock infos (mb_w_ + 1) + uint8_t* preds_; // predictions modes: (4*mb_w+1) * (4*mb_h+1) + uint32_t* nz_; // non-zero bit context: mb_w+1 + uint8_t* y_top_; // top luma samples. + uint8_t* uv_top_; // top u/v samples. + // U and V are packed into 16 bytes (8 U + 8 V) + LFStats* lf_stats_; // autofilter stats (if NULL, autofilter is off) +}; + +//------------------------------------------------------------------------------ +// internal functions. Not public. + + // in tree.c +extern const uint8_t VP8CoeffsProba0[NUM_TYPES][NUM_BANDS][NUM_CTX][NUM_PROBAS]; +extern const uint8_t + VP8CoeffsUpdateProba[NUM_TYPES][NUM_BANDS][NUM_CTX][NUM_PROBAS]; +// Reset the token probabilities to their initial (default) values +void VP8DefaultProbas(VP8Encoder* const enc); +// Write the token probabilities +void VP8WriteProbas(VP8BitWriter* const bw, const VP8EncProba* const probas); +// Writes the partition #0 modes (that is: all intra modes) +void VP8CodeIntraModes(VP8Encoder* const enc); + + // in syntax.c +// Generates the final bitstream by coding the partition0 and headers, +// and appending an assembly of all the pre-coded token partitions. +// Return true if everything is ok. +int VP8EncWrite(VP8Encoder* const enc); +// Release memory allocated for bit-writing in VP8EncLoop & seq. +void VP8EncFreeBitWriters(VP8Encoder* const enc); + + // in frame.c +extern const uint8_t VP8Cat3[]; +extern const uint8_t VP8Cat4[]; +extern const uint8_t VP8Cat5[]; +extern const uint8_t VP8Cat6[]; + +// Form all the four Intra16x16 predictions in the yuv_p_ cache +void VP8MakeLuma16Preds(const VP8EncIterator* const it); +// Form all the four Chroma8x8 predictions in the yuv_p_ cache +void VP8MakeChroma8Preds(const VP8EncIterator* const it); +// Form all the ten Intra4x4 predictions in the yuv_p_ cache +// for the 4x4 block it->i4_ +void VP8MakeIntra4Preds(const VP8EncIterator* const it); +// Rate calculation +int VP8GetCostLuma16(VP8EncIterator* const it, const VP8ModeScore* const rd); +int VP8GetCostLuma4(VP8EncIterator* const it, const int16_t levels[16]); +int VP8GetCostUV(VP8EncIterator* const it, const VP8ModeScore* const rd); +// Main coding calls +int VP8EncLoop(VP8Encoder* const enc); +int VP8EncTokenLoop(VP8Encoder* const enc); + + // in webpenc.c +// Assign an error code to a picture. Return false for convenience. +int WebPEncodingSetError(const WebPPicture* const pic, WebPEncodingError error); +int WebPReportProgress(const WebPPicture* const pic, + int percent, int* const percent_store); + + // in analysis.c +// Main analysis loop. Decides the segmentations and complexity. +// Assigns a first guess for Intra16 and uvmode_ prediction modes. +int VP8EncAnalyze(VP8Encoder* const enc); + + // in quant.c +// Sets up segment's quantization values, base_quant_ and filter strengths. +void VP8SetSegmentParams(VP8Encoder* const enc, float quality); +// Pick best modes and fills the levels. Returns true if skipped. +int VP8Decimate(VP8EncIterator* const it, VP8ModeScore* const rd, + VP8RDLevel rd_opt); + + // in alpha.c +void VP8EncInitAlpha(VP8Encoder* const enc); // initialize alpha compression +int VP8EncStartAlpha(VP8Encoder* const enc); // start alpha coding process +int VP8EncFinishAlpha(VP8Encoder* const enc); // finalize compressed data +int VP8EncDeleteAlpha(VP8Encoder* const enc); // delete compressed data + +// autofilter +void VP8InitFilter(VP8EncIterator* const it); +void VP8StoreFilterStats(VP8EncIterator* const it); +void VP8AdjustFilterStrength(VP8EncIterator* const it); + +// returns the approximate filtering strength needed to smooth a edge +// step of 'delta', given a sharpness parameter 'sharpness'. +int VP8FilterStrengthFromDelta(int sharpness, int delta); + + // misc utils for picture_*.c: + +// Remove reference to the ARGB/YUVA buffer (doesn't free anything). +void WebPPictureResetBuffers(WebPPicture* const picture); + +// Allocates ARGB buffer of given dimension (previous one is always free'd). +// Preserves the YUV(A) buffer. Returns false in case of error (invalid param, +// out-of-memory). +int WebPPictureAllocARGB(WebPPicture* const picture, int width, int height); + +// Allocates YUVA buffer of given dimension (previous one is always free'd). +// Uses picture->csp to determine whether an alpha buffer is needed. +// Preserves the ARGB buffer. +// Returns false in case of error (invalid param, out-of-memory). +int WebPPictureAllocYUVA(WebPPicture* const picture, int width, int height); + +// Clean-up the RGB samples under fully transparent area, to help lossless +// compressibility (no guarantee, though). Assumes that pic->use_argb is true. +void WebPCleanupTransparentAreaLossless(WebPPicture* const pic); + + // in near_lossless.c +// Near lossless preprocessing in RGB color-space. +int VP8ApplyNearLossless(int xsize, int ysize, uint32_t* argb, int quality); +// Near lossless adjustment for predictors. +void VP8ApplyNearLosslessPredict(int xsize, int ysize, int pred_bits, + const uint32_t* argb_orig, + uint32_t* argb, uint32_t* argb_scratch, + const uint32_t* const transform_data, + int quality, int subtract_green); +//------------------------------------------------------------------------------ + +#ifdef __cplusplus +} // extern "C" +#endif + +#endif /* WEBP_ENC_VP8ENCI_H_ */ diff --git a/thirdparty/libwebp/enc/vp8l.c b/thirdparty/libwebp/enc/vp8l.c deleted file mode 100644 index e4ad2959b8..0000000000 --- a/thirdparty/libwebp/enc/vp8l.c +++ /dev/null @@ -1,1602 +0,0 @@ -// Copyright 2012 Google Inc. All Rights Reserved. -// -// Use of this source code is governed by a BSD-style license -// that can be found in the COPYING file in the root of the source -// tree. An additional intellectual property rights grant can be found -// in the file PATENTS. All contributing project authors may -// be found in the AUTHORS file in the root of the source tree. -// ----------------------------------------------------------------------------- -// -// main entry for the lossless encoder. -// -// Author: Vikas Arora (vikaas.arora@gmail.com) -// - -#include -#include - -#include "./backward_references.h" -#include "./histogram.h" -#include "./vp8enci.h" -#include "./vp8li.h" -#include "../dsp/lossless.h" -#include "../utils/bit_writer.h" -#include "../utils/huffman_encode.h" -#include "../utils/utils.h" -#include "../webp/format_constants.h" - -#include "./delta_palettization.h" - -#define PALETTE_KEY_RIGHT_SHIFT 22 // Key for 1K buffer. -// Maximum number of histogram images (sub-blocks). -#define MAX_HUFF_IMAGE_SIZE 2600 - -// Palette reordering for smaller sum of deltas (and for smaller storage). - -static int PaletteCompareColorsForQsort(const void* p1, const void* p2) { - const uint32_t a = WebPMemToUint32((uint8_t*)p1); - const uint32_t b = WebPMemToUint32((uint8_t*)p2); - assert(a != b); - return (a < b) ? -1 : 1; -} - -static WEBP_INLINE uint32_t PaletteComponentDistance(uint32_t v) { - return (v <= 128) ? v : (256 - v); -} - -// Computes a value that is related to the entropy created by the -// palette entry diff. -// -// Note that the last & 0xff is a no-operation in the next statement, but -// removed by most compilers and is here only for regularity of the code. -static WEBP_INLINE uint32_t PaletteColorDistance(uint32_t col1, uint32_t col2) { - const uint32_t diff = VP8LSubPixels(col1, col2); - const int kMoreWeightForRGBThanForAlpha = 9; - uint32_t score; - score = PaletteComponentDistance((diff >> 0) & 0xff); - score += PaletteComponentDistance((diff >> 8) & 0xff); - score += PaletteComponentDistance((diff >> 16) & 0xff); - score *= kMoreWeightForRGBThanForAlpha; - score += PaletteComponentDistance((diff >> 24) & 0xff); - return score; -} - -static WEBP_INLINE void SwapColor(uint32_t* const col1, uint32_t* const col2) { - const uint32_t tmp = *col1; - *col1 = *col2; - *col2 = tmp; -} - -static void GreedyMinimizeDeltas(uint32_t palette[], int num_colors) { - // Find greedily always the closest color of the predicted color to minimize - // deltas in the palette. This reduces storage needs since the - // palette is stored with delta encoding. - uint32_t predict = 0x00000000; - int i, k; - for (i = 0; i < num_colors; ++i) { - int best_ix = i; - uint32_t best_score = ~0U; - for (k = i; k < num_colors; ++k) { - const uint32_t cur_score = PaletteColorDistance(palette[k], predict); - if (best_score > cur_score) { - best_score = cur_score; - best_ix = k; - } - } - SwapColor(&palette[best_ix], &palette[i]); - predict = palette[i]; - } -} - -// The palette has been sorted by alpha. This function checks if the other -// components of the palette have a monotonic development with regards to -// position in the palette. If all have monotonic development, there is -// no benefit to re-organize them greedily. A monotonic development -// would be spotted in green-only situations (like lossy alpha) or gray-scale -// images. -static int PaletteHasNonMonotonousDeltas(uint32_t palette[], int num_colors) { - uint32_t predict = 0x000000; - int i; - uint8_t sign_found = 0x00; - for (i = 0; i < num_colors; ++i) { - const uint32_t diff = VP8LSubPixels(palette[i], predict); - const uint8_t rd = (diff >> 16) & 0xff; - const uint8_t gd = (diff >> 8) & 0xff; - const uint8_t bd = (diff >> 0) & 0xff; - if (rd != 0x00) { - sign_found |= (rd < 0x80) ? 1 : 2; - } - if (gd != 0x00) { - sign_found |= (gd < 0x80) ? 8 : 16; - } - if (bd != 0x00) { - sign_found |= (bd < 0x80) ? 64 : 128; - } - predict = palette[i]; - } - return (sign_found & (sign_found << 1)) != 0; // two consequent signs. -} - -// ----------------------------------------------------------------------------- -// Palette - -// If number of colors in the image is less than or equal to MAX_PALETTE_SIZE, -// creates a palette and returns true, else returns false. -static int AnalyzeAndCreatePalette(const WebPPicture* const pic, - int low_effort, - uint32_t palette[MAX_PALETTE_SIZE], - int* const palette_size) { - const int num_colors = WebPGetColorPalette(pic, palette); - if (num_colors > MAX_PALETTE_SIZE) return 0; - *palette_size = num_colors; - qsort(palette, num_colors, sizeof(*palette), PaletteCompareColorsForQsort); - if (!low_effort && PaletteHasNonMonotonousDeltas(palette, num_colors)) { - GreedyMinimizeDeltas(palette, num_colors); - } - return 1; -} - -// These five modes are evaluated and their respective entropy is computed. -typedef enum { - kDirect = 0, - kSpatial = 1, - kSubGreen = 2, - kSpatialSubGreen = 3, - kPalette = 4, - kNumEntropyIx = 5 -} EntropyIx; - -typedef enum { - kHistoAlpha = 0, - kHistoAlphaPred, - kHistoGreen, - kHistoGreenPred, - kHistoRed, - kHistoRedPred, - kHistoBlue, - kHistoBluePred, - kHistoRedSubGreen, - kHistoRedPredSubGreen, - kHistoBlueSubGreen, - kHistoBluePredSubGreen, - kHistoPalette, - kHistoTotal // Must be last. -} HistoIx; - -static void AddSingleSubGreen(uint32_t p, uint32_t* r, uint32_t* b) { - const uint32_t green = p >> 8; // The upper bits are masked away later. - ++r[((p >> 16) - green) & 0xff]; - ++b[(p - green) & 0xff]; -} - -static void AddSingle(uint32_t p, - uint32_t* a, uint32_t* r, uint32_t* g, uint32_t* b) { - ++a[p >> 24]; - ++r[(p >> 16) & 0xff]; - ++g[(p >> 8) & 0xff]; - ++b[(p & 0xff)]; -} - -static int AnalyzeEntropy(const uint32_t* argb, - int width, int height, int argb_stride, - int use_palette, - EntropyIx* const min_entropy_ix, - int* const red_and_blue_always_zero) { - // Allocate histogram set with cache_bits = 0. - uint32_t* const histo = - (uint32_t*)WebPSafeCalloc(kHistoTotal, sizeof(*histo) * 256); - if (histo != NULL) { - int i, x, y; - const uint32_t* prev_row = argb; - const uint32_t* curr_row = argb + argb_stride; - for (y = 1; y < height; ++y) { - uint32_t prev_pix = curr_row[0]; - for (x = 1; x < width; ++x) { - const uint32_t pix = curr_row[x]; - const uint32_t pix_diff = VP8LSubPixels(pix, prev_pix); - if ((pix_diff == 0) || (pix == prev_row[x])) continue; - prev_pix = pix; - AddSingle(pix, - &histo[kHistoAlpha * 256], - &histo[kHistoRed * 256], - &histo[kHistoGreen * 256], - &histo[kHistoBlue * 256]); - AddSingle(pix_diff, - &histo[kHistoAlphaPred * 256], - &histo[kHistoRedPred * 256], - &histo[kHistoGreenPred * 256], - &histo[kHistoBluePred * 256]); - AddSingleSubGreen(pix, - &histo[kHistoRedSubGreen * 256], - &histo[kHistoBlueSubGreen * 256]); - AddSingleSubGreen(pix_diff, - &histo[kHistoRedPredSubGreen * 256], - &histo[kHistoBluePredSubGreen * 256]); - { - // Approximate the palette by the entropy of the multiplicative hash. - const int hash = ((pix + (pix >> 19)) * 0x39c5fba7) >> 24; - ++histo[kHistoPalette * 256 + (hash & 0xff)]; - } - } - prev_row = curr_row; - curr_row += argb_stride; - } - { - double entropy_comp[kHistoTotal]; - double entropy[kNumEntropyIx]; - int k; - int last_mode_to_analyze = use_palette ? kPalette : kSpatialSubGreen; - int j; - // Let's add one zero to the predicted histograms. The zeros are removed - // too efficiently by the pix_diff == 0 comparison, at least one of the - // zeros is likely to exist. - ++histo[kHistoRedPredSubGreen * 256]; - ++histo[kHistoBluePredSubGreen * 256]; - ++histo[kHistoRedPred * 256]; - ++histo[kHistoGreenPred * 256]; - ++histo[kHistoBluePred * 256]; - ++histo[kHistoAlphaPred * 256]; - - for (j = 0; j < kHistoTotal; ++j) { - entropy_comp[j] = VP8LBitsEntropy(&histo[j * 256], 256, NULL); - } - entropy[kDirect] = entropy_comp[kHistoAlpha] + - entropy_comp[kHistoRed] + - entropy_comp[kHistoGreen] + - entropy_comp[kHistoBlue]; - entropy[kSpatial] = entropy_comp[kHistoAlphaPred] + - entropy_comp[kHistoRedPred] + - entropy_comp[kHistoGreenPred] + - entropy_comp[kHistoBluePred]; - entropy[kSubGreen] = entropy_comp[kHistoAlpha] + - entropy_comp[kHistoRedSubGreen] + - entropy_comp[kHistoGreen] + - entropy_comp[kHistoBlueSubGreen]; - entropy[kSpatialSubGreen] = entropy_comp[kHistoAlphaPred] + - entropy_comp[kHistoRedPredSubGreen] + - entropy_comp[kHistoGreenPred] + - entropy_comp[kHistoBluePredSubGreen]; - // Palette mode seems more efficient in a breakeven case. Bias with 1.0. - entropy[kPalette] = entropy_comp[kHistoPalette] - 1.0; - - *min_entropy_ix = kDirect; - for (k = kDirect + 1; k <= last_mode_to_analyze; ++k) { - if (entropy[*min_entropy_ix] > entropy[k]) { - *min_entropy_ix = (EntropyIx)k; - } - } - *red_and_blue_always_zero = 1; - // Let's check if the histogram of the chosen entropy mode has - // non-zero red and blue values. If all are zero, we can later skip - // the cross color optimization. - { - static const uint8_t kHistoPairs[5][2] = { - { kHistoRed, kHistoBlue }, - { kHistoRedPred, kHistoBluePred }, - { kHistoRedSubGreen, kHistoBlueSubGreen }, - { kHistoRedPredSubGreen, kHistoBluePredSubGreen }, - { kHistoRed, kHistoBlue } - }; - const uint32_t* const red_histo = - &histo[256 * kHistoPairs[*min_entropy_ix][0]]; - const uint32_t* const blue_histo = - &histo[256 * kHistoPairs[*min_entropy_ix][1]]; - for (i = 1; i < 256; ++i) { - if ((red_histo[i] | blue_histo[i]) != 0) { - *red_and_blue_always_zero = 0; - break; - } - } - } - } - WebPSafeFree(histo); - return 1; - } else { - return 0; - } -} - -static int GetHistoBits(int method, int use_palette, int width, int height) { - // Make tile size a function of encoding method (Range: 0 to 6). - int histo_bits = (use_palette ? 9 : 7) - method; - while (1) { - const int huff_image_size = VP8LSubSampleSize(width, histo_bits) * - VP8LSubSampleSize(height, histo_bits); - if (huff_image_size <= MAX_HUFF_IMAGE_SIZE) break; - ++histo_bits; - } - return (histo_bits < MIN_HUFFMAN_BITS) ? MIN_HUFFMAN_BITS : - (histo_bits > MAX_HUFFMAN_BITS) ? MAX_HUFFMAN_BITS : histo_bits; -} - -static int GetTransformBits(int method, int histo_bits) { - const int max_transform_bits = (method < 4) ? 6 : (method > 4) ? 4 : 5; - return (histo_bits > max_transform_bits) ? max_transform_bits : histo_bits; -} - -static int AnalyzeAndInit(VP8LEncoder* const enc) { - const WebPPicture* const pic = enc->pic_; - const int width = pic->width; - const int height = pic->height; - const int pix_cnt = width * height; - const WebPConfig* const config = enc->config_; - const int method = config->method; - const int low_effort = (config->method == 0); - // we round the block size up, so we're guaranteed to have - // at max MAX_REFS_BLOCK_PER_IMAGE blocks used: - int refs_block_size = (pix_cnt - 1) / MAX_REFS_BLOCK_PER_IMAGE + 1; - assert(pic != NULL && pic->argb != NULL); - - enc->use_cross_color_ = 0; - enc->use_predict_ = 0; - enc->use_subtract_green_ = 0; - enc->use_palette_ = - AnalyzeAndCreatePalette(pic, low_effort, - enc->palette_, &enc->palette_size_); - - // TODO(jyrki): replace the decision to be based on an actual estimate - // of entropy, or even spatial variance of entropy. - enc->histo_bits_ = GetHistoBits(method, enc->use_palette_, - pic->width, pic->height); - enc->transform_bits_ = GetTransformBits(method, enc->histo_bits_); - - if (low_effort) { - // AnalyzeEntropy is somewhat slow. - enc->use_predict_ = !enc->use_palette_; - enc->use_subtract_green_ = !enc->use_palette_; - enc->use_cross_color_ = 0; - } else { - int red_and_blue_always_zero; - EntropyIx min_entropy_ix; - if (!AnalyzeEntropy(pic->argb, width, height, pic->argb_stride, - enc->use_palette_, &min_entropy_ix, - &red_and_blue_always_zero)) { - return 0; - } - enc->use_palette_ = (min_entropy_ix == kPalette); - enc->use_subtract_green_ = - (min_entropy_ix == kSubGreen) || (min_entropy_ix == kSpatialSubGreen); - enc->use_predict_ = - (min_entropy_ix == kSpatial) || (min_entropy_ix == kSpatialSubGreen); - enc->use_cross_color_ = red_and_blue_always_zero ? 0 : enc->use_predict_; - } - - if (!VP8LHashChainInit(&enc->hash_chain_, pix_cnt)) return 0; - - // palette-friendly input typically uses less literals - // -> reduce block size a bit - if (enc->use_palette_) refs_block_size /= 2; - VP8LBackwardRefsInit(&enc->refs_[0], refs_block_size); - VP8LBackwardRefsInit(&enc->refs_[1], refs_block_size); - - return 1; -} - -// Returns false in case of memory error. -static int GetHuffBitLengthsAndCodes( - const VP8LHistogramSet* const histogram_image, - HuffmanTreeCode* const huffman_codes) { - int i, k; - int ok = 0; - uint64_t total_length_size = 0; - uint8_t* mem_buf = NULL; - const int histogram_image_size = histogram_image->size; - int max_num_symbols = 0; - uint8_t* buf_rle = NULL; - HuffmanTree* huff_tree = NULL; - - // Iterate over all histograms and get the aggregate number of codes used. - for (i = 0; i < histogram_image_size; ++i) { - const VP8LHistogram* const histo = histogram_image->histograms[i]; - HuffmanTreeCode* const codes = &huffman_codes[5 * i]; - for (k = 0; k < 5; ++k) { - const int num_symbols = - (k == 0) ? VP8LHistogramNumCodes(histo->palette_code_bits_) : - (k == 4) ? NUM_DISTANCE_CODES : 256; - codes[k].num_symbols = num_symbols; - total_length_size += num_symbols; - } - } - - // Allocate and Set Huffman codes. - { - uint16_t* codes; - uint8_t* lengths; - mem_buf = (uint8_t*)WebPSafeCalloc(total_length_size, - sizeof(*lengths) + sizeof(*codes)); - if (mem_buf == NULL) goto End; - - codes = (uint16_t*)mem_buf; - lengths = (uint8_t*)&codes[total_length_size]; - for (i = 0; i < 5 * histogram_image_size; ++i) { - const int bit_length = huffman_codes[i].num_symbols; - huffman_codes[i].codes = codes; - huffman_codes[i].code_lengths = lengths; - codes += bit_length; - lengths += bit_length; - if (max_num_symbols < bit_length) { - max_num_symbols = bit_length; - } - } - } - - buf_rle = (uint8_t*)WebPSafeMalloc(1ULL, max_num_symbols); - huff_tree = (HuffmanTree*)WebPSafeMalloc(3ULL * max_num_symbols, - sizeof(*huff_tree)); - if (buf_rle == NULL || huff_tree == NULL) goto End; - - // Create Huffman trees. - for (i = 0; i < histogram_image_size; ++i) { - HuffmanTreeCode* const codes = &huffman_codes[5 * i]; - VP8LHistogram* const histo = histogram_image->histograms[i]; - VP8LCreateHuffmanTree(histo->literal_, 15, buf_rle, huff_tree, codes + 0); - VP8LCreateHuffmanTree(histo->red_, 15, buf_rle, huff_tree, codes + 1); - VP8LCreateHuffmanTree(histo->blue_, 15, buf_rle, huff_tree, codes + 2); - VP8LCreateHuffmanTree(histo->alpha_, 15, buf_rle, huff_tree, codes + 3); - VP8LCreateHuffmanTree(histo->distance_, 15, buf_rle, huff_tree, codes + 4); - } - ok = 1; - End: - WebPSafeFree(huff_tree); - WebPSafeFree(buf_rle); - if (!ok) { - WebPSafeFree(mem_buf); - memset(huffman_codes, 0, 5 * histogram_image_size * sizeof(*huffman_codes)); - } - return ok; -} - -static void StoreHuffmanTreeOfHuffmanTreeToBitMask( - VP8LBitWriter* const bw, const uint8_t* code_length_bitdepth) { - // RFC 1951 will calm you down if you are worried about this funny sequence. - // This sequence is tuned from that, but more weighted for lower symbol count, - // and more spiking histograms. - static const uint8_t kStorageOrder[CODE_LENGTH_CODES] = { - 17, 18, 0, 1, 2, 3, 4, 5, 16, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 - }; - int i; - // Throw away trailing zeros: - int codes_to_store = CODE_LENGTH_CODES; - for (; codes_to_store > 4; --codes_to_store) { - if (code_length_bitdepth[kStorageOrder[codes_to_store - 1]] != 0) { - break; - } - } - VP8LPutBits(bw, codes_to_store - 4, 4); - for (i = 0; i < codes_to_store; ++i) { - VP8LPutBits(bw, code_length_bitdepth[kStorageOrder[i]], 3); - } -} - -static void ClearHuffmanTreeIfOnlyOneSymbol( - HuffmanTreeCode* const huffman_code) { - int k; - int count = 0; - for (k = 0; k < huffman_code->num_symbols; ++k) { - if (huffman_code->code_lengths[k] != 0) { - ++count; - if (count > 1) return; - } - } - for (k = 0; k < huffman_code->num_symbols; ++k) { - huffman_code->code_lengths[k] = 0; - huffman_code->codes[k] = 0; - } -} - -static void StoreHuffmanTreeToBitMask( - VP8LBitWriter* const bw, - const HuffmanTreeToken* const tokens, const int num_tokens, - const HuffmanTreeCode* const huffman_code) { - int i; - for (i = 0; i < num_tokens; ++i) { - const int ix = tokens[i].code; - const int extra_bits = tokens[i].extra_bits; - VP8LPutBits(bw, huffman_code->codes[ix], huffman_code->code_lengths[ix]); - switch (ix) { - case 16: - VP8LPutBits(bw, extra_bits, 2); - break; - case 17: - VP8LPutBits(bw, extra_bits, 3); - break; - case 18: - VP8LPutBits(bw, extra_bits, 7); - break; - } - } -} - -// 'huff_tree' and 'tokens' are pre-alloacted buffers. -static void StoreFullHuffmanCode(VP8LBitWriter* const bw, - HuffmanTree* const huff_tree, - HuffmanTreeToken* const tokens, - const HuffmanTreeCode* const tree) { - uint8_t code_length_bitdepth[CODE_LENGTH_CODES] = { 0 }; - uint16_t code_length_bitdepth_symbols[CODE_LENGTH_CODES] = { 0 }; - const int max_tokens = tree->num_symbols; - int num_tokens; - HuffmanTreeCode huffman_code; - huffman_code.num_symbols = CODE_LENGTH_CODES; - huffman_code.code_lengths = code_length_bitdepth; - huffman_code.codes = code_length_bitdepth_symbols; - - VP8LPutBits(bw, 0, 1); - num_tokens = VP8LCreateCompressedHuffmanTree(tree, tokens, max_tokens); - { - uint32_t histogram[CODE_LENGTH_CODES] = { 0 }; - uint8_t buf_rle[CODE_LENGTH_CODES] = { 0 }; - int i; - for (i = 0; i < num_tokens; ++i) { - ++histogram[tokens[i].code]; - } - - VP8LCreateHuffmanTree(histogram, 7, buf_rle, huff_tree, &huffman_code); - } - - StoreHuffmanTreeOfHuffmanTreeToBitMask(bw, code_length_bitdepth); - ClearHuffmanTreeIfOnlyOneSymbol(&huffman_code); - { - int trailing_zero_bits = 0; - int trimmed_length = num_tokens; - int write_trimmed_length; - int length; - int i = num_tokens; - while (i-- > 0) { - const int ix = tokens[i].code; - if (ix == 0 || ix == 17 || ix == 18) { - --trimmed_length; // discount trailing zeros - trailing_zero_bits += code_length_bitdepth[ix]; - if (ix == 17) { - trailing_zero_bits += 3; - } else if (ix == 18) { - trailing_zero_bits += 7; - } - } else { - break; - } - } - write_trimmed_length = (trimmed_length > 1 && trailing_zero_bits > 12); - length = write_trimmed_length ? trimmed_length : num_tokens; - VP8LPutBits(bw, write_trimmed_length, 1); - if (write_trimmed_length) { - const int nbits = VP8LBitsLog2Ceiling(trimmed_length - 1); - const int nbitpairs = (nbits == 0) ? 1 : (nbits + 1) / 2; - VP8LPutBits(bw, nbitpairs - 1, 3); - assert(trimmed_length >= 2); - VP8LPutBits(bw, trimmed_length - 2, nbitpairs * 2); - } - StoreHuffmanTreeToBitMask(bw, tokens, length, &huffman_code); - } -} - -// 'huff_tree' and 'tokens' are pre-alloacted buffers. -static void StoreHuffmanCode(VP8LBitWriter* const bw, - HuffmanTree* const huff_tree, - HuffmanTreeToken* const tokens, - const HuffmanTreeCode* const huffman_code) { - int i; - int count = 0; - int symbols[2] = { 0, 0 }; - const int kMaxBits = 8; - const int kMaxSymbol = 1 << kMaxBits; - - // Check whether it's a small tree. - for (i = 0; i < huffman_code->num_symbols && count < 3; ++i) { - if (huffman_code->code_lengths[i] != 0) { - if (count < 2) symbols[count] = i; - ++count; - } - } - - if (count == 0) { // emit minimal tree for empty cases - // bits: small tree marker: 1, count-1: 0, large 8-bit code: 0, code: 0 - VP8LPutBits(bw, 0x01, 4); - } else if (count <= 2 && symbols[0] < kMaxSymbol && symbols[1] < kMaxSymbol) { - VP8LPutBits(bw, 1, 1); // Small tree marker to encode 1 or 2 symbols. - VP8LPutBits(bw, count - 1, 1); - if (symbols[0] <= 1) { - VP8LPutBits(bw, 0, 1); // Code bit for small (1 bit) symbol value. - VP8LPutBits(bw, symbols[0], 1); - } else { - VP8LPutBits(bw, 1, 1); - VP8LPutBits(bw, symbols[0], 8); - } - if (count == 2) { - VP8LPutBits(bw, symbols[1], 8); - } - } else { - StoreFullHuffmanCode(bw, huff_tree, tokens, huffman_code); - } -} - -static WEBP_INLINE void WriteHuffmanCode(VP8LBitWriter* const bw, - const HuffmanTreeCode* const code, - int code_index) { - const int depth = code->code_lengths[code_index]; - const int symbol = code->codes[code_index]; - VP8LPutBits(bw, symbol, depth); -} - -static WEBP_INLINE void WriteHuffmanCodeWithExtraBits( - VP8LBitWriter* const bw, - const HuffmanTreeCode* const code, - int code_index, - int bits, - int n_bits) { - const int depth = code->code_lengths[code_index]; - const int symbol = code->codes[code_index]; - VP8LPutBits(bw, (bits << depth) | symbol, depth + n_bits); -} - -static WebPEncodingError StoreImageToBitMask( - VP8LBitWriter* const bw, int width, int histo_bits, - VP8LBackwardRefs* const refs, - const uint16_t* histogram_symbols, - const HuffmanTreeCode* const huffman_codes) { - const int histo_xsize = histo_bits ? VP8LSubSampleSize(width, histo_bits) : 1; - const int tile_mask = (histo_bits == 0) ? 0 : -(1 << histo_bits); - // x and y trace the position in the image. - int x = 0; - int y = 0; - int tile_x = x & tile_mask; - int tile_y = y & tile_mask; - int histogram_ix = histogram_symbols[0]; - const HuffmanTreeCode* codes = huffman_codes + 5 * histogram_ix; - VP8LRefsCursor c = VP8LRefsCursorInit(refs); - while (VP8LRefsCursorOk(&c)) { - const PixOrCopy* const v = c.cur_pos; - if ((tile_x != (x & tile_mask)) || (tile_y != (y & tile_mask))) { - tile_x = x & tile_mask; - tile_y = y & tile_mask; - histogram_ix = histogram_symbols[(y >> histo_bits) * histo_xsize + - (x >> histo_bits)]; - codes = huffman_codes + 5 * histogram_ix; - } - if (PixOrCopyIsLiteral(v)) { - static const int order[] = { 1, 2, 0, 3 }; - int k; - for (k = 0; k < 4; ++k) { - const int code = PixOrCopyLiteral(v, order[k]); - WriteHuffmanCode(bw, codes + k, code); - } - } else if (PixOrCopyIsCacheIdx(v)) { - const int code = PixOrCopyCacheIdx(v); - const int literal_ix = 256 + NUM_LENGTH_CODES + code; - WriteHuffmanCode(bw, codes, literal_ix); - } else { - int bits, n_bits; - int code; - - const int distance = PixOrCopyDistance(v); - VP8LPrefixEncode(v->len, &code, &n_bits, &bits); - WriteHuffmanCodeWithExtraBits(bw, codes, 256 + code, bits, n_bits); - - // Don't write the distance with the extra bits code since - // the distance can be up to 18 bits of extra bits, and the prefix - // 15 bits, totaling to 33, and our PutBits only supports up to 32 bits. - // TODO(jyrki): optimize this further. - VP8LPrefixEncode(distance, &code, &n_bits, &bits); - WriteHuffmanCode(bw, codes + 4, code); - VP8LPutBits(bw, bits, n_bits); - } - x += PixOrCopyLength(v); - while (x >= width) { - x -= width; - ++y; - } - VP8LRefsCursorNext(&c); - } - return bw->error_ ? VP8_ENC_ERROR_OUT_OF_MEMORY : VP8_ENC_OK; -} - -// Special case of EncodeImageInternal() for cache-bits=0, histo_bits=31 -static WebPEncodingError EncodeImageNoHuffman(VP8LBitWriter* const bw, - const uint32_t* const argb, - VP8LHashChain* const hash_chain, - VP8LBackwardRefs refs_array[2], - int width, int height, - int quality) { - int i; - int max_tokens = 0; - WebPEncodingError err = VP8_ENC_OK; - VP8LBackwardRefs* refs; - HuffmanTreeToken* tokens = NULL; - HuffmanTreeCode huffman_codes[5] = { { 0, NULL, NULL } }; - const uint16_t histogram_symbols[1] = { 0 }; // only one tree, one symbol - int cache_bits = 0; - VP8LHistogramSet* histogram_image = NULL; - HuffmanTree* const huff_tree = (HuffmanTree*)WebPSafeMalloc( - 3ULL * CODE_LENGTH_CODES, sizeof(*huff_tree)); - if (huff_tree == NULL) { - err = VP8_ENC_ERROR_OUT_OF_MEMORY; - goto Error; - } - - // Calculate backward references from ARGB image. - if (VP8LHashChainFill(hash_chain, quality, argb, width, height) == 0) { - err = VP8_ENC_ERROR_OUT_OF_MEMORY; - goto Error; - } - refs = VP8LGetBackwardReferences(width, height, argb, quality, 0, &cache_bits, - hash_chain, refs_array); - if (refs == NULL) { - err = VP8_ENC_ERROR_OUT_OF_MEMORY; - goto Error; - } - histogram_image = VP8LAllocateHistogramSet(1, cache_bits); - if (histogram_image == NULL) { - err = VP8_ENC_ERROR_OUT_OF_MEMORY; - goto Error; - } - - // Build histogram image and symbols from backward references. - VP8LHistogramStoreRefs(refs, histogram_image->histograms[0]); - - // Create Huffman bit lengths and codes for each histogram image. - assert(histogram_image->size == 1); - if (!GetHuffBitLengthsAndCodes(histogram_image, huffman_codes)) { - err = VP8_ENC_ERROR_OUT_OF_MEMORY; - goto Error; - } - - // No color cache, no Huffman image. - VP8LPutBits(bw, 0, 1); - - // Find maximum number of symbols for the huffman tree-set. - for (i = 0; i < 5; ++i) { - HuffmanTreeCode* const codes = &huffman_codes[i]; - if (max_tokens < codes->num_symbols) { - max_tokens = codes->num_symbols; - } - } - - tokens = (HuffmanTreeToken*)WebPSafeMalloc(max_tokens, sizeof(*tokens)); - if (tokens == NULL) { - err = VP8_ENC_ERROR_OUT_OF_MEMORY; - goto Error; - } - - // Store Huffman codes. - for (i = 0; i < 5; ++i) { - HuffmanTreeCode* const codes = &huffman_codes[i]; - StoreHuffmanCode(bw, huff_tree, tokens, codes); - ClearHuffmanTreeIfOnlyOneSymbol(codes); - } - - // Store actual literals. - err = StoreImageToBitMask(bw, width, 0, refs, histogram_symbols, - huffman_codes); - - Error: - WebPSafeFree(tokens); - WebPSafeFree(huff_tree); - VP8LFreeHistogramSet(histogram_image); - WebPSafeFree(huffman_codes[0].codes); - return err; -} - -static WebPEncodingError EncodeImageInternal(VP8LBitWriter* const bw, - const uint32_t* const argb, - VP8LHashChain* const hash_chain, - VP8LBackwardRefs refs_array[2], - int width, int height, int quality, - int low_effort, - int use_cache, int* cache_bits, - int histogram_bits, - size_t init_byte_position, - int* const hdr_size, - int* const data_size) { - WebPEncodingError err = VP8_ENC_OK; - const uint32_t histogram_image_xysize = - VP8LSubSampleSize(width, histogram_bits) * - VP8LSubSampleSize(height, histogram_bits); - VP8LHistogramSet* histogram_image = NULL; - VP8LHistogramSet* tmp_histos = NULL; - int histogram_image_size = 0; - size_t bit_array_size = 0; - HuffmanTree* huff_tree = NULL; - HuffmanTreeToken* tokens = NULL; - HuffmanTreeCode* huffman_codes = NULL; - VP8LBackwardRefs refs; - VP8LBackwardRefs* best_refs; - uint16_t* const histogram_symbols = - (uint16_t*)WebPSafeMalloc(histogram_image_xysize, - sizeof(*histogram_symbols)); - assert(histogram_bits >= MIN_HUFFMAN_BITS); - assert(histogram_bits <= MAX_HUFFMAN_BITS); - assert(hdr_size != NULL); - assert(data_size != NULL); - - VP8LBackwardRefsInit(&refs, refs_array[0].block_size_); - if (histogram_symbols == NULL) { - err = VP8_ENC_ERROR_OUT_OF_MEMORY; - goto Error; - } - - *cache_bits = use_cache ? MAX_COLOR_CACHE_BITS : 0; - // 'best_refs' is the reference to the best backward refs and points to one - // of refs_array[0] or refs_array[1]. - // Calculate backward references from ARGB image. - if (VP8LHashChainFill(hash_chain, quality, argb, width, height) == 0) { - err = VP8_ENC_ERROR_OUT_OF_MEMORY; - goto Error; - } - best_refs = VP8LGetBackwardReferences(width, height, argb, quality, - low_effort, cache_bits, hash_chain, - refs_array); - if (best_refs == NULL || !VP8LBackwardRefsCopy(best_refs, &refs)) { - err = VP8_ENC_ERROR_OUT_OF_MEMORY; - goto Error; - } - histogram_image = - VP8LAllocateHistogramSet(histogram_image_xysize, *cache_bits); - tmp_histos = VP8LAllocateHistogramSet(2, *cache_bits); - if (histogram_image == NULL || tmp_histos == NULL) { - err = VP8_ENC_ERROR_OUT_OF_MEMORY; - goto Error; - } - - // Build histogram image and symbols from backward references. - if (!VP8LGetHistoImageSymbols(width, height, &refs, quality, low_effort, - histogram_bits, *cache_bits, histogram_image, - tmp_histos, histogram_symbols)) { - err = VP8_ENC_ERROR_OUT_OF_MEMORY; - goto Error; - } - // Create Huffman bit lengths and codes for each histogram image. - histogram_image_size = histogram_image->size; - bit_array_size = 5 * histogram_image_size; - huffman_codes = (HuffmanTreeCode*)WebPSafeCalloc(bit_array_size, - sizeof(*huffman_codes)); - // Note: some histogram_image entries may point to tmp_histos[], so the latter - // need to outlive the following call to GetHuffBitLengthsAndCodes(). - if (huffman_codes == NULL || - !GetHuffBitLengthsAndCodes(histogram_image, huffman_codes)) { - err = VP8_ENC_ERROR_OUT_OF_MEMORY; - goto Error; - } - // Free combined histograms. - VP8LFreeHistogramSet(histogram_image); - histogram_image = NULL; - - // Free scratch histograms. - VP8LFreeHistogramSet(tmp_histos); - tmp_histos = NULL; - - // Color Cache parameters. - if (*cache_bits > 0) { - VP8LPutBits(bw, 1, 1); - VP8LPutBits(bw, *cache_bits, 4); - } else { - VP8LPutBits(bw, 0, 1); - } - - // Huffman image + meta huffman. - { - const int write_histogram_image = (histogram_image_size > 1); - VP8LPutBits(bw, write_histogram_image, 1); - if (write_histogram_image) { - uint32_t* const histogram_argb = - (uint32_t*)WebPSafeMalloc(histogram_image_xysize, - sizeof(*histogram_argb)); - int max_index = 0; - uint32_t i; - if (histogram_argb == NULL) { - err = VP8_ENC_ERROR_OUT_OF_MEMORY; - goto Error; - } - for (i = 0; i < histogram_image_xysize; ++i) { - const int symbol_index = histogram_symbols[i] & 0xffff; - histogram_argb[i] = (symbol_index << 8); - if (symbol_index >= max_index) { - max_index = symbol_index + 1; - } - } - histogram_image_size = max_index; - - VP8LPutBits(bw, histogram_bits - 2, 3); - err = EncodeImageNoHuffman(bw, histogram_argb, hash_chain, refs_array, - VP8LSubSampleSize(width, histogram_bits), - VP8LSubSampleSize(height, histogram_bits), - quality); - WebPSafeFree(histogram_argb); - if (err != VP8_ENC_OK) goto Error; - } - } - - // Store Huffman codes. - { - int i; - int max_tokens = 0; - huff_tree = (HuffmanTree*)WebPSafeMalloc(3ULL * CODE_LENGTH_CODES, - sizeof(*huff_tree)); - if (huff_tree == NULL) { - err = VP8_ENC_ERROR_OUT_OF_MEMORY; - goto Error; - } - // Find maximum number of symbols for the huffman tree-set. - for (i = 0; i < 5 * histogram_image_size; ++i) { - HuffmanTreeCode* const codes = &huffman_codes[i]; - if (max_tokens < codes->num_symbols) { - max_tokens = codes->num_symbols; - } - } - tokens = (HuffmanTreeToken*)WebPSafeMalloc(max_tokens, - sizeof(*tokens)); - if (tokens == NULL) { - err = VP8_ENC_ERROR_OUT_OF_MEMORY; - goto Error; - } - for (i = 0; i < 5 * histogram_image_size; ++i) { - HuffmanTreeCode* const codes = &huffman_codes[i]; - StoreHuffmanCode(bw, huff_tree, tokens, codes); - ClearHuffmanTreeIfOnlyOneSymbol(codes); - } - } - - *hdr_size = (int)(VP8LBitWriterNumBytes(bw) - init_byte_position); - // Store actual literals. - err = StoreImageToBitMask(bw, width, histogram_bits, &refs, - histogram_symbols, huffman_codes); - *data_size = - (int)(VP8LBitWriterNumBytes(bw) - init_byte_position - *hdr_size); - - Error: - WebPSafeFree(tokens); - WebPSafeFree(huff_tree); - VP8LFreeHistogramSet(histogram_image); - VP8LFreeHistogramSet(tmp_histos); - VP8LBackwardRefsClear(&refs); - if (huffman_codes != NULL) { - WebPSafeFree(huffman_codes->codes); - WebPSafeFree(huffman_codes); - } - WebPSafeFree(histogram_symbols); - return err; -} - -// ----------------------------------------------------------------------------- -// Transforms - -static void ApplySubtractGreen(VP8LEncoder* const enc, int width, int height, - VP8LBitWriter* const bw) { - VP8LPutBits(bw, TRANSFORM_PRESENT, 1); - VP8LPutBits(bw, SUBTRACT_GREEN, 2); - VP8LSubtractGreenFromBlueAndRed(enc->argb_, width * height); -} - -static WebPEncodingError ApplyPredictFilter(const VP8LEncoder* const enc, - int width, int height, - int quality, int low_effort, - int used_subtract_green, - VP8LBitWriter* const bw) { - const int pred_bits = enc->transform_bits_; - const int transform_width = VP8LSubSampleSize(width, pred_bits); - const int transform_height = VP8LSubSampleSize(height, pred_bits); - // we disable near-lossless quantization if palette is used. - const int near_lossless_strength = enc->use_palette_ ? 100 - : enc->config_->near_lossless; - - VP8LResidualImage(width, height, pred_bits, low_effort, enc->argb_, - enc->argb_scratch_, enc->transform_data_, - near_lossless_strength, enc->config_->exact, - used_subtract_green); - VP8LPutBits(bw, TRANSFORM_PRESENT, 1); - VP8LPutBits(bw, PREDICTOR_TRANSFORM, 2); - assert(pred_bits >= 2); - VP8LPutBits(bw, pred_bits - 2, 3); - return EncodeImageNoHuffman(bw, enc->transform_data_, - (VP8LHashChain*)&enc->hash_chain_, - (VP8LBackwardRefs*)enc->refs_, // cast const away - transform_width, transform_height, - quality); -} - -static WebPEncodingError ApplyCrossColorFilter(const VP8LEncoder* const enc, - int width, int height, - int quality, - VP8LBitWriter* const bw) { - const int ccolor_transform_bits = enc->transform_bits_; - const int transform_width = VP8LSubSampleSize(width, ccolor_transform_bits); - const int transform_height = VP8LSubSampleSize(height, ccolor_transform_bits); - - VP8LColorSpaceTransform(width, height, ccolor_transform_bits, quality, - enc->argb_, enc->transform_data_); - VP8LPutBits(bw, TRANSFORM_PRESENT, 1); - VP8LPutBits(bw, CROSS_COLOR_TRANSFORM, 2); - assert(ccolor_transform_bits >= 2); - VP8LPutBits(bw, ccolor_transform_bits - 2, 3); - return EncodeImageNoHuffman(bw, enc->transform_data_, - (VP8LHashChain*)&enc->hash_chain_, - (VP8LBackwardRefs*)enc->refs_, // cast const away - transform_width, transform_height, - quality); -} - -// ----------------------------------------------------------------------------- - -static WebPEncodingError WriteRiffHeader(const WebPPicture* const pic, - size_t riff_size, size_t vp8l_size) { - uint8_t riff[RIFF_HEADER_SIZE + CHUNK_HEADER_SIZE + VP8L_SIGNATURE_SIZE] = { - 'R', 'I', 'F', 'F', 0, 0, 0, 0, 'W', 'E', 'B', 'P', - 'V', 'P', '8', 'L', 0, 0, 0, 0, VP8L_MAGIC_BYTE, - }; - PutLE32(riff + TAG_SIZE, (uint32_t)riff_size); - PutLE32(riff + RIFF_HEADER_SIZE + TAG_SIZE, (uint32_t)vp8l_size); - if (!pic->writer(riff, sizeof(riff), pic)) { - return VP8_ENC_ERROR_BAD_WRITE; - } - return VP8_ENC_OK; -} - -static int WriteImageSize(const WebPPicture* const pic, - VP8LBitWriter* const bw) { - const int width = pic->width - 1; - const int height = pic->height - 1; - assert(width < WEBP_MAX_DIMENSION && height < WEBP_MAX_DIMENSION); - - VP8LPutBits(bw, width, VP8L_IMAGE_SIZE_BITS); - VP8LPutBits(bw, height, VP8L_IMAGE_SIZE_BITS); - return !bw->error_; -} - -static int WriteRealAlphaAndVersion(VP8LBitWriter* const bw, int has_alpha) { - VP8LPutBits(bw, has_alpha, 1); - VP8LPutBits(bw, VP8L_VERSION, VP8L_VERSION_BITS); - return !bw->error_; -} - -static WebPEncodingError WriteImage(const WebPPicture* const pic, - VP8LBitWriter* const bw, - size_t* const coded_size) { - WebPEncodingError err = VP8_ENC_OK; - const uint8_t* const webpll_data = VP8LBitWriterFinish(bw); - const size_t webpll_size = VP8LBitWriterNumBytes(bw); - const size_t vp8l_size = VP8L_SIGNATURE_SIZE + webpll_size; - const size_t pad = vp8l_size & 1; - const size_t riff_size = TAG_SIZE + CHUNK_HEADER_SIZE + vp8l_size + pad; - - err = WriteRiffHeader(pic, riff_size, vp8l_size); - if (err != VP8_ENC_OK) goto Error; - - if (!pic->writer(webpll_data, webpll_size, pic)) { - err = VP8_ENC_ERROR_BAD_WRITE; - goto Error; - } - - if (pad) { - const uint8_t pad_byte[1] = { 0 }; - if (!pic->writer(pad_byte, 1, pic)) { - err = VP8_ENC_ERROR_BAD_WRITE; - goto Error; - } - } - *coded_size = CHUNK_HEADER_SIZE + riff_size; - return VP8_ENC_OK; - - Error: - return err; -} - -// ----------------------------------------------------------------------------- - -static void ClearTransformBuffer(VP8LEncoder* const enc) { - WebPSafeFree(enc->transform_mem_); - enc->transform_mem_ = NULL; - enc->transform_mem_size_ = 0; -} - -// Allocates the memory for argb (W x H) buffer, 2 rows of context for -// prediction and transform data. -// Flags influencing the memory allocated: -// enc->transform_bits_ -// enc->use_predict_, enc->use_cross_color_ -static WebPEncodingError AllocateTransformBuffer(VP8LEncoder* const enc, - int width, int height) { - WebPEncodingError err = VP8_ENC_OK; - const uint64_t image_size = width * height; - // VP8LResidualImage needs room for 2 scanlines of uint32 pixels with an extra - // pixel in each, plus 2 regular scanlines of bytes. - // TODO(skal): Clean up by using arithmetic in bytes instead of words. - const uint64_t argb_scratch_size = - enc->use_predict_ - ? (width + 1) * 2 + - (width * 2 + sizeof(uint32_t) - 1) / sizeof(uint32_t) - : 0; - const uint64_t transform_data_size = - (enc->use_predict_ || enc->use_cross_color_) - ? VP8LSubSampleSize(width, enc->transform_bits_) * - VP8LSubSampleSize(height, enc->transform_bits_) - : 0; - const uint64_t max_alignment_in_words = - (WEBP_ALIGN_CST + sizeof(uint32_t) - 1) / sizeof(uint32_t); - const uint64_t mem_size = - image_size + max_alignment_in_words + - argb_scratch_size + max_alignment_in_words + - transform_data_size; - uint32_t* mem = enc->transform_mem_; - if (mem == NULL || mem_size > enc->transform_mem_size_) { - ClearTransformBuffer(enc); - mem = (uint32_t*)WebPSafeMalloc(mem_size, sizeof(*mem)); - if (mem == NULL) { - err = VP8_ENC_ERROR_OUT_OF_MEMORY; - goto Error; - } - enc->transform_mem_ = mem; - enc->transform_mem_size_ = (size_t)mem_size; - } - enc->argb_ = mem; - mem = (uint32_t*)WEBP_ALIGN(mem + image_size); - enc->argb_scratch_ = mem; - mem = (uint32_t*)WEBP_ALIGN(mem + argb_scratch_size); - enc->transform_data_ = mem; - - enc->current_width_ = width; - Error: - return err; -} - -static WebPEncodingError MakeInputImageCopy(VP8LEncoder* const enc) { - WebPEncodingError err = VP8_ENC_OK; - const WebPPicture* const picture = enc->pic_; - const int width = picture->width; - const int height = picture->height; - int y; - err = AllocateTransformBuffer(enc, width, height); - if (err != VP8_ENC_OK) return err; - for (y = 0; y < height; ++y) { - memcpy(enc->argb_ + y * width, - picture->argb + y * picture->argb_stride, - width * sizeof(*enc->argb_)); - } - assert(enc->current_width_ == width); - return VP8_ENC_OK; -} - -// ----------------------------------------------------------------------------- - -static int SearchColor(const uint32_t sorted[], uint32_t color, int hi) { - int low = 0; - if (sorted[low] == color) return low; // loop invariant: sorted[low] != color - while (1) { - const int mid = (low + hi) >> 1; - if (sorted[mid] == color) { - return mid; - } else if (sorted[mid] < color) { - low = mid; - } else { - hi = mid; - } - } -} - -// Sort palette in increasing order and prepare an inverse mapping array. -static void PrepareMapToPalette(const uint32_t palette[], int num_colors, - uint32_t sorted[], int idx_map[]) { - int i; - memcpy(sorted, palette, num_colors * sizeof(*sorted)); - qsort(sorted, num_colors, sizeof(*sorted), PaletteCompareColorsForQsort); - for (i = 0; i < num_colors; ++i) { - idx_map[SearchColor(sorted, palette[i], num_colors)] = i; - } -} - -static void MapToPalette(const uint32_t sorted_palette[], int num_colors, - uint32_t* const last_pix, int* const last_idx, - const int idx_map[], - const uint32_t* src, uint8_t* dst, int width) { - int x; - int prev_idx = *last_idx; - uint32_t prev_pix = *last_pix; - for (x = 0; x < width; ++x) { - const uint32_t pix = src[x]; - if (pix != prev_pix) { - prev_idx = idx_map[SearchColor(sorted_palette, pix, num_colors)]; - prev_pix = pix; - } - dst[x] = prev_idx; - } - *last_idx = prev_idx; - *last_pix = prev_pix; -} - -// Remap argb values in src[] to packed palettes entries in dst[] -// using 'row' as a temporary buffer of size 'width'. -// We assume that all src[] values have a corresponding entry in the palette. -// Note: src[] can be the same as dst[] -static WebPEncodingError ApplyPalette(const uint32_t* src, uint32_t src_stride, - uint32_t* dst, uint32_t dst_stride, - const uint32_t* palette, int palette_size, - int width, int height, int xbits) { - // TODO(skal): this tmp buffer is not needed if VP8LBundleColorMap() can be - // made to work in-place. - uint8_t* const tmp_row = (uint8_t*)WebPSafeMalloc(width, sizeof(*tmp_row)); - int i, x, y; - int use_LUT = 1; - - if (tmp_row == NULL) return VP8_ENC_ERROR_OUT_OF_MEMORY; - for (i = 0; i < palette_size; ++i) { - if ((palette[i] & 0xffff00ffu) != 0) { - use_LUT = 0; - break; - } - } - - if (use_LUT) { - uint8_t inv_palette[MAX_PALETTE_SIZE] = { 0 }; - for (i = 0; i < palette_size; ++i) { - const int color = (palette[i] >> 8) & 0xff; - inv_palette[color] = i; - } - for (y = 0; y < height; ++y) { - for (x = 0; x < width; ++x) { - const int color = (src[x] >> 8) & 0xff; - tmp_row[x] = inv_palette[color]; - } - VP8LBundleColorMap(tmp_row, width, xbits, dst); - src += src_stride; - dst += dst_stride; - } - } else { - // Use 1 pixel cache for ARGB pixels. - uint32_t last_pix; - int last_idx; - uint32_t sorted[MAX_PALETTE_SIZE]; - int idx_map[MAX_PALETTE_SIZE]; - PrepareMapToPalette(palette, palette_size, sorted, idx_map); - last_pix = palette[0]; - last_idx = 0; - for (y = 0; y < height; ++y) { - MapToPalette(sorted, palette_size, &last_pix, &last_idx, - idx_map, src, tmp_row, width); - VP8LBundleColorMap(tmp_row, width, xbits, dst); - src += src_stride; - dst += dst_stride; - } - } - WebPSafeFree(tmp_row); - return VP8_ENC_OK; -} - -// Note: Expects "enc->palette_" to be set properly. -static WebPEncodingError MapImageFromPalette(VP8LEncoder* const enc, - int in_place) { - WebPEncodingError err = VP8_ENC_OK; - const WebPPicture* const pic = enc->pic_; - const int width = pic->width; - const int height = pic->height; - const uint32_t* const palette = enc->palette_; - const uint32_t* src = in_place ? enc->argb_ : pic->argb; - const int src_stride = in_place ? enc->current_width_ : pic->argb_stride; - const int palette_size = enc->palette_size_; - int xbits; - - // Replace each input pixel by corresponding palette index. - // This is done line by line. - if (palette_size <= 4) { - xbits = (palette_size <= 2) ? 3 : 2; - } else { - xbits = (palette_size <= 16) ? 1 : 0; - } - - err = AllocateTransformBuffer(enc, VP8LSubSampleSize(width, xbits), height); - if (err != VP8_ENC_OK) return err; - - err = ApplyPalette(src, src_stride, - enc->argb_, enc->current_width_, - palette, palette_size, width, height, xbits); - return err; -} - -// Save palette_[] to bitstream. -static WebPEncodingError EncodePalette(VP8LBitWriter* const bw, - VP8LEncoder* const enc) { - int i; - uint32_t tmp_palette[MAX_PALETTE_SIZE]; - const int palette_size = enc->palette_size_; - const uint32_t* const palette = enc->palette_; - VP8LPutBits(bw, TRANSFORM_PRESENT, 1); - VP8LPutBits(bw, COLOR_INDEXING_TRANSFORM, 2); - assert(palette_size >= 1 && palette_size <= MAX_PALETTE_SIZE); - VP8LPutBits(bw, palette_size - 1, 8); - for (i = palette_size - 1; i >= 1; --i) { - tmp_palette[i] = VP8LSubPixels(palette[i], palette[i - 1]); - } - tmp_palette[0] = palette[0]; - return EncodeImageNoHuffman(bw, tmp_palette, &enc->hash_chain_, enc->refs_, - palette_size, 1, 20 /* quality */); -} - -#ifdef WEBP_EXPERIMENTAL_FEATURES - -static WebPEncodingError EncodeDeltaPalettePredictorImage( - VP8LBitWriter* const bw, VP8LEncoder* const enc, int quality) { - const WebPPicture* const pic = enc->pic_; - const int width = pic->width; - const int height = pic->height; - - const int pred_bits = 5; - const int transform_width = VP8LSubSampleSize(width, pred_bits); - const int transform_height = VP8LSubSampleSize(height, pred_bits); - const int pred = 7; // default is Predictor7 (Top/Left Average) - const int tiles_per_row = VP8LSubSampleSize(width, pred_bits); - const int tiles_per_col = VP8LSubSampleSize(height, pred_bits); - uint32_t* predictors; - int tile_x, tile_y; - WebPEncodingError err = VP8_ENC_OK; - - predictors = (uint32_t*)WebPSafeMalloc(tiles_per_col * tiles_per_row, - sizeof(*predictors)); - if (predictors == NULL) return VP8_ENC_ERROR_OUT_OF_MEMORY; - - for (tile_y = 0; tile_y < tiles_per_col; ++tile_y) { - for (tile_x = 0; tile_x < tiles_per_row; ++tile_x) { - predictors[tile_y * tiles_per_row + tile_x] = 0xff000000u | (pred << 8); - } - } - - VP8LPutBits(bw, TRANSFORM_PRESENT, 1); - VP8LPutBits(bw, PREDICTOR_TRANSFORM, 2); - VP8LPutBits(bw, pred_bits - 2, 3); - err = EncodeImageNoHuffman(bw, predictors, &enc->hash_chain_, - (VP8LBackwardRefs*)enc->refs_, // cast const away - transform_width, transform_height, - quality); - WebPSafeFree(predictors); - return err; -} - -#endif // WEBP_EXPERIMENTAL_FEATURES - -// ----------------------------------------------------------------------------- -// VP8LEncoder - -static VP8LEncoder* VP8LEncoderNew(const WebPConfig* const config, - const WebPPicture* const picture) { - VP8LEncoder* const enc = (VP8LEncoder*)WebPSafeCalloc(1ULL, sizeof(*enc)); - if (enc == NULL) { - WebPEncodingSetError(picture, VP8_ENC_ERROR_OUT_OF_MEMORY); - return NULL; - } - enc->config_ = config; - enc->pic_ = picture; - - VP8LEncDspInit(); - - return enc; -} - -static void VP8LEncoderDelete(VP8LEncoder* enc) { - if (enc != NULL) { - VP8LHashChainClear(&enc->hash_chain_); - VP8LBackwardRefsClear(&enc->refs_[0]); - VP8LBackwardRefsClear(&enc->refs_[1]); - ClearTransformBuffer(enc); - WebPSafeFree(enc); - } -} - -// ----------------------------------------------------------------------------- -// Main call - -WebPEncodingError VP8LEncodeStream(const WebPConfig* const config, - const WebPPicture* const picture, - VP8LBitWriter* const bw, int use_cache) { - WebPEncodingError err = VP8_ENC_OK; - const int quality = (int)config->quality; - const int low_effort = (config->method == 0); - const int width = picture->width; - const int height = picture->height; - VP8LEncoder* const enc = VP8LEncoderNew(config, picture); - const size_t byte_position = VP8LBitWriterNumBytes(bw); - int use_near_lossless = 0; - int hdr_size = 0; - int data_size = 0; - int use_delta_palettization = 0; - - if (enc == NULL) { - err = VP8_ENC_ERROR_OUT_OF_MEMORY; - goto Error; - } - - // --------------------------------------------------------------------------- - // Analyze image (entropy, num_palettes etc) - - if (!AnalyzeAndInit(enc)) { - err = VP8_ENC_ERROR_OUT_OF_MEMORY; - goto Error; - } - - // Apply near-lossless preprocessing. - use_near_lossless = - (config->near_lossless < 100) && !enc->use_palette_ && !enc->use_predict_; - if (use_near_lossless) { - if (!VP8ApplyNearLossless(width, height, picture->argb, - config->near_lossless)) { - err = VP8_ENC_ERROR_OUT_OF_MEMORY; - goto Error; - } - } - -#ifdef WEBP_EXPERIMENTAL_FEATURES - if (config->delta_palettization) { - enc->use_predict_ = 1; - enc->use_cross_color_ = 0; - enc->use_subtract_green_ = 0; - enc->use_palette_ = 1; - err = MakeInputImageCopy(enc); - if (err != VP8_ENC_OK) goto Error; - err = WebPSearchOptimalDeltaPalette(enc); - if (err != VP8_ENC_OK) goto Error; - if (enc->use_palette_) { - err = AllocateTransformBuffer(enc, width, height); - if (err != VP8_ENC_OK) goto Error; - err = EncodeDeltaPalettePredictorImage(bw, enc, quality); - if (err != VP8_ENC_OK) goto Error; - use_delta_palettization = 1; - } - } -#endif // WEBP_EXPERIMENTAL_FEATURES - - // Encode palette - if (enc->use_palette_) { - err = EncodePalette(bw, enc); - if (err != VP8_ENC_OK) goto Error; - err = MapImageFromPalette(enc, use_delta_palettization); - if (err != VP8_ENC_OK) goto Error; - } - if (!use_delta_palettization) { - // In case image is not packed. - if (enc->argb_ == NULL) { - err = MakeInputImageCopy(enc); - if (err != VP8_ENC_OK) goto Error; - } - - // ------------------------------------------------------------------------- - // Apply transforms and write transform data. - - if (enc->use_subtract_green_) { - ApplySubtractGreen(enc, enc->current_width_, height, bw); - } - - if (enc->use_predict_) { - err = ApplyPredictFilter(enc, enc->current_width_, height, quality, - low_effort, enc->use_subtract_green_, bw); - if (err != VP8_ENC_OK) goto Error; - } - - if (enc->use_cross_color_) { - err = ApplyCrossColorFilter(enc, enc->current_width_, - height, quality, bw); - if (err != VP8_ENC_OK) goto Error; - } - } - - VP8LPutBits(bw, !TRANSFORM_PRESENT, 1); // No more transforms. - - // --------------------------------------------------------------------------- - // Encode and write the transformed image. - err = EncodeImageInternal(bw, enc->argb_, &enc->hash_chain_, enc->refs_, - enc->current_width_, height, quality, low_effort, - use_cache, &enc->cache_bits_, enc->histo_bits_, - byte_position, &hdr_size, &data_size); - if (err != VP8_ENC_OK) goto Error; - - if (picture->stats != NULL) { - WebPAuxStats* const stats = picture->stats; - stats->lossless_features = 0; - if (enc->use_predict_) stats->lossless_features |= 1; - if (enc->use_cross_color_) stats->lossless_features |= 2; - if (enc->use_subtract_green_) stats->lossless_features |= 4; - if (enc->use_palette_) stats->lossless_features |= 8; - stats->histogram_bits = enc->histo_bits_; - stats->transform_bits = enc->transform_bits_; - stats->cache_bits = enc->cache_bits_; - stats->palette_size = enc->palette_size_; - stats->lossless_size = (int)(VP8LBitWriterNumBytes(bw) - byte_position); - stats->lossless_hdr_size = hdr_size; - stats->lossless_data_size = data_size; - } - - Error: - VP8LEncoderDelete(enc); - return err; -} - -int VP8LEncodeImage(const WebPConfig* const config, - const WebPPicture* const picture) { - int width, height; - int has_alpha; - size_t coded_size; - int percent = 0; - int initial_size; - WebPEncodingError err = VP8_ENC_OK; - VP8LBitWriter bw; - - if (picture == NULL) return 0; - - if (config == NULL || picture->argb == NULL) { - err = VP8_ENC_ERROR_NULL_PARAMETER; - WebPEncodingSetError(picture, err); - return 0; - } - - width = picture->width; - height = picture->height; - // Initialize BitWriter with size corresponding to 16 bpp to photo images and - // 8 bpp for graphical images. - initial_size = (config->image_hint == WEBP_HINT_GRAPH) ? - width * height : width * height * 2; - if (!VP8LBitWriterInit(&bw, initial_size)) { - err = VP8_ENC_ERROR_OUT_OF_MEMORY; - goto Error; - } - - if (!WebPReportProgress(picture, 1, &percent)) { - UserAbort: - err = VP8_ENC_ERROR_USER_ABORT; - goto Error; - } - // Reset stats (for pure lossless coding) - if (picture->stats != NULL) { - WebPAuxStats* const stats = picture->stats; - memset(stats, 0, sizeof(*stats)); - stats->PSNR[0] = 99.f; - stats->PSNR[1] = 99.f; - stats->PSNR[2] = 99.f; - stats->PSNR[3] = 99.f; - stats->PSNR[4] = 99.f; - } - - // Write image size. - if (!WriteImageSize(picture, &bw)) { - err = VP8_ENC_ERROR_OUT_OF_MEMORY; - goto Error; - } - - has_alpha = WebPPictureHasTransparency(picture); - // Write the non-trivial Alpha flag and lossless version. - if (!WriteRealAlphaAndVersion(&bw, has_alpha)) { - err = VP8_ENC_ERROR_OUT_OF_MEMORY; - goto Error; - } - - if (!WebPReportProgress(picture, 5, &percent)) goto UserAbort; - - // Encode main image stream. - err = VP8LEncodeStream(config, picture, &bw, 1 /*use_cache*/); - if (err != VP8_ENC_OK) goto Error; - - // TODO(skal): have a fine-grained progress report in VP8LEncodeStream(). - if (!WebPReportProgress(picture, 90, &percent)) goto UserAbort; - - // Finish the RIFF chunk. - err = WriteImage(picture, &bw, &coded_size); - if (err != VP8_ENC_OK) goto Error; - - if (!WebPReportProgress(picture, 100, &percent)) goto UserAbort; - - // Save size. - if (picture->stats != NULL) { - picture->stats->coded_size += (int)coded_size; - picture->stats->lossless_size = (int)coded_size; - } - - if (picture->extra_info != NULL) { - const int mb_w = (width + 15) >> 4; - const int mb_h = (height + 15) >> 4; - memset(picture->extra_info, 0, mb_w * mb_h * sizeof(*picture->extra_info)); - } - - Error: - if (bw.error_) err = VP8_ENC_ERROR_OUT_OF_MEMORY; - VP8LBitWriterWipeOut(&bw); - if (err != VP8_ENC_OK) { - WebPEncodingSetError(picture, err); - return 0; - } - return 1; -} - -//------------------------------------------------------------------------------ diff --git a/thirdparty/libwebp/enc/vp8l_enc.c b/thirdparty/libwebp/enc/vp8l_enc.c new file mode 100644 index 0000000000..b1a793d956 --- /dev/null +++ b/thirdparty/libwebp/enc/vp8l_enc.c @@ -0,0 +1,1667 @@ +// Copyright 2012 Google Inc. All Rights Reserved. +// +// Use of this source code is governed by a BSD-style license +// that can be found in the COPYING file in the root of the source +// tree. An additional intellectual property rights grant can be found +// in the file PATENTS. All contributing project authors may +// be found in the AUTHORS file in the root of the source tree. +// ----------------------------------------------------------------------------- +// +// main entry for the lossless encoder. +// +// Author: Vikas Arora (vikaas.arora@gmail.com) +// + +#include +#include + +#include "./backward_references_enc.h" +#include "./histogram_enc.h" +#include "./vp8i_enc.h" +#include "./vp8li_enc.h" +#include "../dsp/lossless.h" +#include "../dsp/lossless_common.h" +#include "../utils/bit_writer_utils.h" +#include "../utils/huffman_encode_utils.h" +#include "../utils/utils.h" +#include "../webp/format_constants.h" + +#include "./delta_palettization_enc.h" + +#define PALETTE_KEY_RIGHT_SHIFT 22 // Key for 1K buffer. +// Maximum number of histogram images (sub-blocks). +#define MAX_HUFF_IMAGE_SIZE 2600 + +// Palette reordering for smaller sum of deltas (and for smaller storage). + +static int PaletteCompareColorsForQsort(const void* p1, const void* p2) { + const uint32_t a = WebPMemToUint32((uint8_t*)p1); + const uint32_t b = WebPMemToUint32((uint8_t*)p2); + assert(a != b); + return (a < b) ? -1 : 1; +} + +static WEBP_INLINE uint32_t PaletteComponentDistance(uint32_t v) { + return (v <= 128) ? v : (256 - v); +} + +// Computes a value that is related to the entropy created by the +// palette entry diff. +// +// Note that the last & 0xff is a no-operation in the next statement, but +// removed by most compilers and is here only for regularity of the code. +static WEBP_INLINE uint32_t PaletteColorDistance(uint32_t col1, uint32_t col2) { + const uint32_t diff = VP8LSubPixels(col1, col2); + const int kMoreWeightForRGBThanForAlpha = 9; + uint32_t score; + score = PaletteComponentDistance((diff >> 0) & 0xff); + score += PaletteComponentDistance((diff >> 8) & 0xff); + score += PaletteComponentDistance((diff >> 16) & 0xff); + score *= kMoreWeightForRGBThanForAlpha; + score += PaletteComponentDistance((diff >> 24) & 0xff); + return score; +} + +static WEBP_INLINE void SwapColor(uint32_t* const col1, uint32_t* const col2) { + const uint32_t tmp = *col1; + *col1 = *col2; + *col2 = tmp; +} + +static void GreedyMinimizeDeltas(uint32_t palette[], int num_colors) { + // Find greedily always the closest color of the predicted color to minimize + // deltas in the palette. This reduces storage needs since the + // palette is stored with delta encoding. + uint32_t predict = 0x00000000; + int i, k; + for (i = 0; i < num_colors; ++i) { + int best_ix = i; + uint32_t best_score = ~0U; + for (k = i; k < num_colors; ++k) { + const uint32_t cur_score = PaletteColorDistance(palette[k], predict); + if (best_score > cur_score) { + best_score = cur_score; + best_ix = k; + } + } + SwapColor(&palette[best_ix], &palette[i]); + predict = palette[i]; + } +} + +// The palette has been sorted by alpha. This function checks if the other +// components of the palette have a monotonic development with regards to +// position in the palette. If all have monotonic development, there is +// no benefit to re-organize them greedily. A monotonic development +// would be spotted in green-only situations (like lossy alpha) or gray-scale +// images. +static int PaletteHasNonMonotonousDeltas(uint32_t palette[], int num_colors) { + uint32_t predict = 0x000000; + int i; + uint8_t sign_found = 0x00; + for (i = 0; i < num_colors; ++i) { + const uint32_t diff = VP8LSubPixels(palette[i], predict); + const uint8_t rd = (diff >> 16) & 0xff; + const uint8_t gd = (diff >> 8) & 0xff; + const uint8_t bd = (diff >> 0) & 0xff; + if (rd != 0x00) { + sign_found |= (rd < 0x80) ? 1 : 2; + } + if (gd != 0x00) { + sign_found |= (gd < 0x80) ? 8 : 16; + } + if (bd != 0x00) { + sign_found |= (bd < 0x80) ? 64 : 128; + } + predict = palette[i]; + } + return (sign_found & (sign_found << 1)) != 0; // two consequent signs. +} + +// ----------------------------------------------------------------------------- +// Palette + +// If number of colors in the image is less than or equal to MAX_PALETTE_SIZE, +// creates a palette and returns true, else returns false. +static int AnalyzeAndCreatePalette(const WebPPicture* const pic, + int low_effort, + uint32_t palette[MAX_PALETTE_SIZE], + int* const palette_size) { + const int num_colors = WebPGetColorPalette(pic, palette); + if (num_colors > MAX_PALETTE_SIZE) return 0; + *palette_size = num_colors; + qsort(palette, num_colors, sizeof(*palette), PaletteCompareColorsForQsort); + if (!low_effort && PaletteHasNonMonotonousDeltas(palette, num_colors)) { + GreedyMinimizeDeltas(palette, num_colors); + } + return 1; +} + +// These five modes are evaluated and their respective entropy is computed. +typedef enum { + kDirect = 0, + kSpatial = 1, + kSubGreen = 2, + kSpatialSubGreen = 3, + kPalette = 4, + kNumEntropyIx = 5 +} EntropyIx; + +typedef enum { + kHistoAlpha = 0, + kHistoAlphaPred, + kHistoGreen, + kHistoGreenPred, + kHistoRed, + kHistoRedPred, + kHistoBlue, + kHistoBluePred, + kHistoRedSubGreen, + kHistoRedPredSubGreen, + kHistoBlueSubGreen, + kHistoBluePredSubGreen, + kHistoPalette, + kHistoTotal // Must be last. +} HistoIx; + +static void AddSingleSubGreen(int p, uint32_t* const r, uint32_t* const b) { + const int green = p >> 8; // The upper bits are masked away later. + ++r[((p >> 16) - green) & 0xff]; + ++b[((p >> 0) - green) & 0xff]; +} + +static void AddSingle(uint32_t p, + uint32_t* const a, uint32_t* const r, + uint32_t* const g, uint32_t* const b) { + ++a[(p >> 24) & 0xff]; + ++r[(p >> 16) & 0xff]; + ++g[(p >> 8) & 0xff]; + ++b[(p >> 0) & 0xff]; +} + +static WEBP_INLINE uint32_t HashPix(uint32_t pix) { + // Note that masking with 0xffffffffu is for preventing an + // 'unsigned int overflow' warning. Doesn't impact the compiled code. + return ((((uint64_t)pix + (pix >> 19)) * 0x39c5fba7ull) & 0xffffffffu) >> 24; +} + +static int AnalyzeEntropy(const uint32_t* argb, + int width, int height, int argb_stride, + int use_palette, + EntropyIx* const min_entropy_ix, + int* const red_and_blue_always_zero) { + // Allocate histogram set with cache_bits = 0. + uint32_t* const histo = + (uint32_t*)WebPSafeCalloc(kHistoTotal, sizeof(*histo) * 256); + if (histo != NULL) { + int i, x, y; + const uint32_t* prev_row = argb; + const uint32_t* curr_row = argb + argb_stride; + for (y = 1; y < height; ++y) { + uint32_t prev_pix = curr_row[0]; + for (x = 1; x < width; ++x) { + const uint32_t pix = curr_row[x]; + const uint32_t pix_diff = VP8LSubPixels(pix, prev_pix); + if ((pix_diff == 0) || (pix == prev_row[x])) continue; + prev_pix = pix; + AddSingle(pix, + &histo[kHistoAlpha * 256], + &histo[kHistoRed * 256], + &histo[kHistoGreen * 256], + &histo[kHistoBlue * 256]); + AddSingle(pix_diff, + &histo[kHistoAlphaPred * 256], + &histo[kHistoRedPred * 256], + &histo[kHistoGreenPred * 256], + &histo[kHistoBluePred * 256]); + AddSingleSubGreen(pix, + &histo[kHistoRedSubGreen * 256], + &histo[kHistoBlueSubGreen * 256]); + AddSingleSubGreen(pix_diff, + &histo[kHistoRedPredSubGreen * 256], + &histo[kHistoBluePredSubGreen * 256]); + { + // Approximate the palette by the entropy of the multiplicative hash. + const uint32_t hash = HashPix(pix); + ++histo[kHistoPalette * 256 + hash]; + } + } + prev_row = curr_row; + curr_row += argb_stride; + } + { + double entropy_comp[kHistoTotal]; + double entropy[kNumEntropyIx]; + int k; + int last_mode_to_analyze = use_palette ? kPalette : kSpatialSubGreen; + int j; + // Let's add one zero to the predicted histograms. The zeros are removed + // too efficiently by the pix_diff == 0 comparison, at least one of the + // zeros is likely to exist. + ++histo[kHistoRedPredSubGreen * 256]; + ++histo[kHistoBluePredSubGreen * 256]; + ++histo[kHistoRedPred * 256]; + ++histo[kHistoGreenPred * 256]; + ++histo[kHistoBluePred * 256]; + ++histo[kHistoAlphaPred * 256]; + + for (j = 0; j < kHistoTotal; ++j) { + entropy_comp[j] = VP8LBitsEntropy(&histo[j * 256], 256, NULL); + } + entropy[kDirect] = entropy_comp[kHistoAlpha] + + entropy_comp[kHistoRed] + + entropy_comp[kHistoGreen] + + entropy_comp[kHistoBlue]; + entropy[kSpatial] = entropy_comp[kHistoAlphaPred] + + entropy_comp[kHistoRedPred] + + entropy_comp[kHistoGreenPred] + + entropy_comp[kHistoBluePred]; + entropy[kSubGreen] = entropy_comp[kHistoAlpha] + + entropy_comp[kHistoRedSubGreen] + + entropy_comp[kHistoGreen] + + entropy_comp[kHistoBlueSubGreen]; + entropy[kSpatialSubGreen] = entropy_comp[kHistoAlphaPred] + + entropy_comp[kHistoRedPredSubGreen] + + entropy_comp[kHistoGreenPred] + + entropy_comp[kHistoBluePredSubGreen]; + // Palette mode seems more efficient in a breakeven case. Bias with 1.0. + entropy[kPalette] = entropy_comp[kHistoPalette] - 1.0; + + *min_entropy_ix = kDirect; + for (k = kDirect + 1; k <= last_mode_to_analyze; ++k) { + if (entropy[*min_entropy_ix] > entropy[k]) { + *min_entropy_ix = (EntropyIx)k; + } + } + *red_and_blue_always_zero = 1; + // Let's check if the histogram of the chosen entropy mode has + // non-zero red and blue values. If all are zero, we can later skip + // the cross color optimization. + { + static const uint8_t kHistoPairs[5][2] = { + { kHistoRed, kHistoBlue }, + { kHistoRedPred, kHistoBluePred }, + { kHistoRedSubGreen, kHistoBlueSubGreen }, + { kHistoRedPredSubGreen, kHistoBluePredSubGreen }, + { kHistoRed, kHistoBlue } + }; + const uint32_t* const red_histo = + &histo[256 * kHistoPairs[*min_entropy_ix][0]]; + const uint32_t* const blue_histo = + &histo[256 * kHistoPairs[*min_entropy_ix][1]]; + for (i = 1; i < 256; ++i) { + if ((red_histo[i] | blue_histo[i]) != 0) { + *red_and_blue_always_zero = 0; + break; + } + } + } + } + WebPSafeFree(histo); + return 1; + } else { + return 0; + } +} + +static int GetHistoBits(int method, int use_palette, int width, int height) { + // Make tile size a function of encoding method (Range: 0 to 6). + int histo_bits = (use_palette ? 9 : 7) - method; + while (1) { + const int huff_image_size = VP8LSubSampleSize(width, histo_bits) * + VP8LSubSampleSize(height, histo_bits); + if (huff_image_size <= MAX_HUFF_IMAGE_SIZE) break; + ++histo_bits; + } + return (histo_bits < MIN_HUFFMAN_BITS) ? MIN_HUFFMAN_BITS : + (histo_bits > MAX_HUFFMAN_BITS) ? MAX_HUFFMAN_BITS : histo_bits; +} + +static int GetTransformBits(int method, int histo_bits) { + const int max_transform_bits = (method < 4) ? 6 : (method > 4) ? 4 : 5; + const int res = + (histo_bits > max_transform_bits) ? max_transform_bits : histo_bits; + assert(res <= MAX_TRANSFORM_BITS); + return res; +} + +static int AnalyzeAndInit(VP8LEncoder* const enc) { + const WebPPicture* const pic = enc->pic_; + const int width = pic->width; + const int height = pic->height; + const int pix_cnt = width * height; + const WebPConfig* const config = enc->config_; + const int method = config->method; + const int low_effort = (config->method == 0); + // we round the block size up, so we're guaranteed to have + // at max MAX_REFS_BLOCK_PER_IMAGE blocks used: + int refs_block_size = (pix_cnt - 1) / MAX_REFS_BLOCK_PER_IMAGE + 1; + assert(pic != NULL && pic->argb != NULL); + + enc->use_cross_color_ = 0; + enc->use_predict_ = 0; + enc->use_subtract_green_ = 0; + enc->use_palette_ = + AnalyzeAndCreatePalette(pic, low_effort, + enc->palette_, &enc->palette_size_); + + // TODO(jyrki): replace the decision to be based on an actual estimate + // of entropy, or even spatial variance of entropy. + enc->histo_bits_ = GetHistoBits(method, enc->use_palette_, + pic->width, pic->height); + enc->transform_bits_ = GetTransformBits(method, enc->histo_bits_); + + if (low_effort) { + // AnalyzeEntropy is somewhat slow. + enc->use_predict_ = !enc->use_palette_; + enc->use_subtract_green_ = !enc->use_palette_; + enc->use_cross_color_ = 0; + } else { + int red_and_blue_always_zero; + EntropyIx min_entropy_ix; + if (!AnalyzeEntropy(pic->argb, width, height, pic->argb_stride, + enc->use_palette_, &min_entropy_ix, + &red_and_blue_always_zero)) { + return 0; + } + enc->use_palette_ = (min_entropy_ix == kPalette); + enc->use_subtract_green_ = + (min_entropy_ix == kSubGreen) || (min_entropy_ix == kSpatialSubGreen); + enc->use_predict_ = + (min_entropy_ix == kSpatial) || (min_entropy_ix == kSpatialSubGreen); + enc->use_cross_color_ = red_and_blue_always_zero ? 0 : enc->use_predict_; + } + + if (!VP8LHashChainInit(&enc->hash_chain_, pix_cnt)) return 0; + + // palette-friendly input typically uses less literals + // -> reduce block size a bit + if (enc->use_palette_) refs_block_size /= 2; + VP8LBackwardRefsInit(&enc->refs_[0], refs_block_size); + VP8LBackwardRefsInit(&enc->refs_[1], refs_block_size); + + return 1; +} + +// Returns false in case of memory error. +static int GetHuffBitLengthsAndCodes( + const VP8LHistogramSet* const histogram_image, + HuffmanTreeCode* const huffman_codes) { + int i, k; + int ok = 0; + uint64_t total_length_size = 0; + uint8_t* mem_buf = NULL; + const int histogram_image_size = histogram_image->size; + int max_num_symbols = 0; + uint8_t* buf_rle = NULL; + HuffmanTree* huff_tree = NULL; + + // Iterate over all histograms and get the aggregate number of codes used. + for (i = 0; i < histogram_image_size; ++i) { + const VP8LHistogram* const histo = histogram_image->histograms[i]; + HuffmanTreeCode* const codes = &huffman_codes[5 * i]; + for (k = 0; k < 5; ++k) { + const int num_symbols = + (k == 0) ? VP8LHistogramNumCodes(histo->palette_code_bits_) : + (k == 4) ? NUM_DISTANCE_CODES : 256; + codes[k].num_symbols = num_symbols; + total_length_size += num_symbols; + } + } + + // Allocate and Set Huffman codes. + { + uint16_t* codes; + uint8_t* lengths; + mem_buf = (uint8_t*)WebPSafeCalloc(total_length_size, + sizeof(*lengths) + sizeof(*codes)); + if (mem_buf == NULL) goto End; + + codes = (uint16_t*)mem_buf; + lengths = (uint8_t*)&codes[total_length_size]; + for (i = 0; i < 5 * histogram_image_size; ++i) { + const int bit_length = huffman_codes[i].num_symbols; + huffman_codes[i].codes = codes; + huffman_codes[i].code_lengths = lengths; + codes += bit_length; + lengths += bit_length; + if (max_num_symbols < bit_length) { + max_num_symbols = bit_length; + } + } + } + + buf_rle = (uint8_t*)WebPSafeMalloc(1ULL, max_num_symbols); + huff_tree = (HuffmanTree*)WebPSafeMalloc(3ULL * max_num_symbols, + sizeof(*huff_tree)); + if (buf_rle == NULL || huff_tree == NULL) goto End; + + // Create Huffman trees. + for (i = 0; i < histogram_image_size; ++i) { + HuffmanTreeCode* const codes = &huffman_codes[5 * i]; + VP8LHistogram* const histo = histogram_image->histograms[i]; + VP8LCreateHuffmanTree(histo->literal_, 15, buf_rle, huff_tree, codes + 0); + VP8LCreateHuffmanTree(histo->red_, 15, buf_rle, huff_tree, codes + 1); + VP8LCreateHuffmanTree(histo->blue_, 15, buf_rle, huff_tree, codes + 2); + VP8LCreateHuffmanTree(histo->alpha_, 15, buf_rle, huff_tree, codes + 3); + VP8LCreateHuffmanTree(histo->distance_, 15, buf_rle, huff_tree, codes + 4); + } + ok = 1; + End: + WebPSafeFree(huff_tree); + WebPSafeFree(buf_rle); + if (!ok) { + WebPSafeFree(mem_buf); + memset(huffman_codes, 0, 5 * histogram_image_size * sizeof(*huffman_codes)); + } + return ok; +} + +static void StoreHuffmanTreeOfHuffmanTreeToBitMask( + VP8LBitWriter* const bw, const uint8_t* code_length_bitdepth) { + // RFC 1951 will calm you down if you are worried about this funny sequence. + // This sequence is tuned from that, but more weighted for lower symbol count, + // and more spiking histograms. + static const uint8_t kStorageOrder[CODE_LENGTH_CODES] = { + 17, 18, 0, 1, 2, 3, 4, 5, 16, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 + }; + int i; + // Throw away trailing zeros: + int codes_to_store = CODE_LENGTH_CODES; + for (; codes_to_store > 4; --codes_to_store) { + if (code_length_bitdepth[kStorageOrder[codes_to_store - 1]] != 0) { + break; + } + } + VP8LPutBits(bw, codes_to_store - 4, 4); + for (i = 0; i < codes_to_store; ++i) { + VP8LPutBits(bw, code_length_bitdepth[kStorageOrder[i]], 3); + } +} + +static void ClearHuffmanTreeIfOnlyOneSymbol( + HuffmanTreeCode* const huffman_code) { + int k; + int count = 0; + for (k = 0; k < huffman_code->num_symbols; ++k) { + if (huffman_code->code_lengths[k] != 0) { + ++count; + if (count > 1) return; + } + } + for (k = 0; k < huffman_code->num_symbols; ++k) { + huffman_code->code_lengths[k] = 0; + huffman_code->codes[k] = 0; + } +} + +static void StoreHuffmanTreeToBitMask( + VP8LBitWriter* const bw, + const HuffmanTreeToken* const tokens, const int num_tokens, + const HuffmanTreeCode* const huffman_code) { + int i; + for (i = 0; i < num_tokens; ++i) { + const int ix = tokens[i].code; + const int extra_bits = tokens[i].extra_bits; + VP8LPutBits(bw, huffman_code->codes[ix], huffman_code->code_lengths[ix]); + switch (ix) { + case 16: + VP8LPutBits(bw, extra_bits, 2); + break; + case 17: + VP8LPutBits(bw, extra_bits, 3); + break; + case 18: + VP8LPutBits(bw, extra_bits, 7); + break; + } + } +} + +// 'huff_tree' and 'tokens' are pre-alloacted buffers. +static void StoreFullHuffmanCode(VP8LBitWriter* const bw, + HuffmanTree* const huff_tree, + HuffmanTreeToken* const tokens, + const HuffmanTreeCode* const tree) { + uint8_t code_length_bitdepth[CODE_LENGTH_CODES] = { 0 }; + uint16_t code_length_bitdepth_symbols[CODE_LENGTH_CODES] = { 0 }; + const int max_tokens = tree->num_symbols; + int num_tokens; + HuffmanTreeCode huffman_code; + huffman_code.num_symbols = CODE_LENGTH_CODES; + huffman_code.code_lengths = code_length_bitdepth; + huffman_code.codes = code_length_bitdepth_symbols; + + VP8LPutBits(bw, 0, 1); + num_tokens = VP8LCreateCompressedHuffmanTree(tree, tokens, max_tokens); + { + uint32_t histogram[CODE_LENGTH_CODES] = { 0 }; + uint8_t buf_rle[CODE_LENGTH_CODES] = { 0 }; + int i; + for (i = 0; i < num_tokens; ++i) { + ++histogram[tokens[i].code]; + } + + VP8LCreateHuffmanTree(histogram, 7, buf_rle, huff_tree, &huffman_code); + } + + StoreHuffmanTreeOfHuffmanTreeToBitMask(bw, code_length_bitdepth); + ClearHuffmanTreeIfOnlyOneSymbol(&huffman_code); + { + int trailing_zero_bits = 0; + int trimmed_length = num_tokens; + int write_trimmed_length; + int length; + int i = num_tokens; + while (i-- > 0) { + const int ix = tokens[i].code; + if (ix == 0 || ix == 17 || ix == 18) { + --trimmed_length; // discount trailing zeros + trailing_zero_bits += code_length_bitdepth[ix]; + if (ix == 17) { + trailing_zero_bits += 3; + } else if (ix == 18) { + trailing_zero_bits += 7; + } + } else { + break; + } + } + write_trimmed_length = (trimmed_length > 1 && trailing_zero_bits > 12); + length = write_trimmed_length ? trimmed_length : num_tokens; + VP8LPutBits(bw, write_trimmed_length, 1); + if (write_trimmed_length) { + const int nbits = VP8LBitsLog2Ceiling(trimmed_length - 1); + const int nbitpairs = (nbits == 0) ? 1 : (nbits + 1) / 2; + VP8LPutBits(bw, nbitpairs - 1, 3); + assert(trimmed_length >= 2); + VP8LPutBits(bw, trimmed_length - 2, nbitpairs * 2); + } + StoreHuffmanTreeToBitMask(bw, tokens, length, &huffman_code); + } +} + +// 'huff_tree' and 'tokens' are pre-alloacted buffers. +static void StoreHuffmanCode(VP8LBitWriter* const bw, + HuffmanTree* const huff_tree, + HuffmanTreeToken* const tokens, + const HuffmanTreeCode* const huffman_code) { + int i; + int count = 0; + int symbols[2] = { 0, 0 }; + const int kMaxBits = 8; + const int kMaxSymbol = 1 << kMaxBits; + + // Check whether it's a small tree. + for (i = 0; i < huffman_code->num_symbols && count < 3; ++i) { + if (huffman_code->code_lengths[i] != 0) { + if (count < 2) symbols[count] = i; + ++count; + } + } + + if (count == 0) { // emit minimal tree for empty cases + // bits: small tree marker: 1, count-1: 0, large 8-bit code: 0, code: 0 + VP8LPutBits(bw, 0x01, 4); + } else if (count <= 2 && symbols[0] < kMaxSymbol && symbols[1] < kMaxSymbol) { + VP8LPutBits(bw, 1, 1); // Small tree marker to encode 1 or 2 symbols. + VP8LPutBits(bw, count - 1, 1); + if (symbols[0] <= 1) { + VP8LPutBits(bw, 0, 1); // Code bit for small (1 bit) symbol value. + VP8LPutBits(bw, symbols[0], 1); + } else { + VP8LPutBits(bw, 1, 1); + VP8LPutBits(bw, symbols[0], 8); + } + if (count == 2) { + VP8LPutBits(bw, symbols[1], 8); + } + } else { + StoreFullHuffmanCode(bw, huff_tree, tokens, huffman_code); + } +} + +static WEBP_INLINE void WriteHuffmanCode(VP8LBitWriter* const bw, + const HuffmanTreeCode* const code, + int code_index) { + const int depth = code->code_lengths[code_index]; + const int symbol = code->codes[code_index]; + VP8LPutBits(bw, symbol, depth); +} + +static WEBP_INLINE void WriteHuffmanCodeWithExtraBits( + VP8LBitWriter* const bw, + const HuffmanTreeCode* const code, + int code_index, + int bits, + int n_bits) { + const int depth = code->code_lengths[code_index]; + const int symbol = code->codes[code_index]; + VP8LPutBits(bw, (bits << depth) | symbol, depth + n_bits); +} + +static WebPEncodingError StoreImageToBitMask( + VP8LBitWriter* const bw, int width, int histo_bits, + VP8LBackwardRefs* const refs, + const uint16_t* histogram_symbols, + const HuffmanTreeCode* const huffman_codes) { + const int histo_xsize = histo_bits ? VP8LSubSampleSize(width, histo_bits) : 1; + const int tile_mask = (histo_bits == 0) ? 0 : -(1 << histo_bits); + // x and y trace the position in the image. + int x = 0; + int y = 0; + int tile_x = x & tile_mask; + int tile_y = y & tile_mask; + int histogram_ix = histogram_symbols[0]; + const HuffmanTreeCode* codes = huffman_codes + 5 * histogram_ix; + VP8LRefsCursor c = VP8LRefsCursorInit(refs); + while (VP8LRefsCursorOk(&c)) { + const PixOrCopy* const v = c.cur_pos; + if ((tile_x != (x & tile_mask)) || (tile_y != (y & tile_mask))) { + tile_x = x & tile_mask; + tile_y = y & tile_mask; + histogram_ix = histogram_symbols[(y >> histo_bits) * histo_xsize + + (x >> histo_bits)]; + codes = huffman_codes + 5 * histogram_ix; + } + if (PixOrCopyIsLiteral(v)) { + static const int order[] = { 1, 2, 0, 3 }; + int k; + for (k = 0; k < 4; ++k) { + const int code = PixOrCopyLiteral(v, order[k]); + WriteHuffmanCode(bw, codes + k, code); + } + } else if (PixOrCopyIsCacheIdx(v)) { + const int code = PixOrCopyCacheIdx(v); + const int literal_ix = 256 + NUM_LENGTH_CODES + code; + WriteHuffmanCode(bw, codes, literal_ix); + } else { + int bits, n_bits; + int code; + + const int distance = PixOrCopyDistance(v); + VP8LPrefixEncode(v->len, &code, &n_bits, &bits); + WriteHuffmanCodeWithExtraBits(bw, codes, 256 + code, bits, n_bits); + + // Don't write the distance with the extra bits code since + // the distance can be up to 18 bits of extra bits, and the prefix + // 15 bits, totaling to 33, and our PutBits only supports up to 32 bits. + // TODO(jyrki): optimize this further. + VP8LPrefixEncode(distance, &code, &n_bits, &bits); + WriteHuffmanCode(bw, codes + 4, code); + VP8LPutBits(bw, bits, n_bits); + } + x += PixOrCopyLength(v); + while (x >= width) { + x -= width; + ++y; + } + VP8LRefsCursorNext(&c); + } + return bw->error_ ? VP8_ENC_ERROR_OUT_OF_MEMORY : VP8_ENC_OK; +} + +// Special case of EncodeImageInternal() for cache-bits=0, histo_bits=31 +static WebPEncodingError EncodeImageNoHuffman(VP8LBitWriter* const bw, + const uint32_t* const argb, + VP8LHashChain* const hash_chain, + VP8LBackwardRefs refs_array[2], + int width, int height, + int quality, int low_effort) { + int i; + int max_tokens = 0; + WebPEncodingError err = VP8_ENC_OK; + VP8LBackwardRefs* refs; + HuffmanTreeToken* tokens = NULL; + HuffmanTreeCode huffman_codes[5] = { { 0, NULL, NULL } }; + const uint16_t histogram_symbols[1] = { 0 }; // only one tree, one symbol + int cache_bits = 0; + VP8LHistogramSet* histogram_image = NULL; + HuffmanTree* const huff_tree = (HuffmanTree*)WebPSafeMalloc( + 3ULL * CODE_LENGTH_CODES, sizeof(*huff_tree)); + if (huff_tree == NULL) { + err = VP8_ENC_ERROR_OUT_OF_MEMORY; + goto Error; + } + + // Calculate backward references from ARGB image. + if (!VP8LHashChainFill(hash_chain, quality, argb, width, height, + low_effort)) { + err = VP8_ENC_ERROR_OUT_OF_MEMORY; + goto Error; + } + refs = VP8LGetBackwardReferences(width, height, argb, quality, 0, &cache_bits, + hash_chain, refs_array); + if (refs == NULL) { + err = VP8_ENC_ERROR_OUT_OF_MEMORY; + goto Error; + } + histogram_image = VP8LAllocateHistogramSet(1, cache_bits); + if (histogram_image == NULL) { + err = VP8_ENC_ERROR_OUT_OF_MEMORY; + goto Error; + } + + // Build histogram image and symbols from backward references. + VP8LHistogramStoreRefs(refs, histogram_image->histograms[0]); + + // Create Huffman bit lengths and codes for each histogram image. + assert(histogram_image->size == 1); + if (!GetHuffBitLengthsAndCodes(histogram_image, huffman_codes)) { + err = VP8_ENC_ERROR_OUT_OF_MEMORY; + goto Error; + } + + // No color cache, no Huffman image. + VP8LPutBits(bw, 0, 1); + + // Find maximum number of symbols for the huffman tree-set. + for (i = 0; i < 5; ++i) { + HuffmanTreeCode* const codes = &huffman_codes[i]; + if (max_tokens < codes->num_symbols) { + max_tokens = codes->num_symbols; + } + } + + tokens = (HuffmanTreeToken*)WebPSafeMalloc(max_tokens, sizeof(*tokens)); + if (tokens == NULL) { + err = VP8_ENC_ERROR_OUT_OF_MEMORY; + goto Error; + } + + // Store Huffman codes. + for (i = 0; i < 5; ++i) { + HuffmanTreeCode* const codes = &huffman_codes[i]; + StoreHuffmanCode(bw, huff_tree, tokens, codes); + ClearHuffmanTreeIfOnlyOneSymbol(codes); + } + + // Store actual literals. + err = StoreImageToBitMask(bw, width, 0, refs, histogram_symbols, + huffman_codes); + + Error: + WebPSafeFree(tokens); + WebPSafeFree(huff_tree); + VP8LFreeHistogramSet(histogram_image); + WebPSafeFree(huffman_codes[0].codes); + return err; +} + +static WebPEncodingError EncodeImageInternal(VP8LBitWriter* const bw, + const uint32_t* const argb, + VP8LHashChain* const hash_chain, + VP8LBackwardRefs refs_array[2], + int width, int height, int quality, + int low_effort, + int use_cache, int* cache_bits, + int histogram_bits, + size_t init_byte_position, + int* const hdr_size, + int* const data_size) { + WebPEncodingError err = VP8_ENC_OK; + const uint32_t histogram_image_xysize = + VP8LSubSampleSize(width, histogram_bits) * + VP8LSubSampleSize(height, histogram_bits); + VP8LHistogramSet* histogram_image = NULL; + VP8LHistogramSet* tmp_histos = NULL; + int histogram_image_size = 0; + size_t bit_array_size = 0; + HuffmanTree* huff_tree = NULL; + HuffmanTreeToken* tokens = NULL; + HuffmanTreeCode* huffman_codes = NULL; + VP8LBackwardRefs refs; + VP8LBackwardRefs* best_refs; + uint16_t* const histogram_symbols = + (uint16_t*)WebPSafeMalloc(histogram_image_xysize, + sizeof(*histogram_symbols)); + assert(histogram_bits >= MIN_HUFFMAN_BITS); + assert(histogram_bits <= MAX_HUFFMAN_BITS); + assert(hdr_size != NULL); + assert(data_size != NULL); + + VP8LBackwardRefsInit(&refs, refs_array[0].block_size_); + if (histogram_symbols == NULL) { + err = VP8_ENC_ERROR_OUT_OF_MEMORY; + goto Error; + } + + if (use_cache) { + // If the value is different from zero, it has been set during the + // palette analysis. + if (*cache_bits == 0) *cache_bits = MAX_COLOR_CACHE_BITS; + } else { + *cache_bits = 0; + } + // 'best_refs' is the reference to the best backward refs and points to one + // of refs_array[0] or refs_array[1]. + // Calculate backward references from ARGB image. + if (!VP8LHashChainFill(hash_chain, quality, argb, width, height, + low_effort)) { + err = VP8_ENC_ERROR_OUT_OF_MEMORY; + goto Error; + } + best_refs = VP8LGetBackwardReferences(width, height, argb, quality, + low_effort, cache_bits, hash_chain, + refs_array); + if (best_refs == NULL || !VP8LBackwardRefsCopy(best_refs, &refs)) { + err = VP8_ENC_ERROR_OUT_OF_MEMORY; + goto Error; + } + histogram_image = + VP8LAllocateHistogramSet(histogram_image_xysize, *cache_bits); + tmp_histos = VP8LAllocateHistogramSet(2, *cache_bits); + if (histogram_image == NULL || tmp_histos == NULL) { + err = VP8_ENC_ERROR_OUT_OF_MEMORY; + goto Error; + } + + // Build histogram image and symbols from backward references. + if (!VP8LGetHistoImageSymbols(width, height, &refs, quality, low_effort, + histogram_bits, *cache_bits, histogram_image, + tmp_histos, histogram_symbols)) { + err = VP8_ENC_ERROR_OUT_OF_MEMORY; + goto Error; + } + // Create Huffman bit lengths and codes for each histogram image. + histogram_image_size = histogram_image->size; + bit_array_size = 5 * histogram_image_size; + huffman_codes = (HuffmanTreeCode*)WebPSafeCalloc(bit_array_size, + sizeof(*huffman_codes)); + // Note: some histogram_image entries may point to tmp_histos[], so the latter + // need to outlive the following call to GetHuffBitLengthsAndCodes(). + if (huffman_codes == NULL || + !GetHuffBitLengthsAndCodes(histogram_image, huffman_codes)) { + err = VP8_ENC_ERROR_OUT_OF_MEMORY; + goto Error; + } + // Free combined histograms. + VP8LFreeHistogramSet(histogram_image); + histogram_image = NULL; + + // Free scratch histograms. + VP8LFreeHistogramSet(tmp_histos); + tmp_histos = NULL; + + // Color Cache parameters. + if (*cache_bits > 0) { + VP8LPutBits(bw, 1, 1); + VP8LPutBits(bw, *cache_bits, 4); + } else { + VP8LPutBits(bw, 0, 1); + } + + // Huffman image + meta huffman. + { + const int write_histogram_image = (histogram_image_size > 1); + VP8LPutBits(bw, write_histogram_image, 1); + if (write_histogram_image) { + uint32_t* const histogram_argb = + (uint32_t*)WebPSafeMalloc(histogram_image_xysize, + sizeof(*histogram_argb)); + int max_index = 0; + uint32_t i; + if (histogram_argb == NULL) { + err = VP8_ENC_ERROR_OUT_OF_MEMORY; + goto Error; + } + for (i = 0; i < histogram_image_xysize; ++i) { + const int symbol_index = histogram_symbols[i] & 0xffff; + histogram_argb[i] = (symbol_index << 8); + if (symbol_index >= max_index) { + max_index = symbol_index + 1; + } + } + histogram_image_size = max_index; + + VP8LPutBits(bw, histogram_bits - 2, 3); + err = EncodeImageNoHuffman(bw, histogram_argb, hash_chain, refs_array, + VP8LSubSampleSize(width, histogram_bits), + VP8LSubSampleSize(height, histogram_bits), + quality, low_effort); + WebPSafeFree(histogram_argb); + if (err != VP8_ENC_OK) goto Error; + } + } + + // Store Huffman codes. + { + int i; + int max_tokens = 0; + huff_tree = (HuffmanTree*)WebPSafeMalloc(3ULL * CODE_LENGTH_CODES, + sizeof(*huff_tree)); + if (huff_tree == NULL) { + err = VP8_ENC_ERROR_OUT_OF_MEMORY; + goto Error; + } + // Find maximum number of symbols for the huffman tree-set. + for (i = 0; i < 5 * histogram_image_size; ++i) { + HuffmanTreeCode* const codes = &huffman_codes[i]; + if (max_tokens < codes->num_symbols) { + max_tokens = codes->num_symbols; + } + } + tokens = (HuffmanTreeToken*)WebPSafeMalloc(max_tokens, + sizeof(*tokens)); + if (tokens == NULL) { + err = VP8_ENC_ERROR_OUT_OF_MEMORY; + goto Error; + } + for (i = 0; i < 5 * histogram_image_size; ++i) { + HuffmanTreeCode* const codes = &huffman_codes[i]; + StoreHuffmanCode(bw, huff_tree, tokens, codes); + ClearHuffmanTreeIfOnlyOneSymbol(codes); + } + } + + *hdr_size = (int)(VP8LBitWriterNumBytes(bw) - init_byte_position); + // Store actual literals. + err = StoreImageToBitMask(bw, width, histogram_bits, &refs, + histogram_symbols, huffman_codes); + *data_size = + (int)(VP8LBitWriterNumBytes(bw) - init_byte_position - *hdr_size); + + Error: + WebPSafeFree(tokens); + WebPSafeFree(huff_tree); + VP8LFreeHistogramSet(histogram_image); + VP8LFreeHistogramSet(tmp_histos); + VP8LBackwardRefsClear(&refs); + if (huffman_codes != NULL) { + WebPSafeFree(huffman_codes->codes); + WebPSafeFree(huffman_codes); + } + WebPSafeFree(histogram_symbols); + return err; +} + +// ----------------------------------------------------------------------------- +// Transforms + +static void ApplySubtractGreen(VP8LEncoder* const enc, int width, int height, + VP8LBitWriter* const bw) { + VP8LPutBits(bw, TRANSFORM_PRESENT, 1); + VP8LPutBits(bw, SUBTRACT_GREEN, 2); + VP8LSubtractGreenFromBlueAndRed(enc->argb_, width * height); +} + +static WebPEncodingError ApplyPredictFilter(const VP8LEncoder* const enc, + int width, int height, + int quality, int low_effort, + int used_subtract_green, + VP8LBitWriter* const bw) { + const int pred_bits = enc->transform_bits_; + const int transform_width = VP8LSubSampleSize(width, pred_bits); + const int transform_height = VP8LSubSampleSize(height, pred_bits); + // we disable near-lossless quantization if palette is used. + const int near_lossless_strength = enc->use_palette_ ? 100 + : enc->config_->near_lossless; + + VP8LResidualImage(width, height, pred_bits, low_effort, enc->argb_, + enc->argb_scratch_, enc->transform_data_, + near_lossless_strength, enc->config_->exact, + used_subtract_green); + VP8LPutBits(bw, TRANSFORM_PRESENT, 1); + VP8LPutBits(bw, PREDICTOR_TRANSFORM, 2); + assert(pred_bits >= 2); + VP8LPutBits(bw, pred_bits - 2, 3); + return EncodeImageNoHuffman(bw, enc->transform_data_, + (VP8LHashChain*)&enc->hash_chain_, + (VP8LBackwardRefs*)enc->refs_, // cast const away + transform_width, transform_height, + quality, low_effort); +} + +static WebPEncodingError ApplyCrossColorFilter(const VP8LEncoder* const enc, + int width, int height, + int quality, int low_effort, + VP8LBitWriter* const bw) { + const int ccolor_transform_bits = enc->transform_bits_; + const int transform_width = VP8LSubSampleSize(width, ccolor_transform_bits); + const int transform_height = VP8LSubSampleSize(height, ccolor_transform_bits); + + VP8LColorSpaceTransform(width, height, ccolor_transform_bits, quality, + enc->argb_, enc->transform_data_); + VP8LPutBits(bw, TRANSFORM_PRESENT, 1); + VP8LPutBits(bw, CROSS_COLOR_TRANSFORM, 2); + assert(ccolor_transform_bits >= 2); + VP8LPutBits(bw, ccolor_transform_bits - 2, 3); + return EncodeImageNoHuffman(bw, enc->transform_data_, + (VP8LHashChain*)&enc->hash_chain_, + (VP8LBackwardRefs*)enc->refs_, // cast const away + transform_width, transform_height, + quality, low_effort); +} + +// ----------------------------------------------------------------------------- + +static WebPEncodingError WriteRiffHeader(const WebPPicture* const pic, + size_t riff_size, size_t vp8l_size) { + uint8_t riff[RIFF_HEADER_SIZE + CHUNK_HEADER_SIZE + VP8L_SIGNATURE_SIZE] = { + 'R', 'I', 'F', 'F', 0, 0, 0, 0, 'W', 'E', 'B', 'P', + 'V', 'P', '8', 'L', 0, 0, 0, 0, VP8L_MAGIC_BYTE, + }; + PutLE32(riff + TAG_SIZE, (uint32_t)riff_size); + PutLE32(riff + RIFF_HEADER_SIZE + TAG_SIZE, (uint32_t)vp8l_size); + if (!pic->writer(riff, sizeof(riff), pic)) { + return VP8_ENC_ERROR_BAD_WRITE; + } + return VP8_ENC_OK; +} + +static int WriteImageSize(const WebPPicture* const pic, + VP8LBitWriter* const bw) { + const int width = pic->width - 1; + const int height = pic->height - 1; + assert(width < WEBP_MAX_DIMENSION && height < WEBP_MAX_DIMENSION); + + VP8LPutBits(bw, width, VP8L_IMAGE_SIZE_BITS); + VP8LPutBits(bw, height, VP8L_IMAGE_SIZE_BITS); + return !bw->error_; +} + +static int WriteRealAlphaAndVersion(VP8LBitWriter* const bw, int has_alpha) { + VP8LPutBits(bw, has_alpha, 1); + VP8LPutBits(bw, VP8L_VERSION, VP8L_VERSION_BITS); + return !bw->error_; +} + +static WebPEncodingError WriteImage(const WebPPicture* const pic, + VP8LBitWriter* const bw, + size_t* const coded_size) { + WebPEncodingError err = VP8_ENC_OK; + const uint8_t* const webpll_data = VP8LBitWriterFinish(bw); + const size_t webpll_size = VP8LBitWriterNumBytes(bw); + const size_t vp8l_size = VP8L_SIGNATURE_SIZE + webpll_size; + const size_t pad = vp8l_size & 1; + const size_t riff_size = TAG_SIZE + CHUNK_HEADER_SIZE + vp8l_size + pad; + + err = WriteRiffHeader(pic, riff_size, vp8l_size); + if (err != VP8_ENC_OK) goto Error; + + if (!pic->writer(webpll_data, webpll_size, pic)) { + err = VP8_ENC_ERROR_BAD_WRITE; + goto Error; + } + + if (pad) { + const uint8_t pad_byte[1] = { 0 }; + if (!pic->writer(pad_byte, 1, pic)) { + err = VP8_ENC_ERROR_BAD_WRITE; + goto Error; + } + } + *coded_size = CHUNK_HEADER_SIZE + riff_size; + return VP8_ENC_OK; + + Error: + return err; +} + +// ----------------------------------------------------------------------------- + +static void ClearTransformBuffer(VP8LEncoder* const enc) { + WebPSafeFree(enc->transform_mem_); + enc->transform_mem_ = NULL; + enc->transform_mem_size_ = 0; +} + +// Allocates the memory for argb (W x H) buffer, 2 rows of context for +// prediction and transform data. +// Flags influencing the memory allocated: +// enc->transform_bits_ +// enc->use_predict_, enc->use_cross_color_ +static WebPEncodingError AllocateTransformBuffer(VP8LEncoder* const enc, + int width, int height) { + WebPEncodingError err = VP8_ENC_OK; + const uint64_t image_size = width * height; + // VP8LResidualImage needs room for 2 scanlines of uint32 pixels with an extra + // pixel in each, plus 2 regular scanlines of bytes. + // TODO(skal): Clean up by using arithmetic in bytes instead of words. + const uint64_t argb_scratch_size = + enc->use_predict_ + ? (width + 1) * 2 + + (width * 2 + sizeof(uint32_t) - 1) / sizeof(uint32_t) + : 0; + const uint64_t transform_data_size = + (enc->use_predict_ || enc->use_cross_color_) + ? VP8LSubSampleSize(width, enc->transform_bits_) * + VP8LSubSampleSize(height, enc->transform_bits_) + : 0; + const uint64_t max_alignment_in_words = + (WEBP_ALIGN_CST + sizeof(uint32_t) - 1) / sizeof(uint32_t); + const uint64_t mem_size = + image_size + max_alignment_in_words + + argb_scratch_size + max_alignment_in_words + + transform_data_size; + uint32_t* mem = enc->transform_mem_; + if (mem == NULL || mem_size > enc->transform_mem_size_) { + ClearTransformBuffer(enc); + mem = (uint32_t*)WebPSafeMalloc(mem_size, sizeof(*mem)); + if (mem == NULL) { + err = VP8_ENC_ERROR_OUT_OF_MEMORY; + goto Error; + } + enc->transform_mem_ = mem; + enc->transform_mem_size_ = (size_t)mem_size; + } + enc->argb_ = mem; + mem = (uint32_t*)WEBP_ALIGN(mem + image_size); + enc->argb_scratch_ = mem; + mem = (uint32_t*)WEBP_ALIGN(mem + argb_scratch_size); + enc->transform_data_ = mem; + + enc->current_width_ = width; + Error: + return err; +} + +static WebPEncodingError MakeInputImageCopy(VP8LEncoder* const enc) { + WebPEncodingError err = VP8_ENC_OK; + const WebPPicture* const picture = enc->pic_; + const int width = picture->width; + const int height = picture->height; + int y; + err = AllocateTransformBuffer(enc, width, height); + if (err != VP8_ENC_OK) return err; + for (y = 0; y < height; ++y) { + memcpy(enc->argb_ + y * width, + picture->argb + y * picture->argb_stride, + width * sizeof(*enc->argb_)); + } + assert(enc->current_width_ == width); + return VP8_ENC_OK; +} + +// ----------------------------------------------------------------------------- + +static WEBP_INLINE int SearchColorNoIdx(const uint32_t sorted[], uint32_t color, + int hi) { + int low = 0; + if (sorted[low] == color) return low; // loop invariant: sorted[low] != color + while (1) { + const int mid = (low + hi) >> 1; + if (sorted[mid] == color) { + return mid; + } else if (sorted[mid] < color) { + low = mid; + } else { + hi = mid; + } + } +} + +#define APPLY_PALETTE_GREEDY_MAX 4 + +static WEBP_INLINE uint32_t SearchColorGreedy(const uint32_t palette[], + int palette_size, + uint32_t color) { + (void)palette_size; + assert(palette_size < APPLY_PALETTE_GREEDY_MAX); + assert(3 == APPLY_PALETTE_GREEDY_MAX - 1); + if (color == palette[0]) return 0; + if (color == palette[1]) return 1; + if (color == palette[2]) return 2; + return 3; +} + +static WEBP_INLINE uint32_t ApplyPaletteHash0(uint32_t color) { + // Focus on the green color. + return (color >> 8) & 0xff; +} + +#define PALETTE_INV_SIZE_BITS 11 +#define PALETTE_INV_SIZE (1 << PALETTE_INV_SIZE_BITS) + +static WEBP_INLINE uint32_t ApplyPaletteHash1(uint32_t color) { + // Forget about alpha. + return ((color & 0x00ffffffu) * 4222244071u) >> (32 - PALETTE_INV_SIZE_BITS); +} + +static WEBP_INLINE uint32_t ApplyPaletteHash2(uint32_t color) { + // Forget about alpha. + return (color & 0x00ffffffu) * ((1u << 31) - 1) >> + (32 - PALETTE_INV_SIZE_BITS); +} + +// Sort palette in increasing order and prepare an inverse mapping array. +static void PrepareMapToPalette(const uint32_t palette[], int num_colors, + uint32_t sorted[], uint32_t idx_map[]) { + int i; + memcpy(sorted, palette, num_colors * sizeof(*sorted)); + qsort(sorted, num_colors, sizeof(*sorted), PaletteCompareColorsForQsort); + for (i = 0; i < num_colors; ++i) { + idx_map[SearchColorNoIdx(sorted, palette[i], num_colors)] = i; + } +} + +// Use 1 pixel cache for ARGB pixels. +#define APPLY_PALETTE_FOR(COLOR_INDEX) do { \ + uint32_t prev_pix = palette[0]; \ + uint32_t prev_idx = 0; \ + for (y = 0; y < height; ++y) { \ + for (x = 0; x < width; ++x) { \ + const uint32_t pix = src[x]; \ + if (pix != prev_pix) { \ + prev_idx = COLOR_INDEX; \ + prev_pix = pix; \ + } \ + tmp_row[x] = prev_idx; \ + } \ + VP8LBundleColorMap(tmp_row, width, xbits, dst); \ + src += src_stride; \ + dst += dst_stride; \ + } \ +} while (0) + +// Remap argb values in src[] to packed palettes entries in dst[] +// using 'row' as a temporary buffer of size 'width'. +// We assume that all src[] values have a corresponding entry in the palette. +// Note: src[] can be the same as dst[] +static WebPEncodingError ApplyPalette(const uint32_t* src, uint32_t src_stride, + uint32_t* dst, uint32_t dst_stride, + const uint32_t* palette, int palette_size, + int width, int height, int xbits) { + // TODO(skal): this tmp buffer is not needed if VP8LBundleColorMap() can be + // made to work in-place. + uint8_t* const tmp_row = (uint8_t*)WebPSafeMalloc(width, sizeof(*tmp_row)); + int x, y; + + if (tmp_row == NULL) return VP8_ENC_ERROR_OUT_OF_MEMORY; + + if (palette_size < APPLY_PALETTE_GREEDY_MAX) { + APPLY_PALETTE_FOR(SearchColorGreedy(palette, palette_size, pix)); + } else { + int i, j; + uint16_t buffer[PALETTE_INV_SIZE]; + uint32_t (*const hash_functions[])(uint32_t) = { + ApplyPaletteHash0, ApplyPaletteHash1, ApplyPaletteHash2 + }; + + // Try to find a perfect hash function able to go from a color to an index + // within 1 << PALETTE_INV_SIZE_BITS in order to build a hash map to go + // from color to index in palette. + for (i = 0; i < 3; ++i) { + int use_LUT = 1; + // Set each element in buffer to max uint16_t. + memset(buffer, 0xff, sizeof(buffer)); + for (j = 0; j < palette_size; ++j) { + const uint32_t ind = hash_functions[i](palette[j]); + if (buffer[ind] != 0xffffu) { + use_LUT = 0; + break; + } else { + buffer[ind] = j; + } + } + if (use_LUT) break; + } + + if (i == 0) { + APPLY_PALETTE_FOR(buffer[ApplyPaletteHash0(pix)]); + } else if (i == 1) { + APPLY_PALETTE_FOR(buffer[ApplyPaletteHash1(pix)]); + } else if (i == 2) { + APPLY_PALETTE_FOR(buffer[ApplyPaletteHash2(pix)]); + } else { + uint32_t idx_map[MAX_PALETTE_SIZE]; + uint32_t palette_sorted[MAX_PALETTE_SIZE]; + PrepareMapToPalette(palette, palette_size, palette_sorted, idx_map); + APPLY_PALETTE_FOR( + idx_map[SearchColorNoIdx(palette_sorted, pix, palette_size)]); + } + } + WebPSafeFree(tmp_row); + return VP8_ENC_OK; +} +#undef APPLY_PALETTE_FOR +#undef PALETTE_INV_SIZE_BITS +#undef PALETTE_INV_SIZE +#undef APPLY_PALETTE_GREEDY_MAX + +// Note: Expects "enc->palette_" to be set properly. +static WebPEncodingError MapImageFromPalette(VP8LEncoder* const enc, + int in_place) { + WebPEncodingError err = VP8_ENC_OK; + const WebPPicture* const pic = enc->pic_; + const int width = pic->width; + const int height = pic->height; + const uint32_t* const palette = enc->palette_; + const uint32_t* src = in_place ? enc->argb_ : pic->argb; + const int src_stride = in_place ? enc->current_width_ : pic->argb_stride; + const int palette_size = enc->palette_size_; + int xbits; + + // Replace each input pixel by corresponding palette index. + // This is done line by line. + if (palette_size <= 4) { + xbits = (palette_size <= 2) ? 3 : 2; + } else { + xbits = (palette_size <= 16) ? 1 : 0; + } + + err = AllocateTransformBuffer(enc, VP8LSubSampleSize(width, xbits), height); + if (err != VP8_ENC_OK) return err; + + err = ApplyPalette(src, src_stride, + enc->argb_, enc->current_width_, + palette, palette_size, width, height, xbits); + return err; +} + +// Save palette_[] to bitstream. +static WebPEncodingError EncodePalette(VP8LBitWriter* const bw, int low_effort, + VP8LEncoder* const enc) { + int i; + uint32_t tmp_palette[MAX_PALETTE_SIZE]; + const int palette_size = enc->palette_size_; + const uint32_t* const palette = enc->palette_; + VP8LPutBits(bw, TRANSFORM_PRESENT, 1); + VP8LPutBits(bw, COLOR_INDEXING_TRANSFORM, 2); + assert(palette_size >= 1 && palette_size <= MAX_PALETTE_SIZE); + VP8LPutBits(bw, palette_size - 1, 8); + for (i = palette_size - 1; i >= 1; --i) { + tmp_palette[i] = VP8LSubPixels(palette[i], palette[i - 1]); + } + tmp_palette[0] = palette[0]; + return EncodeImageNoHuffman(bw, tmp_palette, &enc->hash_chain_, enc->refs_, + palette_size, 1, 20 /* quality */, low_effort); +} + +#ifdef WEBP_EXPERIMENTAL_FEATURES + +static WebPEncodingError EncodeDeltaPalettePredictorImage( + VP8LBitWriter* const bw, VP8LEncoder* const enc, int quality, + int low_effort) { + const WebPPicture* const pic = enc->pic_; + const int width = pic->width; + const int height = pic->height; + + const int pred_bits = 5; + const int transform_width = VP8LSubSampleSize(width, pred_bits); + const int transform_height = VP8LSubSampleSize(height, pred_bits); + const int pred = 7; // default is Predictor7 (Top/Left Average) + const int tiles_per_row = VP8LSubSampleSize(width, pred_bits); + const int tiles_per_col = VP8LSubSampleSize(height, pred_bits); + uint32_t* predictors; + int tile_x, tile_y; + WebPEncodingError err = VP8_ENC_OK; + + predictors = (uint32_t*)WebPSafeMalloc(tiles_per_col * tiles_per_row, + sizeof(*predictors)); + if (predictors == NULL) return VP8_ENC_ERROR_OUT_OF_MEMORY; + + for (tile_y = 0; tile_y < tiles_per_col; ++tile_y) { + for (tile_x = 0; tile_x < tiles_per_row; ++tile_x) { + predictors[tile_y * tiles_per_row + tile_x] = 0xff000000u | (pred << 8); + } + } + + VP8LPutBits(bw, TRANSFORM_PRESENT, 1); + VP8LPutBits(bw, PREDICTOR_TRANSFORM, 2); + VP8LPutBits(bw, pred_bits - 2, 3); + err = EncodeImageNoHuffman(bw, predictors, &enc->hash_chain_, + (VP8LBackwardRefs*)enc->refs_, // cast const away + transform_width, transform_height, + quality, low_effort); + WebPSafeFree(predictors); + return err; +} + +#endif // WEBP_EXPERIMENTAL_FEATURES + +// ----------------------------------------------------------------------------- +// VP8LEncoder + +static VP8LEncoder* VP8LEncoderNew(const WebPConfig* const config, + const WebPPicture* const picture) { + VP8LEncoder* const enc = (VP8LEncoder*)WebPSafeCalloc(1ULL, sizeof(*enc)); + if (enc == NULL) { + WebPEncodingSetError(picture, VP8_ENC_ERROR_OUT_OF_MEMORY); + return NULL; + } + enc->config_ = config; + enc->pic_ = picture; + + VP8LEncDspInit(); + + return enc; +} + +static void VP8LEncoderDelete(VP8LEncoder* enc) { + if (enc != NULL) { + VP8LHashChainClear(&enc->hash_chain_); + VP8LBackwardRefsClear(&enc->refs_[0]); + VP8LBackwardRefsClear(&enc->refs_[1]); + ClearTransformBuffer(enc); + WebPSafeFree(enc); + } +} + +// ----------------------------------------------------------------------------- +// Main call + +WebPEncodingError VP8LEncodeStream(const WebPConfig* const config, + const WebPPicture* const picture, + VP8LBitWriter* const bw, int use_cache) { + WebPEncodingError err = VP8_ENC_OK; + const int quality = (int)config->quality; + const int low_effort = (config->method == 0); + const int width = picture->width; + const int height = picture->height; + VP8LEncoder* const enc = VP8LEncoderNew(config, picture); + const size_t byte_position = VP8LBitWriterNumBytes(bw); + int use_near_lossless = 0; + int hdr_size = 0; + int data_size = 0; + int use_delta_palette = 0; + + if (enc == NULL) { + err = VP8_ENC_ERROR_OUT_OF_MEMORY; + goto Error; + } + + // --------------------------------------------------------------------------- + // Analyze image (entropy, num_palettes etc) + + if (!AnalyzeAndInit(enc)) { + err = VP8_ENC_ERROR_OUT_OF_MEMORY; + goto Error; + } + + // Apply near-lossless preprocessing. + use_near_lossless = + (config->near_lossless < 100) && !enc->use_palette_ && !enc->use_predict_; + if (use_near_lossless) { + if (!VP8ApplyNearLossless(width, height, picture->argb, + config->near_lossless)) { + err = VP8_ENC_ERROR_OUT_OF_MEMORY; + goto Error; + } + } + +#ifdef WEBP_EXPERIMENTAL_FEATURES + if (config->use_delta_palette) { + enc->use_predict_ = 1; + enc->use_cross_color_ = 0; + enc->use_subtract_green_ = 0; + enc->use_palette_ = 1; + err = MakeInputImageCopy(enc); + if (err != VP8_ENC_OK) goto Error; + err = WebPSearchOptimalDeltaPalette(enc); + if (err != VP8_ENC_OK) goto Error; + if (enc->use_palette_) { + err = AllocateTransformBuffer(enc, width, height); + if (err != VP8_ENC_OK) goto Error; + err = EncodeDeltaPalettePredictorImage(bw, enc, quality, low_effort); + if (err != VP8_ENC_OK) goto Error; + use_delta_palette = 1; + } + } +#endif // WEBP_EXPERIMENTAL_FEATURES + + // Encode palette + if (enc->use_palette_) { + err = EncodePalette(bw, low_effort, enc); + if (err != VP8_ENC_OK) goto Error; + err = MapImageFromPalette(enc, use_delta_palette); + if (err != VP8_ENC_OK) goto Error; + // If using a color cache, do not have it bigger than the number of colors. + if (use_cache && enc->palette_size_ < (1 << MAX_COLOR_CACHE_BITS)) { + enc->cache_bits_ = BitsLog2Floor(enc->palette_size_) + 1; + } + } + if (!use_delta_palette) { + // In case image is not packed. + if (enc->argb_ == NULL) { + err = MakeInputImageCopy(enc); + if (err != VP8_ENC_OK) goto Error; + } + + // ------------------------------------------------------------------------- + // Apply transforms and write transform data. + + if (enc->use_subtract_green_) { + ApplySubtractGreen(enc, enc->current_width_, height, bw); + } + + if (enc->use_predict_) { + err = ApplyPredictFilter(enc, enc->current_width_, height, quality, + low_effort, enc->use_subtract_green_, bw); + if (err != VP8_ENC_OK) goto Error; + } + + if (enc->use_cross_color_) { + err = ApplyCrossColorFilter(enc, enc->current_width_, + height, quality, low_effort, bw); + if (err != VP8_ENC_OK) goto Error; + } + } + + VP8LPutBits(bw, !TRANSFORM_PRESENT, 1); // No more transforms. + + // --------------------------------------------------------------------------- + // Encode and write the transformed image. + err = EncodeImageInternal(bw, enc->argb_, &enc->hash_chain_, enc->refs_, + enc->current_width_, height, quality, low_effort, + use_cache, &enc->cache_bits_, enc->histo_bits_, + byte_position, &hdr_size, &data_size); + if (err != VP8_ENC_OK) goto Error; + + if (picture->stats != NULL) { + WebPAuxStats* const stats = picture->stats; + stats->lossless_features = 0; + if (enc->use_predict_) stats->lossless_features |= 1; + if (enc->use_cross_color_) stats->lossless_features |= 2; + if (enc->use_subtract_green_) stats->lossless_features |= 4; + if (enc->use_palette_) stats->lossless_features |= 8; + stats->histogram_bits = enc->histo_bits_; + stats->transform_bits = enc->transform_bits_; + stats->cache_bits = enc->cache_bits_; + stats->palette_size = enc->palette_size_; + stats->lossless_size = (int)(VP8LBitWriterNumBytes(bw) - byte_position); + stats->lossless_hdr_size = hdr_size; + stats->lossless_data_size = data_size; + } + + Error: + VP8LEncoderDelete(enc); + return err; +} + +int VP8LEncodeImage(const WebPConfig* const config, + const WebPPicture* const picture) { + int width, height; + int has_alpha; + size_t coded_size; + int percent = 0; + int initial_size; + WebPEncodingError err = VP8_ENC_OK; + VP8LBitWriter bw; + + if (picture == NULL) return 0; + + if (config == NULL || picture->argb == NULL) { + err = VP8_ENC_ERROR_NULL_PARAMETER; + WebPEncodingSetError(picture, err); + return 0; + } + + width = picture->width; + height = picture->height; + // Initialize BitWriter with size corresponding to 16 bpp to photo images and + // 8 bpp for graphical images. + initial_size = (config->image_hint == WEBP_HINT_GRAPH) ? + width * height : width * height * 2; + if (!VP8LBitWriterInit(&bw, initial_size)) { + err = VP8_ENC_ERROR_OUT_OF_MEMORY; + goto Error; + } + + if (!WebPReportProgress(picture, 1, &percent)) { + UserAbort: + err = VP8_ENC_ERROR_USER_ABORT; + goto Error; + } + // Reset stats (for pure lossless coding) + if (picture->stats != NULL) { + WebPAuxStats* const stats = picture->stats; + memset(stats, 0, sizeof(*stats)); + stats->PSNR[0] = 99.f; + stats->PSNR[1] = 99.f; + stats->PSNR[2] = 99.f; + stats->PSNR[3] = 99.f; + stats->PSNR[4] = 99.f; + } + + // Write image size. + if (!WriteImageSize(picture, &bw)) { + err = VP8_ENC_ERROR_OUT_OF_MEMORY; + goto Error; + } + + has_alpha = WebPPictureHasTransparency(picture); + // Write the non-trivial Alpha flag and lossless version. + if (!WriteRealAlphaAndVersion(&bw, has_alpha)) { + err = VP8_ENC_ERROR_OUT_OF_MEMORY; + goto Error; + } + + if (!WebPReportProgress(picture, 5, &percent)) goto UserAbort; + + // Encode main image stream. + err = VP8LEncodeStream(config, picture, &bw, 1 /*use_cache*/); + if (err != VP8_ENC_OK) goto Error; + + // TODO(skal): have a fine-grained progress report in VP8LEncodeStream(). + if (!WebPReportProgress(picture, 90, &percent)) goto UserAbort; + + // Finish the RIFF chunk. + err = WriteImage(picture, &bw, &coded_size); + if (err != VP8_ENC_OK) goto Error; + + if (!WebPReportProgress(picture, 100, &percent)) goto UserAbort; + + // Save size. + if (picture->stats != NULL) { + picture->stats->coded_size += (int)coded_size; + picture->stats->lossless_size = (int)coded_size; + } + + if (picture->extra_info != NULL) { + const int mb_w = (width + 15) >> 4; + const int mb_h = (height + 15) >> 4; + memset(picture->extra_info, 0, mb_w * mb_h * sizeof(*picture->extra_info)); + } + + Error: + if (bw.error_) err = VP8_ENC_ERROR_OUT_OF_MEMORY; + VP8LBitWriterWipeOut(&bw); + if (err != VP8_ENC_OK) { + WebPEncodingSetError(picture, err); + return 0; + } + return 1; +} + +//------------------------------------------------------------------------------ diff --git a/thirdparty/libwebp/enc/vp8li.h b/thirdparty/libwebp/enc/vp8li.h deleted file mode 100644 index 371e276ee0..0000000000 --- a/thirdparty/libwebp/enc/vp8li.h +++ /dev/null @@ -1,81 +0,0 @@ -// Copyright 2012 Google Inc. All Rights Reserved. -// -// Use of this source code is governed by a BSD-style license -// that can be found in the COPYING file in the root of the source -// tree. An additional intellectual property rights grant can be found -// in the file PATENTS. All contributing project authors may -// be found in the AUTHORS file in the root of the source tree. -// ----------------------------------------------------------------------------- -// -// Lossless encoder: internal header. -// -// Author: Vikas Arora (vikaas.arora@gmail.com) - -#ifndef WEBP_ENC_VP8LI_H_ -#define WEBP_ENC_VP8LI_H_ - -#include "./backward_references.h" -#include "./histogram.h" -#include "../utils/bit_writer.h" -#include "../webp/encode.h" -#include "../webp/format_constants.h" - -#ifdef __cplusplus -extern "C" { -#endif - -typedef struct { - const WebPConfig* config_; // user configuration and parameters - const WebPPicture* pic_; // input picture. - - uint32_t* argb_; // Transformed argb image data. - uint32_t* argb_scratch_; // Scratch memory for argb rows - // (used for prediction). - uint32_t* transform_data_; // Scratch memory for transform data. - uint32_t* transform_mem_; // Currently allocated memory. - size_t transform_mem_size_; // Currently allocated memory size. - - int current_width_; // Corresponds to packed image width. - - // Encoding parameters derived from quality parameter. - int histo_bits_; - int transform_bits_; - int cache_bits_; // If equal to 0, don't use color cache. - - // Encoding parameters derived from image characteristics. - int use_cross_color_; - int use_subtract_green_; - int use_predict_; - int use_palette_; - int palette_size_; - uint32_t palette_[MAX_PALETTE_SIZE]; - - // Some 'scratch' (potentially large) objects. - struct VP8LBackwardRefs refs_[2]; // Backward Refs array corresponding to - // LZ77 & RLE coding. - VP8LHashChain hash_chain_; // HashChain data for constructing - // backward references. -} VP8LEncoder; - -//------------------------------------------------------------------------------ -// internal functions. Not public. - -// Encodes the picture. -// Returns 0 if config or picture is NULL or picture doesn't have valid argb -// input. -int VP8LEncodeImage(const WebPConfig* const config, - const WebPPicture* const picture); - -// Encodes the main image stream using the supplied bit writer. -// If 'use_cache' is false, disables the use of color cache. -WebPEncodingError VP8LEncodeStream(const WebPConfig* const config, - const WebPPicture* const picture, - VP8LBitWriter* const bw, int use_cache); - -//------------------------------------------------------------------------------ - -#ifdef __cplusplus -} // extern "C" -#endif - -#endif /* WEBP_ENC_VP8LI_H_ */ diff --git a/thirdparty/libwebp/enc/vp8li_enc.h b/thirdparty/libwebp/enc/vp8li_enc.h new file mode 100644 index 0000000000..8c5fbcbb2e --- /dev/null +++ b/thirdparty/libwebp/enc/vp8li_enc.h @@ -0,0 +1,95 @@ +// Copyright 2012 Google Inc. All Rights Reserved. +// +// Use of this source code is governed by a BSD-style license +// that can be found in the COPYING file in the root of the source +// tree. An additional intellectual property rights grant can be found +// in the file PATENTS. All contributing project authors may +// be found in the AUTHORS file in the root of the source tree. +// ----------------------------------------------------------------------------- +// +// Lossless encoder: internal header. +// +// Author: Vikas Arora (vikaas.arora@gmail.com) + +#ifndef WEBP_ENC_VP8LI_H_ +#define WEBP_ENC_VP8LI_H_ + +#include "./backward_references_enc.h" +#include "./histogram_enc.h" +#include "../utils/bit_writer_utils.h" +#include "../webp/encode.h" +#include "../webp/format_constants.h" + +#ifdef __cplusplus +extern "C" { +#endif + +// maximum value of transform_bits_ in VP8LEncoder. +#define MAX_TRANSFORM_BITS 6 + +typedef struct { + const WebPConfig* config_; // user configuration and parameters + const WebPPicture* pic_; // input picture. + + uint32_t* argb_; // Transformed argb image data. + uint32_t* argb_scratch_; // Scratch memory for argb rows + // (used for prediction). + uint32_t* transform_data_; // Scratch memory for transform data. + uint32_t* transform_mem_; // Currently allocated memory. + size_t transform_mem_size_; // Currently allocated memory size. + + int current_width_; // Corresponds to packed image width. + + // Encoding parameters derived from quality parameter. + int histo_bits_; + int transform_bits_; // <= MAX_TRANSFORM_BITS. + int cache_bits_; // If equal to 0, don't use color cache. + + // Encoding parameters derived from image characteristics. + int use_cross_color_; + int use_subtract_green_; + int use_predict_; + int use_palette_; + int palette_size_; + uint32_t palette_[MAX_PALETTE_SIZE]; + + // Some 'scratch' (potentially large) objects. + struct VP8LBackwardRefs refs_[2]; // Backward Refs array corresponding to + // LZ77 & RLE coding. + VP8LHashChain hash_chain_; // HashChain data for constructing + // backward references. +} VP8LEncoder; + +//------------------------------------------------------------------------------ +// internal functions. Not public. + +// Encodes the picture. +// Returns 0 if config or picture is NULL or picture doesn't have valid argb +// input. +int VP8LEncodeImage(const WebPConfig* const config, + const WebPPicture* const picture); + +// Encodes the main image stream using the supplied bit writer. +// If 'use_cache' is false, disables the use of color cache. +WebPEncodingError VP8LEncodeStream(const WebPConfig* const config, + const WebPPicture* const picture, + VP8LBitWriter* const bw, int use_cache); + +//------------------------------------------------------------------------------ +// Image transforms in predictor.c. + +void VP8LResidualImage(int width, int height, int bits, int low_effort, + uint32_t* const argb, uint32_t* const argb_scratch, + uint32_t* const image, int near_lossless, int exact, + int used_subtract_green); + +void VP8LColorSpaceTransform(int width, int height, int bits, int quality, + uint32_t* const argb, uint32_t* image); + +//------------------------------------------------------------------------------ + +#ifdef __cplusplus +} // extern "C" +#endif + +#endif /* WEBP_ENC_VP8LI_H_ */ diff --git a/thirdparty/libwebp/enc/webp_enc.c b/thirdparty/libwebp/enc/webp_enc.c new file mode 100644 index 0000000000..f18461ef92 --- /dev/null +++ b/thirdparty/libwebp/enc/webp_enc.c @@ -0,0 +1,398 @@ +// Copyright 2011 Google Inc. All Rights Reserved. +// +// Use of this source code is governed by a BSD-style license +// that can be found in the COPYING file in the root of the source +// tree. An additional intellectual property rights grant can be found +// in the file PATENTS. All contributing project authors may +// be found in the AUTHORS file in the root of the source tree. +// ----------------------------------------------------------------------------- +// +// WebP encoder: main entry point +// +// Author: Skal (pascal.massimino@gmail.com) + +#include +#include +#include +#include + +#include "./cost_enc.h" +#include "./vp8i_enc.h" +#include "./vp8li_enc.h" +#include "../utils/utils.h" + +// #define PRINT_MEMORY_INFO + +#ifdef PRINT_MEMORY_INFO +#include +#endif + +//------------------------------------------------------------------------------ + +int WebPGetEncoderVersion(void) { + return (ENC_MAJ_VERSION << 16) | (ENC_MIN_VERSION << 8) | ENC_REV_VERSION; +} + +//------------------------------------------------------------------------------ +// VP8Encoder +//------------------------------------------------------------------------------ + +static void ResetSegmentHeader(VP8Encoder* const enc) { + VP8EncSegmentHeader* const hdr = &enc->segment_hdr_; + hdr->num_segments_ = enc->config_->segments; + hdr->update_map_ = (hdr->num_segments_ > 1); + hdr->size_ = 0; +} + +static void ResetFilterHeader(VP8Encoder* const enc) { + VP8EncFilterHeader* const hdr = &enc->filter_hdr_; + hdr->simple_ = 1; + hdr->level_ = 0; + hdr->sharpness_ = 0; + hdr->i4x4_lf_delta_ = 0; +} + +static void ResetBoundaryPredictions(VP8Encoder* const enc) { + // init boundary values once for all + // Note: actually, initializing the preds_[] is only needed for intra4. + int i; + uint8_t* const top = enc->preds_ - enc->preds_w_; + uint8_t* const left = enc->preds_ - 1; + for (i = -1; i < 4 * enc->mb_w_; ++i) { + top[i] = B_DC_PRED; + } + for (i = 0; i < 4 * enc->mb_h_; ++i) { + left[i * enc->preds_w_] = B_DC_PRED; + } + enc->nz_[-1] = 0; // constant +} + +// Mapping from config->method_ to coding tools used. +//-------------------+---+---+---+---+---+---+---+ +// Method | 0 | 1 | 2 | 3 |(4)| 5 | 6 | +//-------------------+---+---+---+---+---+---+---+ +// fast probe | x | | | x | | | | +//-------------------+---+---+---+---+---+---+---+ +// dynamic proba | ~ | x | x | x | x | x | x | +//-------------------+---+---+---+---+---+---+---+ +// fast mode analysis|[x]|[x]| | | x | x | x | +//-------------------+---+---+---+---+---+---+---+ +// basic rd-opt | | | | x | x | x | x | +//-------------------+---+---+---+---+---+---+---+ +// disto-refine i4/16| x | x | x | | | | | +//-------------------+---+---+---+---+---+---+---+ +// disto-refine uv | | x | x | | | | | +//-------------------+---+---+---+---+---+---+---+ +// rd-opt i4/16 | | | ~ | x | x | x | x | +//-------------------+---+---+---+---+---+---+---+ +// token buffer (opt)| | | | x | x | x | x | +//-------------------+---+---+---+---+---+---+---+ +// Trellis | | | | | | x |Ful| +//-------------------+---+---+---+---+---+---+---+ +// full-SNS | | | | | x | x | x | +//-------------------+---+---+---+---+---+---+---+ + +static void MapConfigToTools(VP8Encoder* const enc) { + const WebPConfig* const config = enc->config_; + const int method = config->method; + const int limit = 100 - config->partition_limit; + enc->method_ = method; + enc->rd_opt_level_ = (method >= 6) ? RD_OPT_TRELLIS_ALL + : (method >= 5) ? RD_OPT_TRELLIS + : (method >= 3) ? RD_OPT_BASIC + : RD_OPT_NONE; + enc->max_i4_header_bits_ = + 256 * 16 * 16 * // upper bound: up to 16bit per 4x4 block + (limit * limit) / (100 * 100); // ... modulated with a quadratic curve. + + // partition0 = 512k max. + enc->mb_header_limit_ = + (score_t)256 * 510 * 8 * 1024 / (enc->mb_w_ * enc->mb_h_); + + enc->thread_level_ = config->thread_level; + + enc->do_search_ = (config->target_size > 0 || config->target_PSNR > 0); + if (!config->low_memory) { +#if !defined(DISABLE_TOKEN_BUFFER) + enc->use_tokens_ = (enc->rd_opt_level_ >= RD_OPT_BASIC); // need rd stats +#endif + if (enc->use_tokens_) { + enc->num_parts_ = 1; // doesn't work with multi-partition + } + } +} + +// Memory scaling with dimensions: +// memory (bytes) ~= 2.25 * w + 0.0625 * w * h +// +// Typical memory footprint (614x440 picture) +// encoder: 22111 +// info: 4368 +// preds: 17741 +// top samples: 1263 +// non-zero: 175 +// lf-stats: 0 +// total: 45658 +// Transient object sizes: +// VP8EncIterator: 3360 +// VP8ModeScore: 872 +// VP8SegmentInfo: 732 +// VP8EncProba: 18352 +// LFStats: 2048 +// Picture size (yuv): 419328 + +static VP8Encoder* InitVP8Encoder(const WebPConfig* const config, + WebPPicture* const picture) { + VP8Encoder* enc; + const int use_filter = + (config->filter_strength > 0) || (config->autofilter > 0); + const int mb_w = (picture->width + 15) >> 4; + const int mb_h = (picture->height + 15) >> 4; + const int preds_w = 4 * mb_w + 1; + const int preds_h = 4 * mb_h + 1; + const size_t preds_size = preds_w * preds_h * sizeof(*enc->preds_); + const int top_stride = mb_w * 16; + const size_t nz_size = (mb_w + 1) * sizeof(*enc->nz_) + WEBP_ALIGN_CST; + const size_t info_size = mb_w * mb_h * sizeof(*enc->mb_info_); + const size_t samples_size = + 2 * top_stride * sizeof(*enc->y_top_) // top-luma/u/v + + WEBP_ALIGN_CST; // align all + const size_t lf_stats_size = + config->autofilter ? sizeof(*enc->lf_stats_) + WEBP_ALIGN_CST : 0; + uint8_t* mem; + const uint64_t size = (uint64_t)sizeof(*enc) // main struct + + WEBP_ALIGN_CST // cache alignment + + info_size // modes info + + preds_size // prediction modes + + samples_size // top/left samples + + nz_size // coeff context bits + + lf_stats_size; // autofilter stats + +#ifdef PRINT_MEMORY_INFO + printf("===================================\n"); + printf("Memory used:\n" + " encoder: %ld\n" + " info: %ld\n" + " preds: %ld\n" + " top samples: %ld\n" + " non-zero: %ld\n" + " lf-stats: %ld\n" + " total: %ld\n", + sizeof(*enc) + WEBP_ALIGN_CST, info_size, + preds_size, samples_size, nz_size, lf_stats_size, size); + printf("Transient object sizes:\n" + " VP8EncIterator: %ld\n" + " VP8ModeScore: %ld\n" + " VP8SegmentInfo: %ld\n" + " VP8EncProba: %ld\n" + " LFStats: %ld\n", + sizeof(VP8EncIterator), sizeof(VP8ModeScore), + sizeof(VP8SegmentInfo), sizeof(VP8EncProba), + sizeof(LFStats)); + printf("Picture size (yuv): %ld\n", + mb_w * mb_h * 384 * sizeof(uint8_t)); + printf("===================================\n"); +#endif + mem = (uint8_t*)WebPSafeMalloc(size, sizeof(*mem)); + if (mem == NULL) { + WebPEncodingSetError(picture, VP8_ENC_ERROR_OUT_OF_MEMORY); + return NULL; + } + enc = (VP8Encoder*)mem; + mem = (uint8_t*)WEBP_ALIGN(mem + sizeof(*enc)); + memset(enc, 0, sizeof(*enc)); + enc->num_parts_ = 1 << config->partitions; + enc->mb_w_ = mb_w; + enc->mb_h_ = mb_h; + enc->preds_w_ = preds_w; + enc->mb_info_ = (VP8MBInfo*)mem; + mem += info_size; + enc->preds_ = ((uint8_t*)mem) + 1 + enc->preds_w_; + mem += preds_size; + enc->nz_ = 1 + (uint32_t*)WEBP_ALIGN(mem); + mem += nz_size; + enc->lf_stats_ = lf_stats_size ? (LFStats*)WEBP_ALIGN(mem) : NULL; + mem += lf_stats_size; + + // top samples (all 16-aligned) + mem = (uint8_t*)WEBP_ALIGN(mem); + enc->y_top_ = (uint8_t*)mem; + enc->uv_top_ = enc->y_top_ + top_stride; + mem += 2 * top_stride; + assert(mem <= (uint8_t*)enc + size); + + enc->config_ = config; + enc->profile_ = use_filter ? ((config->filter_type == 1) ? 0 : 1) : 2; + enc->pic_ = picture; + enc->percent_ = 0; + + MapConfigToTools(enc); + VP8EncDspInit(); + VP8DefaultProbas(enc); + ResetSegmentHeader(enc); + ResetFilterHeader(enc); + ResetBoundaryPredictions(enc); + VP8EncDspCostInit(); + VP8EncInitAlpha(enc); + + // lower quality means smaller output -> we modulate a little the page + // size based on quality. This is just a crude 1rst-order prediction. + { + const float scale = 1.f + config->quality * 5.f / 100.f; // in [1,6] + VP8TBufferInit(&enc->tokens_, (int)(mb_w * mb_h * 4 * scale)); + } + return enc; +} + +static int DeleteVP8Encoder(VP8Encoder* enc) { + int ok = 1; + if (enc != NULL) { + ok = VP8EncDeleteAlpha(enc); + VP8TBufferClear(&enc->tokens_); + WebPSafeFree(enc); + } + return ok; +} + +//------------------------------------------------------------------------------ + +static double GetPSNR(uint64_t err, uint64_t size) { + return (err > 0 && size > 0) ? 10. * log10(255. * 255. * size / err) : 99.; +} + +static void FinalizePSNR(const VP8Encoder* const enc) { + WebPAuxStats* stats = enc->pic_->stats; + const uint64_t size = enc->sse_count_; + const uint64_t* const sse = enc->sse_; + stats->PSNR[0] = (float)GetPSNR(sse[0], size); + stats->PSNR[1] = (float)GetPSNR(sse[1], size / 4); + stats->PSNR[2] = (float)GetPSNR(sse[2], size / 4); + stats->PSNR[3] = (float)GetPSNR(sse[0] + sse[1] + sse[2], size * 3 / 2); + stats->PSNR[4] = (float)GetPSNR(sse[3], size); +} + +static void StoreStats(VP8Encoder* const enc) { + WebPAuxStats* const stats = enc->pic_->stats; + if (stats != NULL) { + int i, s; + for (i = 0; i < NUM_MB_SEGMENTS; ++i) { + stats->segment_level[i] = enc->dqm_[i].fstrength_; + stats->segment_quant[i] = enc->dqm_[i].quant_; + for (s = 0; s <= 2; ++s) { + stats->residual_bytes[s][i] = enc->residual_bytes_[s][i]; + } + } + FinalizePSNR(enc); + stats->coded_size = enc->coded_size_; + for (i = 0; i < 3; ++i) { + stats->block_count[i] = enc->block_count_[i]; + } + } + WebPReportProgress(enc->pic_, 100, &enc->percent_); // done! +} + +int WebPEncodingSetError(const WebPPicture* const pic, + WebPEncodingError error) { + assert((int)error < VP8_ENC_ERROR_LAST); + assert((int)error >= VP8_ENC_OK); + ((WebPPicture*)pic)->error_code = error; + return 0; +} + +int WebPReportProgress(const WebPPicture* const pic, + int percent, int* const percent_store) { + if (percent_store != NULL && percent != *percent_store) { + *percent_store = percent; + if (pic->progress_hook && !pic->progress_hook(percent, pic)) { + // user abort requested + WebPEncodingSetError(pic, VP8_ENC_ERROR_USER_ABORT); + return 0; + } + } + return 1; // ok +} +//------------------------------------------------------------------------------ + +int WebPEncode(const WebPConfig* config, WebPPicture* pic) { + int ok = 0; + if (pic == NULL) return 0; + + WebPEncodingSetError(pic, VP8_ENC_OK); // all ok so far + if (config == NULL) { // bad params + return WebPEncodingSetError(pic, VP8_ENC_ERROR_NULL_PARAMETER); + } + if (!WebPValidateConfig(config)) { + return WebPEncodingSetError(pic, VP8_ENC_ERROR_INVALID_CONFIGURATION); + } + if (pic->width <= 0 || pic->height <= 0) { + return WebPEncodingSetError(pic, VP8_ENC_ERROR_BAD_DIMENSION); + } + if (pic->width > WEBP_MAX_DIMENSION || pic->height > WEBP_MAX_DIMENSION) { + return WebPEncodingSetError(pic, VP8_ENC_ERROR_BAD_DIMENSION); + } + + if (pic->stats != NULL) memset(pic->stats, 0, sizeof(*pic->stats)); + + if (!config->lossless) { + VP8Encoder* enc = NULL; + + if (!config->exact) { + WebPCleanupTransparentArea(pic); + } + + if (pic->use_argb || pic->y == NULL || pic->u == NULL || pic->v == NULL) { + // Make sure we have YUVA samples. + if (config->use_sharp_yuv || (config->preprocessing & 4)) { + if (!WebPPictureSharpARGBToYUVA(pic)) { + return 0; + } + } else { + float dithering = 0.f; + if (config->preprocessing & 2) { + const float x = config->quality / 100.f; + const float x2 = x * x; + // slowly decreasing from max dithering at low quality (q->0) + // to 0.5 dithering amplitude at high quality (q->100) + dithering = 1.0f + (0.5f - 1.0f) * x2 * x2; + } + if (!WebPPictureARGBToYUVADithered(pic, WEBP_YUV420, dithering)) { + return 0; + } + } + } + + enc = InitVP8Encoder(config, pic); + if (enc == NULL) return 0; // pic->error is already set. + // Note: each of the tasks below account for 20% in the progress report. + ok = VP8EncAnalyze(enc); + + // Analysis is done, proceed to actual coding. + ok = ok && VP8EncStartAlpha(enc); // possibly done in parallel + if (!enc->use_tokens_) { + ok = ok && VP8EncLoop(enc); + } else { + ok = ok && VP8EncTokenLoop(enc); + } + ok = ok && VP8EncFinishAlpha(enc); + + ok = ok && VP8EncWrite(enc); + StoreStats(enc); + if (!ok) { + VP8EncFreeBitWriters(enc); + } + ok &= DeleteVP8Encoder(enc); // must always be called, even if !ok + } else { + // Make sure we have ARGB samples. + if (pic->argb == NULL && !WebPPictureYUVAToARGB(pic)) { + return 0; + } + + if (!config->exact) { + WebPCleanupTransparentAreaLossless(pic); + } + + ok = VP8LEncodeImage(config, pic); // Sets pic->error in case of problem. + } + + return ok; +} diff --git a/thirdparty/libwebp/enc/webpenc.c b/thirdparty/libwebp/enc/webpenc.c deleted file mode 100644 index a7d04ea2ce..0000000000 --- a/thirdparty/libwebp/enc/webpenc.c +++ /dev/null @@ -1,395 +0,0 @@ -// Copyright 2011 Google Inc. All Rights Reserved. -// -// Use of this source code is governed by a BSD-style license -// that can be found in the COPYING file in the root of the source -// tree. An additional intellectual property rights grant can be found -// in the file PATENTS. All contributing project authors may -// be found in the AUTHORS file in the root of the source tree. -// ----------------------------------------------------------------------------- -// -// WebP encoder: main entry point -// -// Author: Skal (pascal.massimino@gmail.com) - -#include -#include -#include -#include - -#include "./cost.h" -#include "./vp8enci.h" -#include "./vp8li.h" -#include "../utils/utils.h" - -// #define PRINT_MEMORY_INFO - -#ifdef PRINT_MEMORY_INFO -#include -#endif - -//------------------------------------------------------------------------------ - -int WebPGetEncoderVersion(void) { - return (ENC_MAJ_VERSION << 16) | (ENC_MIN_VERSION << 8) | ENC_REV_VERSION; -} - -//------------------------------------------------------------------------------ -// VP8Encoder -//------------------------------------------------------------------------------ - -static void ResetSegmentHeader(VP8Encoder* const enc) { - VP8EncSegmentHeader* const hdr = &enc->segment_hdr_; - hdr->num_segments_ = enc->config_->segments; - hdr->update_map_ = (hdr->num_segments_ > 1); - hdr->size_ = 0; -} - -static void ResetFilterHeader(VP8Encoder* const enc) { - VP8EncFilterHeader* const hdr = &enc->filter_hdr_; - hdr->simple_ = 1; - hdr->level_ = 0; - hdr->sharpness_ = 0; - hdr->i4x4_lf_delta_ = 0; -} - -static void ResetBoundaryPredictions(VP8Encoder* const enc) { - // init boundary values once for all - // Note: actually, initializing the preds_[] is only needed for intra4. - int i; - uint8_t* const top = enc->preds_ - enc->preds_w_; - uint8_t* const left = enc->preds_ - 1; - for (i = -1; i < 4 * enc->mb_w_; ++i) { - top[i] = B_DC_PRED; - } - for (i = 0; i < 4 * enc->mb_h_; ++i) { - left[i * enc->preds_w_] = B_DC_PRED; - } - enc->nz_[-1] = 0; // constant -} - -// Mapping from config->method_ to coding tools used. -//-------------------+---+---+---+---+---+---+---+ -// Method | 0 | 1 | 2 | 3 |(4)| 5 | 6 | -//-------------------+---+---+---+---+---+---+---+ -// fast probe | x | | | x | | | | -//-------------------+---+---+---+---+---+---+---+ -// dynamic proba | ~ | x | x | x | x | x | x | -//-------------------+---+---+---+---+---+---+---+ -// fast mode analysis| | | | | x | x | x | -//-------------------+---+---+---+---+---+---+---+ -// basic rd-opt | | | | x | x | x | x | -//-------------------+---+---+---+---+---+---+---+ -// disto-refine i4/16| x | x | x | | | | | -//-------------------+---+---+---+---+---+---+---+ -// disto-refine uv | | x | x | | | | | -//-------------------+---+---+---+---+---+---+---+ -// rd-opt i4/16 | | | ~ | x | x | x | x | -//-------------------+---+---+---+---+---+---+---+ -// token buffer (opt)| | | | x | x | x | x | -//-------------------+---+---+---+---+---+---+---+ -// Trellis | | | | | | x |Ful| -//-------------------+---+---+---+---+---+---+---+ -// full-SNS | | | | | x | x | x | -//-------------------+---+---+---+---+---+---+---+ - -static void MapConfigToTools(VP8Encoder* const enc) { - const WebPConfig* const config = enc->config_; - const int method = config->method; - const int limit = 100 - config->partition_limit; - enc->method_ = method; - enc->rd_opt_level_ = (method >= 6) ? RD_OPT_TRELLIS_ALL - : (method >= 5) ? RD_OPT_TRELLIS - : (method >= 3) ? RD_OPT_BASIC - : RD_OPT_NONE; - enc->max_i4_header_bits_ = - 256 * 16 * 16 * // upper bound: up to 16bit per 4x4 block - (limit * limit) / (100 * 100); // ... modulated with a quadratic curve. - - // partition0 = 512k max. - enc->mb_header_limit_ = - (score_t)256 * 510 * 8 * 1024 / (enc->mb_w_ * enc->mb_h_); - - enc->thread_level_ = config->thread_level; - - enc->do_search_ = (config->target_size > 0 || config->target_PSNR > 0); - if (!config->low_memory) { -#if !defined(DISABLE_TOKEN_BUFFER) - enc->use_tokens_ = (enc->rd_opt_level_ >= RD_OPT_BASIC); // need rd stats -#endif - if (enc->use_tokens_) { - enc->num_parts_ = 1; // doesn't work with multi-partition - } - } -} - -// Memory scaling with dimensions: -// memory (bytes) ~= 2.25 * w + 0.0625 * w * h -// -// Typical memory footprint (614x440 picture) -// encoder: 22111 -// info: 4368 -// preds: 17741 -// top samples: 1263 -// non-zero: 175 -// lf-stats: 0 -// total: 45658 -// Transient object sizes: -// VP8EncIterator: 3360 -// VP8ModeScore: 872 -// VP8SegmentInfo: 732 -// VP8EncProba: 18352 -// LFStats: 2048 -// Picture size (yuv): 419328 - -static VP8Encoder* InitVP8Encoder(const WebPConfig* const config, - WebPPicture* const picture) { - VP8Encoder* enc; - const int use_filter = - (config->filter_strength > 0) || (config->autofilter > 0); - const int mb_w = (picture->width + 15) >> 4; - const int mb_h = (picture->height + 15) >> 4; - const int preds_w = 4 * mb_w + 1; - const int preds_h = 4 * mb_h + 1; - const size_t preds_size = preds_w * preds_h * sizeof(*enc->preds_); - const int top_stride = mb_w * 16; - const size_t nz_size = (mb_w + 1) * sizeof(*enc->nz_) + WEBP_ALIGN_CST; - const size_t info_size = mb_w * mb_h * sizeof(*enc->mb_info_); - const size_t samples_size = - 2 * top_stride * sizeof(*enc->y_top_) // top-luma/u/v - + WEBP_ALIGN_CST; // align all - const size_t lf_stats_size = - config->autofilter ? sizeof(*enc->lf_stats_) + WEBP_ALIGN_CST : 0; - uint8_t* mem; - const uint64_t size = (uint64_t)sizeof(*enc) // main struct - + WEBP_ALIGN_CST // cache alignment - + info_size // modes info - + preds_size // prediction modes - + samples_size // top/left samples - + nz_size // coeff context bits - + lf_stats_size; // autofilter stats - -#ifdef PRINT_MEMORY_INFO - printf("===================================\n"); - printf("Memory used:\n" - " encoder: %ld\n" - " info: %ld\n" - " preds: %ld\n" - " top samples: %ld\n" - " non-zero: %ld\n" - " lf-stats: %ld\n" - " total: %ld\n", - sizeof(*enc) + WEBP_ALIGN_CST, info_size, - preds_size, samples_size, nz_size, lf_stats_size, size); - printf("Transient object sizes:\n" - " VP8EncIterator: %ld\n" - " VP8ModeScore: %ld\n" - " VP8SegmentInfo: %ld\n" - " VP8EncProba: %ld\n" - " LFStats: %ld\n", - sizeof(VP8EncIterator), sizeof(VP8ModeScore), - sizeof(VP8SegmentInfo), sizeof(VP8EncProba), - sizeof(LFStats)); - printf("Picture size (yuv): %ld\n", - mb_w * mb_h * 384 * sizeof(uint8_t)); - printf("===================================\n"); -#endif - mem = (uint8_t*)WebPSafeMalloc(size, sizeof(*mem)); - if (mem == NULL) { - WebPEncodingSetError(picture, VP8_ENC_ERROR_OUT_OF_MEMORY); - return NULL; - } - enc = (VP8Encoder*)mem; - mem = (uint8_t*)WEBP_ALIGN(mem + sizeof(*enc)); - memset(enc, 0, sizeof(*enc)); - enc->num_parts_ = 1 << config->partitions; - enc->mb_w_ = mb_w; - enc->mb_h_ = mb_h; - enc->preds_w_ = preds_w; - enc->mb_info_ = (VP8MBInfo*)mem; - mem += info_size; - enc->preds_ = ((uint8_t*)mem) + 1 + enc->preds_w_; - mem += preds_size; - enc->nz_ = 1 + (uint32_t*)WEBP_ALIGN(mem); - mem += nz_size; - enc->lf_stats_ = lf_stats_size ? (LFStats*)WEBP_ALIGN(mem) : NULL; - mem += lf_stats_size; - - // top samples (all 16-aligned) - mem = (uint8_t*)WEBP_ALIGN(mem); - enc->y_top_ = (uint8_t*)mem; - enc->uv_top_ = enc->y_top_ + top_stride; - mem += 2 * top_stride; - assert(mem <= (uint8_t*)enc + size); - - enc->config_ = config; - enc->profile_ = use_filter ? ((config->filter_type == 1) ? 0 : 1) : 2; - enc->pic_ = picture; - enc->percent_ = 0; - - MapConfigToTools(enc); - VP8EncDspInit(); - VP8DefaultProbas(enc); - ResetSegmentHeader(enc); - ResetFilterHeader(enc); - ResetBoundaryPredictions(enc); - VP8EncDspCostInit(); - VP8EncInitAlpha(enc); - - // lower quality means smaller output -> we modulate a little the page - // size based on quality. This is just a crude 1rst-order prediction. - { - const float scale = 1.f + config->quality * 5.f / 100.f; // in [1,6] - VP8TBufferInit(&enc->tokens_, (int)(mb_w * mb_h * 4 * scale)); - } - return enc; -} - -static int DeleteVP8Encoder(VP8Encoder* enc) { - int ok = 1; - if (enc != NULL) { - ok = VP8EncDeleteAlpha(enc); - VP8TBufferClear(&enc->tokens_); - WebPSafeFree(enc); - } - return ok; -} - -//------------------------------------------------------------------------------ - -static double GetPSNR(uint64_t err, uint64_t size) { - return (err > 0 && size > 0) ? 10. * log10(255. * 255. * size / err) : 99.; -} - -static void FinalizePSNR(const VP8Encoder* const enc) { - WebPAuxStats* stats = enc->pic_->stats; - const uint64_t size = enc->sse_count_; - const uint64_t* const sse = enc->sse_; - stats->PSNR[0] = (float)GetPSNR(sse[0], size); - stats->PSNR[1] = (float)GetPSNR(sse[1], size / 4); - stats->PSNR[2] = (float)GetPSNR(sse[2], size / 4); - stats->PSNR[3] = (float)GetPSNR(sse[0] + sse[1] + sse[2], size * 3 / 2); - stats->PSNR[4] = (float)GetPSNR(sse[3], size); -} - -static void StoreStats(VP8Encoder* const enc) { - WebPAuxStats* const stats = enc->pic_->stats; - if (stats != NULL) { - int i, s; - for (i = 0; i < NUM_MB_SEGMENTS; ++i) { - stats->segment_level[i] = enc->dqm_[i].fstrength_; - stats->segment_quant[i] = enc->dqm_[i].quant_; - for (s = 0; s <= 2; ++s) { - stats->residual_bytes[s][i] = enc->residual_bytes_[s][i]; - } - } - FinalizePSNR(enc); - stats->coded_size = enc->coded_size_; - for (i = 0; i < 3; ++i) { - stats->block_count[i] = enc->block_count_[i]; - } - } - WebPReportProgress(enc->pic_, 100, &enc->percent_); // done! -} - -int WebPEncodingSetError(const WebPPicture* const pic, - WebPEncodingError error) { - assert((int)error < VP8_ENC_ERROR_LAST); - assert((int)error >= VP8_ENC_OK); - ((WebPPicture*)pic)->error_code = error; - return 0; -} - -int WebPReportProgress(const WebPPicture* const pic, - int percent, int* const percent_store) { - if (percent_store != NULL && percent != *percent_store) { - *percent_store = percent; - if (pic->progress_hook && !pic->progress_hook(percent, pic)) { - // user abort requested - WebPEncodingSetError(pic, VP8_ENC_ERROR_USER_ABORT); - return 0; - } - } - return 1; // ok -} -//------------------------------------------------------------------------------ - -int WebPEncode(const WebPConfig* config, WebPPicture* pic) { - int ok = 0; - - if (pic == NULL) - return 0; - WebPEncodingSetError(pic, VP8_ENC_OK); // all ok so far - if (config == NULL) // bad params - return WebPEncodingSetError(pic, VP8_ENC_ERROR_NULL_PARAMETER); - if (!WebPValidateConfig(config)) - return WebPEncodingSetError(pic, VP8_ENC_ERROR_INVALID_CONFIGURATION); - if (pic->width <= 0 || pic->height <= 0) - return WebPEncodingSetError(pic, VP8_ENC_ERROR_BAD_DIMENSION); - if (pic->width > WEBP_MAX_DIMENSION || pic->height > WEBP_MAX_DIMENSION) - return WebPEncodingSetError(pic, VP8_ENC_ERROR_BAD_DIMENSION); - - if (pic->stats != NULL) memset(pic->stats, 0, sizeof(*pic->stats)); - - if (!config->lossless) { - VP8Encoder* enc = NULL; - - if (!config->exact) { - WebPCleanupTransparentArea(pic); - } - - if (pic->use_argb || pic->y == NULL || pic->u == NULL || pic->v == NULL) { - // Make sure we have YUVA samples. - if (config->preprocessing & 4) { - if (!WebPPictureSmartARGBToYUVA(pic)) { - return 0; - } - } else { - float dithering = 0.f; - if (config->preprocessing & 2) { - const float x = config->quality / 100.f; - const float x2 = x * x; - // slowly decreasing from max dithering at low quality (q->0) - // to 0.5 dithering amplitude at high quality (q->100) - dithering = 1.0f + (0.5f - 1.0f) * x2 * x2; - } - if (!WebPPictureARGBToYUVADithered(pic, WEBP_YUV420, dithering)) { - return 0; - } - } - } - - enc = InitVP8Encoder(config, pic); - if (enc == NULL) return 0; // pic->error is already set. - // Note: each of the tasks below account for 20% in the progress report. - ok = VP8EncAnalyze(enc); - - // Analysis is done, proceed to actual coding. - ok = ok && VP8EncStartAlpha(enc); // possibly done in parallel - if (!enc->use_tokens_) { - ok = ok && VP8EncLoop(enc); - } else { - ok = ok && VP8EncTokenLoop(enc); - } - ok = ok && VP8EncFinishAlpha(enc); - - ok = ok && VP8EncWrite(enc); - StoreStats(enc); - if (!ok) { - VP8EncFreeBitWriters(enc); - } - ok &= DeleteVP8Encoder(enc); // must always be called, even if !ok - } else { - // Make sure we have ARGB samples. - if (pic->argb == NULL && !WebPPictureYUVAToARGB(pic)) { - return 0; - } - - if (!config->exact) { - WebPCleanupTransparentAreaLossless(pic); - } - - ok = VP8LEncodeImage(config, pic); // Sets pic->error in case of problem. - } - - return ok; -} diff --git a/thirdparty/libwebp/mux/anim_encode.c b/thirdparty/libwebp/mux/anim_encode.c index 398ba8d850..6066388727 100644 --- a/thirdparty/libwebp/mux/anim_encode.c +++ b/thirdparty/libwebp/mux/anim_encode.c @@ -16,6 +16,7 @@ #include #include // for abs() +#include "../mux/animi.h" #include "../utils/utils.h" #include "../webp/decode.h" #include "../webp/encode.h" @@ -128,14 +129,13 @@ static void SanitizeEncoderOptions(WebPAnimEncoderOptions* const enc_options) { DisableKeyframes(enc_options); } - if (enc_options->kmin <= 0) { - DisableKeyframes(enc_options); - print_warning = 0; - } - if (enc_options->kmax <= 0) { // All frames will be key-frames. + if (enc_options->kmax == 1) { // All frames will be key-frames. enc_options->kmin = 0; enc_options->kmax = 0; return; + } else if (enc_options->kmax <= 0) { + DisableKeyframes(enc_options); + print_warning = 0; } if (enc_options->kmin >= enc_options->kmax) { @@ -378,10 +378,10 @@ static WEBP_INLINE int PixelsAreSimilar(uint32_t src, uint32_t dst, const int dst_g = (dst >> 8) & 0xff; const int dst_b = (dst >> 0) & 0xff; - return (abs(src_r * src_a - dst_r * dst_a) <= (max_allowed_diff * 255)) && - (abs(src_g * src_a - dst_g * dst_a) <= (max_allowed_diff * 255)) && - (abs(src_b * src_a - dst_b * dst_a) <= (max_allowed_diff * 255)) && - (abs(src_a - dst_a) <= max_allowed_diff); + return (src_a == dst_a) && + (abs(src_r - dst_r) * dst_a <= (max_allowed_diff * 255)) && + (abs(src_g - dst_g) * dst_a <= (max_allowed_diff * 255)) && + (abs(src_b - dst_b) * dst_a <= (max_allowed_diff * 255)); } // Returns true if 'length' number of pixels in 'src' and 'dst' are within an @@ -586,6 +586,39 @@ static int GetSubRects(const WebPPicture* const prev_canvas, ¶ms->rect_lossy_, ¶ms->sub_frame_lossy_); } +static WEBP_INLINE int clip(int v, int min_v, int max_v) { + return (v < min_v) ? min_v : (v > max_v) ? max_v : v; +} + +int WebPAnimEncoderRefineRect( + const WebPPicture* const prev_canvas, const WebPPicture* const curr_canvas, + int is_lossless, float quality, int* const x_offset, int* const y_offset, + int* const width, int* const height) { + FrameRect rect; + const int right = clip(*x_offset + *width, 0, curr_canvas->width); + const int left = clip(*x_offset, 0, curr_canvas->width - 1); + const int bottom = clip(*y_offset + *height, 0, curr_canvas->height); + const int top = clip(*y_offset, 0, curr_canvas->height - 1); + if (prev_canvas == NULL || curr_canvas == NULL || + prev_canvas->width != curr_canvas->width || + prev_canvas->height != curr_canvas->height || + !prev_canvas->use_argb || !curr_canvas->use_argb) { + return 0; + } + rect.x_offset_ = left; + rect.y_offset_ = top; + rect.width_ = clip(right - left, 0, curr_canvas->width - rect.x_offset_); + rect.height_ = clip(bottom - top, 0, curr_canvas->height - rect.y_offset_); + MinimizeChangeRectangle(prev_canvas, curr_canvas, &rect, is_lossless, + quality); + SnapToEvenOffsets(&rect); + *x_offset = rect.x_offset_; + *y_offset = rect.y_offset_; + *width = rect.width_; + *height = rect.height_; + return 1; +} + static void DisposeFrameRectangle(int dispose_method, const FrameRect* const rect, WebPPicture* const curr_canvas) { diff --git a/thirdparty/libwebp/mux/animi.h b/thirdparty/libwebp/mux/animi.h new file mode 100644 index 0000000000..cecaf1fee5 --- /dev/null +++ b/thirdparty/libwebp/mux/animi.h @@ -0,0 +1,43 @@ +// Copyright 2016 Google Inc. All Rights Reserved. +// +// Use of this source code is governed by a BSD-style license +// that can be found in the COPYING file in the root of the source +// tree. An additional intellectual property rights grant can be found +// in the file PATENTS. All contributing project authors may +// be found in the AUTHORS file in the root of the source tree. +// ----------------------------------------------------------------------------- +// +// Internal header for animation related functions. +// +// Author: Hui Su (huisu@google.com) + +#ifndef WEBP_MUX_ANIMI_H_ +#define WEBP_MUX_ANIMI_H_ + +#include "../webp/mux.h" + +#ifdef __cplusplus +extern "C" { +#endif + +// Picks the optimal rectangle between two pictures, starting with initial +// values of offsets and dimensions that are passed in. The initial +// values will be clipped, if necessary, to make sure the rectangle is +// within the canvas. "use_argb" must be true for both pictures. +// Parameters: +// prev_canvas, curr_canvas - (in) two input pictures to compare. +// is_lossless, quality - (in) encoding settings. +// x_offset, y_offset, width, height - (in/out) rectangle between the two +// input pictures. +// Returns true on success. +int WebPAnimEncoderRefineRect( + const struct WebPPicture* const prev_canvas, + const struct WebPPicture* const curr_canvas, + int is_lossless, float quality, int* const x_offset, int* const y_offset, + int* const width, int* const height); + +#ifdef __cplusplus +} // extern "C" +#endif + +#endif /* WEBP_MUX_ANIMI_H_ */ diff --git a/thirdparty/libwebp/mux/muxedit.c b/thirdparty/libwebp/mux/muxedit.c index 9bbed42b1a..d2c5305372 100644 --- a/thirdparty/libwebp/mux/muxedit.c +++ b/thirdparty/libwebp/mux/muxedit.c @@ -93,34 +93,32 @@ static WebPMuxError MuxSet(WebPMux* const mux, uint32_t tag, uint32_t nth, } #undef SWITCH_ID_LIST -// Create data for frame/fragment given image data, offsets and duration. -static WebPMuxError CreateFrameFragmentData( - int width, int height, const WebPMuxFrameInfo* const info, int is_frame, - WebPData* const frame_frgm) { - uint8_t* frame_frgm_bytes; - const size_t frame_frgm_size = kChunks[is_frame ? IDX_ANMF : IDX_FRGM].size; +// Create data for frame given image data, offsets and duration. +static WebPMuxError CreateFrameData( + int width, int height, const WebPMuxFrameInfo* const info, + WebPData* const frame) { + uint8_t* frame_bytes; + const size_t frame_size = kChunks[IDX_ANMF].size; assert(width > 0 && height > 0 && info->duration >= 0); assert(info->dispose_method == (info->dispose_method & 1)); // Note: assertion on upper bounds is done in PutLE24(). - frame_frgm_bytes = (uint8_t*)WebPSafeMalloc(1ULL, frame_frgm_size); - if (frame_frgm_bytes == NULL) return WEBP_MUX_MEMORY_ERROR; + frame_bytes = (uint8_t*)WebPSafeMalloc(1ULL, frame_size); + if (frame_bytes == NULL) return WEBP_MUX_MEMORY_ERROR; - PutLE24(frame_frgm_bytes + 0, info->x_offset / 2); - PutLE24(frame_frgm_bytes + 3, info->y_offset / 2); + PutLE24(frame_bytes + 0, info->x_offset / 2); + PutLE24(frame_bytes + 3, info->y_offset / 2); - if (is_frame) { - PutLE24(frame_frgm_bytes + 6, width - 1); - PutLE24(frame_frgm_bytes + 9, height - 1); - PutLE24(frame_frgm_bytes + 12, info->duration); - frame_frgm_bytes[15] = - (info->blend_method == WEBP_MUX_NO_BLEND ? 2 : 0) | - (info->dispose_method == WEBP_MUX_DISPOSE_BACKGROUND ? 1 : 0); - } + PutLE24(frame_bytes + 6, width - 1); + PutLE24(frame_bytes + 9, height - 1); + PutLE24(frame_bytes + 12, info->duration); + frame_bytes[15] = + (info->blend_method == WEBP_MUX_NO_BLEND ? 2 : 0) | + (info->dispose_method == WEBP_MUX_DISPOSE_BACKGROUND ? 1 : 0); - frame_frgm->bytes = frame_frgm_bytes; - frame_frgm->size = frame_frgm_size; + frame->bytes = frame_bytes; + frame->size = frame_size; return WEBP_MUX_OK; } @@ -264,23 +262,16 @@ WebPMuxError WebPMuxSetImage(WebPMux* mux, const WebPData* bitstream, return err; } -WebPMuxError WebPMuxPushFrame(WebPMux* mux, const WebPMuxFrameInfo* frame, +WebPMuxError WebPMuxPushFrame(WebPMux* mux, const WebPMuxFrameInfo* info, int copy_data) { WebPMuxImage wpi; WebPMuxError err; - int is_frame; - const WebPData* const bitstream = &frame->bitstream; + const WebPData* const bitstream = &info->bitstream; // Sanity checks. - if (mux == NULL || frame == NULL) return WEBP_MUX_INVALID_ARGUMENT; + if (mux == NULL || info == NULL) return WEBP_MUX_INVALID_ARGUMENT; - is_frame = (frame->id == WEBP_CHUNK_ANMF); - if (!(is_frame || (frame->id == WEBP_CHUNK_FRGM))) { - return WEBP_MUX_INVALID_ARGUMENT; - } - if (frame->id == WEBP_CHUNK_FRGM) { // Dead experiment. - return WEBP_MUX_INVALID_ARGUMENT; - } + if (info->id != WEBP_CHUNK_ANMF) return WEBP_MUX_INVALID_ARGUMENT; if (bitstream->bytes == NULL || bitstream->size > MAX_CHUNK_PAYLOAD) { return WEBP_MUX_INVALID_ARGUMENT; @@ -290,7 +281,7 @@ WebPMuxError WebPMuxPushFrame(WebPMux* mux, const WebPMuxFrameInfo* frame, const WebPMuxImage* const image = mux->images_; const uint32_t image_id = (image->header_ != NULL) ? ChunkGetIdFromTag(image->header_->tag_) : WEBP_CHUNK_IMAGE; - if (image_id != frame->id) { + if (image_id != info->id) { return WEBP_MUX_INVALID_ARGUMENT; // Conflicting frame types. } } @@ -301,16 +292,11 @@ WebPMuxError WebPMuxPushFrame(WebPMux* mux, const WebPMuxFrameInfo* frame, assert(wpi.img_ != NULL); // As SetAlphaAndImageChunks() was successful. { - WebPData frame_frgm; - const uint32_t tag = kChunks[is_frame ? IDX_ANMF : IDX_FRGM].tag; - WebPMuxFrameInfo tmp = *frame; + WebPData frame; + const uint32_t tag = kChunks[IDX_ANMF].tag; + WebPMuxFrameInfo tmp = *info; tmp.x_offset &= ~1; // Snap offsets to even. tmp.y_offset &= ~1; - if (!is_frame) { // Reset unused values. - tmp.duration = 1; - tmp.dispose_method = WEBP_MUX_DISPOSE_NONE; - tmp.blend_method = WEBP_MUX_BLEND; - } if (tmp.x_offset < 0 || tmp.x_offset >= MAX_POSITION_OFFSET || tmp.y_offset < 0 || tmp.y_offset >= MAX_POSITION_OFFSET || (tmp.duration < 0 || tmp.duration >= MAX_DURATION) || @@ -318,12 +304,11 @@ WebPMuxError WebPMuxPushFrame(WebPMux* mux, const WebPMuxFrameInfo* frame, err = WEBP_MUX_INVALID_ARGUMENT; goto Err; } - err = CreateFrameFragmentData(wpi.width_, wpi.height_, &tmp, is_frame, - &frame_frgm); + err = CreateFrameData(wpi.width_, wpi.height_, &tmp, &frame); if (err != WEBP_MUX_OK) goto Err; - // Add frame/fragment chunk (with copy_data = 1). - err = AddDataToChunkList(&frame_frgm, 1, tag, &wpi.header_); - WebPDataClear(&frame_frgm); // frame_frgm owned by wpi.header_ now. + // Add frame chunk (with copy_data = 1). + err = AddDataToChunkList(&frame, 1, tag, &wpi.header_); + WebPDataClear(&frame); // frame owned by wpi.header_ now. if (err != WEBP_MUX_OK) goto Err; } @@ -402,21 +387,18 @@ WebPMuxError WebPMuxDeleteFrame(WebPMux* mux, uint32_t nth) { //------------------------------------------------------------------------------ // Assembly of the WebP RIFF file. -static WebPMuxError GetFrameFragmentInfo( - const WebPChunk* const frame_frgm_chunk, +static WebPMuxError GetFrameInfo( + const WebPChunk* const frame_chunk, int* const x_offset, int* const y_offset, int* const duration) { - const uint32_t tag = frame_frgm_chunk->tag_; - const int is_frame = (tag == kChunks[IDX_ANMF].tag); - const WebPData* const data = &frame_frgm_chunk->data_; - const size_t expected_data_size = - is_frame ? ANMF_CHUNK_SIZE : FRGM_CHUNK_SIZE; - assert(frame_frgm_chunk != NULL); - assert(tag == kChunks[IDX_ANMF].tag || tag == kChunks[IDX_FRGM].tag); + const WebPData* const data = &frame_chunk->data_; + const size_t expected_data_size = ANMF_CHUNK_SIZE; + assert(frame_chunk->tag_ == kChunks[IDX_ANMF].tag); + assert(frame_chunk != NULL); if (data->size != expected_data_size) return WEBP_MUX_INVALID_ARGUMENT; *x_offset = 2 * GetLE24(data->bytes + 0); *y_offset = 2 * GetLE24(data->bytes + 3); - if (is_frame) *duration = GetLE24(data->bytes + 12); + *duration = GetLE24(data->bytes + 12); return WEBP_MUX_OK; } @@ -424,13 +406,13 @@ static WebPMuxError GetImageInfo(const WebPMuxImage* const wpi, int* const x_offset, int* const y_offset, int* const duration, int* const width, int* const height) { - const WebPChunk* const frame_frgm_chunk = wpi->header_; + const WebPChunk* const frame_chunk = wpi->header_; WebPMuxError err; assert(wpi != NULL); - assert(frame_frgm_chunk != NULL); + assert(frame_chunk != NULL); - // Get offsets and duration from ANMF/FRGM chunk. - err = GetFrameFragmentInfo(frame_frgm_chunk, x_offset, y_offset, duration); + // Get offsets and duration from ANMF chunk. + err = GetFrameInfo(frame_chunk, x_offset, y_offset, duration); if (err != WEBP_MUX_OK) return err; // Get width and height from VP8/VP8L chunk. @@ -441,7 +423,6 @@ static WebPMuxError GetImageInfo(const WebPMuxImage* const wpi, // Returns the tightest dimension for the canvas considering the image list. static WebPMuxError GetAdjustedCanvasSize(const WebPMux* const mux, - uint32_t flags, int* const width, int* const height) { WebPMuxImage* wpi = NULL; assert(mux != NULL); @@ -452,12 +433,10 @@ static WebPMuxError GetAdjustedCanvasSize(const WebPMux* const mux, assert(wpi->img_ != NULL); if (wpi->next_ != NULL) { - int max_x = 0; - int max_y = 0; - int64_t image_area = 0; + int max_x = 0, max_y = 0; // if we have a chain of wpi's, header_ is necessarily set assert(wpi->header_ != NULL); - // Aggregate the bounding box for animation frames & fragmented images. + // Aggregate the bounding box for animation frames. for (; wpi != NULL; wpi = wpi->next_) { int x_offset = 0, y_offset = 0, duration = 0, w = 0, h = 0; const WebPMuxError err = GetImageInfo(wpi, &x_offset, &y_offset, @@ -470,19 +449,9 @@ static WebPMuxError GetAdjustedCanvasSize(const WebPMux* const mux, if (max_x_pos > max_x) max_x = max_x_pos; if (max_y_pos > max_y) max_y = max_y_pos; - image_area += w * h; } *width = max_x; *height = max_y; - // Crude check to validate that there are no image overlaps/holes for - // fragmented images. Check that the aggregated image area for individual - // fragments exactly matches the image area of the constructed canvas. - // However, the area-match is necessary but not sufficient condition. - if ((flags & FRAGMENTS_FLAG) && (image_area != (max_x * max_y))) { - *width = 0; - *height = 0; - return WEBP_MUX_INVALID_ARGUMENT; - } } else { // For a single image, canvas dimensions are same as image dimensions. *width = wpi->width_; @@ -528,10 +497,7 @@ static WebPMuxError CreateVP8XChunk(WebPMux* const mux) { flags |= XMP_FLAG; } if (images->header_ != NULL) { - if (images->header_->tag_ == kChunks[IDX_FRGM].tag) { - // This is a fragmented image. - flags |= FRAGMENTS_FLAG; - } else if (images->header_->tag_ == kChunks[IDX_ANMF].tag) { + if (images->header_->tag_ == kChunks[IDX_ANMF].tag) { // This is an image with animation. flags |= ANIMATION_FLAG; } @@ -540,7 +506,7 @@ static WebPMuxError CreateVP8XChunk(WebPMux* const mux) { flags |= ALPHA_FLAG; // Some images have an alpha channel. } - err = GetAdjustedCanvasSize(mux, flags, &width, &height); + err = GetAdjustedCanvasSize(mux, &width, &height); if (err != WEBP_MUX_OK) return err; if (width <= 0 || height <= 0) { @@ -580,31 +546,26 @@ static WebPMuxError CreateVP8XChunk(WebPMux* const mux) { // Cleans up 'mux' by removing any unnecessary chunks. static WebPMuxError MuxCleanup(WebPMux* const mux) { int num_frames; - int num_fragments; int num_anim_chunks; - // If we have an image with a single fragment or frame, and its rectangle - // covers the whole canvas, convert it to a non-animated non-fragmented image - // (to avoid writing FRGM/ANMF chunk unnecessarily). + // If we have an image with a single frame, and its rectangle + // covers the whole canvas, convert it to a non-animated image + // (to avoid writing ANMF chunk unnecessarily). WebPMuxError err = WebPMuxNumChunks(mux, kChunks[IDX_ANMF].id, &num_frames); if (err != WEBP_MUX_OK) return err; - err = WebPMuxNumChunks(mux, kChunks[IDX_FRGM].id, &num_fragments); - if (err != WEBP_MUX_OK) return err; - if (num_frames == 1 || num_fragments == 1) { - WebPMuxImage* frame_frag; - err = MuxImageGetNth((const WebPMuxImage**)&mux->images_, 1, &frame_frag); - assert(err == WEBP_MUX_OK); // We know that one frame/fragment does exist. - assert(frame_frag != NULL); - if (frame_frag->header_ != NULL && + if (num_frames == 1) { + WebPMuxImage* frame = NULL; + err = MuxImageGetNth((const WebPMuxImage**)&mux->images_, 1, &frame); + assert(err == WEBP_MUX_OK); // We know that one frame does exist. + assert(frame != NULL); + if (frame->header_ != NULL && ((mux->canvas_width_ == 0 && mux->canvas_height_ == 0) || - (frame_frag->width_ == mux->canvas_width_ && - frame_frag->height_ == mux->canvas_height_))) { - assert(frame_frag->header_->tag_ == kChunks[IDX_ANMF].tag || - frame_frag->header_->tag_ == kChunks[IDX_FRGM].tag); - ChunkDelete(frame_frag->header_); // Removes ANMF/FRGM chunk. - frame_frag->header_ = NULL; + (frame->width_ == mux->canvas_width_ && + frame->height_ == mux->canvas_height_))) { + assert(frame->header_->tag_ == kChunks[IDX_ANMF].tag); + ChunkDelete(frame->header_); // Removes ANMF chunk. + frame->header_ = NULL; num_frames = 0; - num_fragments = 0; } } // Remove ANIM chunk if this is a non-animated image. diff --git a/thirdparty/libwebp/mux/muxi.h b/thirdparty/libwebp/mux/muxi.h index b4865fe36f..e6606aa5d1 100644 --- a/thirdparty/libwebp/mux/muxi.h +++ b/thirdparty/libwebp/mux/muxi.h @@ -15,8 +15,8 @@ #define WEBP_MUX_MUXI_H_ #include -#include "../dec/vp8i.h" -#include "../dec/vp8li.h" +#include "../dec/vp8i_dec.h" +#include "../dec/vp8li_dec.h" #include "../webp/mux.h" #ifdef __cplusplus @@ -27,8 +27,8 @@ extern "C" { // Defines and constants. #define MUX_MAJ_VERSION 0 -#define MUX_MIN_VERSION 3 -#define MUX_REV_VERSION 2 +#define MUX_MIN_VERSION 4 +#define MUX_REV_VERSION 0 // Chunk object. typedef struct WebPChunk WebPChunk; @@ -36,16 +36,16 @@ struct WebPChunk { uint32_t tag_; int owner_; // True if *data_ memory is owned internally. // VP8X, ANIM, and other internally created chunks - // like ANMF/FRGM are always owned. + // like ANMF are always owned. WebPData data_; WebPChunk* next_; }; -// MuxImage object. Store a full WebP image (including ANMF/FRGM chunk, ALPH +// MuxImage object. Store a full WebP image (including ANMF chunk, ALPH // chunk and VP8/VP8L chunk), typedef struct WebPMuxImage WebPMuxImage; struct WebPMuxImage { - WebPChunk* header_; // Corresponds to WEBP_CHUNK_ANMF/WEBP_CHUNK_FRGM. + WebPChunk* header_; // Corresponds to WEBP_CHUNK_ANMF. WebPChunk* alpha_; // Corresponds to WEBP_CHUNK_ALPHA. WebPChunk* img_; // Corresponds to WEBP_CHUNK_IMAGE. WebPChunk* unknown_; // Corresponds to WEBP_CHUNK_UNKNOWN. @@ -79,7 +79,6 @@ typedef enum { IDX_ICCP, IDX_ANIM, IDX_ANMF, - IDX_FRGM, IDX_ALPHA, IDX_VP8, IDX_VP8L, @@ -185,7 +184,6 @@ int MuxImageFinalize(WebPMuxImage* const wpi); static WEBP_INLINE int IsWPI(WebPChunkId id) { switch (id) { case WEBP_CHUNK_ANMF: - case WEBP_CHUNK_FRGM: case WEBP_CHUNK_ALPHA: case WEBP_CHUNK_IMAGE: return 1; default: return 0; diff --git a/thirdparty/libwebp/mux/muxinternal.c b/thirdparty/libwebp/mux/muxinternal.c index 372c6a9674..387b57e8fe 100644 --- a/thirdparty/libwebp/mux/muxinternal.c +++ b/thirdparty/libwebp/mux/muxinternal.c @@ -23,7 +23,6 @@ const ChunkInfo kChunks[] = { { MKFOURCC('I', 'C', 'C', 'P'), WEBP_CHUNK_ICCP, UNDEFINED_CHUNK_SIZE }, { MKFOURCC('A', 'N', 'I', 'M'), WEBP_CHUNK_ANIM, ANIM_CHUNK_SIZE }, { MKFOURCC('A', 'N', 'M', 'F'), WEBP_CHUNK_ANMF, ANMF_CHUNK_SIZE }, - { MKFOURCC('F', 'R', 'G', 'M'), WEBP_CHUNK_FRGM, FRGM_CHUNK_SIZE }, { MKFOURCC('A', 'L', 'P', 'H'), WEBP_CHUNK_ALPHA, UNDEFINED_CHUNK_SIZE }, { MKFOURCC('V', 'P', '8', ' '), WEBP_CHUNK_IMAGE, UNDEFINED_CHUNK_SIZE }, { MKFOURCC('V', 'P', '8', 'L'), WEBP_CHUNK_IMAGE, UNDEFINED_CHUNK_SIZE }, @@ -251,8 +250,7 @@ static WebPChunk** GetChunkListFromId(const WebPMuxImage* const wpi, WebPChunkId id) { assert(wpi != NULL); switch (id) { - case WEBP_CHUNK_ANMF: - case WEBP_CHUNK_FRGM: return (WebPChunk**)&wpi->header_; + case WEBP_CHUNK_ANMF: return (WebPChunk**)&wpi->header_; case WEBP_CHUNK_ALPHA: return (WebPChunk**)&wpi->alpha_; case WEBP_CHUNK_IMAGE: return (WebPChunk**)&wpi->img_; default: return NULL; @@ -372,13 +370,12 @@ size_t MuxImageDiskSize(const WebPMuxImage* const wpi) { return size; } -// Special case as ANMF/FRGM chunk encapsulates other image chunks. +// Special case as ANMF chunk encapsulates other image chunks. static uint8_t* ChunkEmitSpecial(const WebPChunk* const header, size_t total_size, uint8_t* dst) { const size_t header_size = header->data_.size; const size_t offset_to_next = total_size - CHUNK_HEADER_SIZE; - assert(header->tag_ == kChunks[IDX_ANMF].tag || - header->tag_ == kChunks[IDX_FRGM].tag); + assert(header->tag_ == kChunks[IDX_ANMF].tag); PutLE32(dst + 0, header->tag_); PutLE32(dst + TAG_SIZE, (uint32_t)offset_to_next); assert(header_size == (uint32_t)header_size); @@ -391,7 +388,7 @@ static uint8_t* ChunkEmitSpecial(const WebPChunk* const header, uint8_t* MuxImageEmit(const WebPMuxImage* const wpi, uint8_t* dst) { // Ordering of chunks to be emitted is strictly as follows: - // 1. ANMF/FRGM chunk (if present). + // 1. ANMF chunk (if present). // 2. ALPH chunk (if present). // 3. VP8/VP8L chunk. assert(wpi); @@ -465,7 +462,6 @@ WebPMuxError MuxValidate(const WebPMux* const mux) { int num_xmp; int num_anim; int num_frames; - int num_fragments; int num_vp8x; int num_images; int num_alpha; @@ -510,10 +506,6 @@ WebPMuxError MuxValidate(const WebPMux* const mux) { } } - // Fragmentation: FRAGMENTS_FLAG and FRGM chunk(s) are consistent. - err = ValidateChunk(mux, IDX_FRGM, FRAGMENTS_FLAG, flags, -1, &num_fragments); - if (err != WEBP_MUX_OK) return err; - // Verify either VP8X chunk is present OR there is only one elem in // mux->images_. err = ValidateChunk(mux, IDX_VP8X, NO_FLAG, flags, 1, &num_vp8x); @@ -537,11 +529,6 @@ WebPMuxError MuxValidate(const WebPMux* const mux) { if (flags & ALPHA_FLAG) return WEBP_MUX_INVALID_ARGUMENT; } - // num_fragments & num_images are consistent. - if (num_fragments > 0 && num_images != num_fragments) { - return WEBP_MUX_INVALID_ARGUMENT; - } - return WEBP_MUX_OK; } diff --git a/thirdparty/libwebp/mux/muxread.c b/thirdparty/libwebp/mux/muxread.c index 8957a1e46e..410acd9119 100644 --- a/thirdparty/libwebp/mux/muxread.c +++ b/thirdparty/libwebp/mux/muxread.c @@ -104,17 +104,15 @@ static int MuxImageParse(const WebPChunk* const chunk, int copy_data, size_t subchunk_size; ChunkInit(&subchunk); - assert(chunk->tag_ == kChunks[IDX_ANMF].tag || - chunk->tag_ == kChunks[IDX_FRGM].tag); + assert(chunk->tag_ == kChunks[IDX_ANMF].tag); assert(!wpi->is_partial_); - // ANMF/FRGM. + // ANMF. { - const size_t hdr_size = (chunk->tag_ == kChunks[IDX_ANMF].tag) ? - ANMF_CHUNK_SIZE : FRGM_CHUNK_SIZE; + const size_t hdr_size = ANMF_CHUNK_SIZE; const WebPData temp = { bytes, hdr_size }; - // Each of ANMF and FRGM chunk contain a header at the beginning. So, its - // size should at least be 'hdr_size'. + // Each of ANMF chunk contain a header at the beginning. So, its size should + // be at least 'hdr_size'. if (size < hdr_size) goto Fail; ChunkAssignData(&subchunk, &temp, copy_data, chunk->tag_); } @@ -292,16 +290,15 @@ WebPMux* WebPMuxCreateInternal(const WebPData* bitstream, int copy_data, static WebPMuxError ValidateForSingleImage(const WebPMux* const mux) { const int num_images = MuxImageCount(mux->images_, WEBP_CHUNK_IMAGE); const int num_frames = MuxImageCount(mux->images_, WEBP_CHUNK_ANMF); - const int num_fragments = MuxImageCount(mux->images_, WEBP_CHUNK_FRGM); if (num_images == 0) { // No images in mux. return WEBP_MUX_NOT_FOUND; - } else if (num_images == 1 && num_frames == 0 && num_fragments == 0) { + } else if (num_images == 1 && num_frames == 0) { // Valid case (single image). return WEBP_MUX_OK; } else { - // Frame/Fragment case OR an invalid mux. + // Frame case OR an invalid mux. return WEBP_MUX_INVALID_ARGUMENT; } } @@ -379,7 +376,7 @@ static WebPMuxError SynthesizeBitstream(const WebPMuxImage* const wpi, const int need_vp8x = (wpi->alpha_ != NULL); const size_t vp8x_size = need_vp8x ? CHUNK_HEADER_SIZE + VP8X_CHUNK_SIZE : 0; const size_t alpha_size = need_vp8x ? ChunkDiskSize(wpi->alpha_) : 0; - // Note: No need to output ANMF/FRGM chunk for a single image. + // Note: No need to output ANMF chunk for a single image. const size_t size = RIFF_HEADER_SIZE + vp8x_size + alpha_size + ChunkDiskSize(wpi->img_); uint8_t* const data = (uint8_t*)WebPSafeMalloc(1ULL, size); @@ -436,29 +433,24 @@ static WebPMuxError MuxGetImageInternal(const WebPMuxImage* const wpi, return SynthesizeBitstream(wpi, &info->bitstream); } -static WebPMuxError MuxGetFrameFragmentInternal(const WebPMuxImage* const wpi, - WebPMuxFrameInfo* const frame) { +static WebPMuxError MuxGetFrameInternal(const WebPMuxImage* const wpi, + WebPMuxFrameInfo* const frame) { const int is_frame = (wpi->header_->tag_ == kChunks[IDX_ANMF].tag); - const CHUNK_INDEX idx = is_frame ? IDX_ANMF : IDX_FRGM; - const WebPData* frame_frgm_data; + const WebPData* frame_data; if (!is_frame) return WEBP_MUX_INVALID_ARGUMENT; assert(wpi->header_ != NULL); // Already checked by WebPMuxGetFrame(). - // Get frame/fragment chunk. - frame_frgm_data = &wpi->header_->data_; - if (frame_frgm_data->size < kChunks[idx].size) return WEBP_MUX_BAD_DATA; + // Get frame chunk. + frame_data = &wpi->header_->data_; + if (frame_data->size < kChunks[IDX_ANMF].size) return WEBP_MUX_BAD_DATA; // Extract info. - frame->x_offset = 2 * GetLE24(frame_frgm_data->bytes + 0); - frame->y_offset = 2 * GetLE24(frame_frgm_data->bytes + 3); - if (is_frame) { - const uint8_t bits = frame_frgm_data->bytes[15]; - frame->duration = GetLE24(frame_frgm_data->bytes + 12); + frame->x_offset = 2 * GetLE24(frame_data->bytes + 0); + frame->y_offset = 2 * GetLE24(frame_data->bytes + 3); + { + const uint8_t bits = frame_data->bytes[15]; + frame->duration = GetLE24(frame_data->bytes + 12); frame->dispose_method = (bits & 1) ? WEBP_MUX_DISPOSE_BACKGROUND : WEBP_MUX_DISPOSE_NONE; frame->blend_method = (bits & 2) ? WEBP_MUX_NO_BLEND : WEBP_MUX_BLEND; - } else { // Defaults for unused values. - frame->duration = 1; - frame->dispose_method = WEBP_MUX_DISPOSE_NONE; - frame->blend_method = WEBP_MUX_BLEND; } frame->id = ChunkGetIdFromTag(wpi->header_->tag_); return SynthesizeBitstream(wpi, &frame->bitstream); @@ -482,7 +474,7 @@ WebPMuxError WebPMuxGetFrame( if (wpi->header_ == NULL) { return MuxGetImageInternal(wpi, frame); } else { - return MuxGetFrameFragmentInternal(wpi, frame); + return MuxGetFrameInternal(wpi, frame); } } diff --git a/thirdparty/libwebp/utils/bit_reader.c b/thirdparty/libwebp/utils/bit_reader.c deleted file mode 100644 index 2eb46e0b4b..0000000000 --- a/thirdparty/libwebp/utils/bit_reader.c +++ /dev/null @@ -1,229 +0,0 @@ -// Copyright 2010 Google Inc. All Rights Reserved. -// -// Use of this source code is governed by a BSD-style license -// that can be found in the COPYING file in the root of the source -// tree. An additional intellectual property rights grant can be found -// in the file PATENTS. All contributing project authors may -// be found in the AUTHORS file in the root of the source tree. -// ----------------------------------------------------------------------------- -// -// Boolean decoder non-inlined methods -// -// Author: Skal (pascal.massimino@gmail.com) - -#ifdef HAVE_CONFIG_H -#include "../webp/config.h" -#endif - -#include "./bit_reader_inl.h" -#include "../utils/utils.h" - -//------------------------------------------------------------------------------ -// VP8BitReader - -void VP8BitReaderSetBuffer(VP8BitReader* const br, - const uint8_t* const start, - size_t size) { - br->buf_ = start; - br->buf_end_ = start + size; - br->buf_max_ = - (size >= sizeof(lbit_t)) ? start + size - sizeof(lbit_t) + 1 - : start; -} - -void VP8InitBitReader(VP8BitReader* const br, - const uint8_t* const start, size_t size) { - assert(br != NULL); - assert(start != NULL); - assert(size < (1u << 31)); // limit ensured by format and upstream checks - br->range_ = 255 - 1; - br->value_ = 0; - br->bits_ = -8; // to load the very first 8bits - br->eof_ = 0; - VP8BitReaderSetBuffer(br, start, size); -// -- GODOT -- begin -#ifdef JAVASCRIPT_ENABLED // html5 required aligned reads - while(((uintptr_t)br->buf_ & 1) != 0 && !br->eof_) - VP8LoadFinalBytes(br); -#else - VP8LoadNewBytes(br); -#endif -// -- GODOT -- end -} - -void VP8RemapBitReader(VP8BitReader* const br, ptrdiff_t offset) { - if (br->buf_ != NULL) { - br->buf_ += offset; - br->buf_end_ += offset; - br->buf_max_ += offset; - } -} - -const uint8_t kVP8Log2Range[128] = { - 7, 6, 6, 5, 5, 5, 5, 4, 4, 4, 4, 4, 4, 4, 4, - 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, - 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, - 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 0 -}; - -// range = ((range - 1) << kVP8Log2Range[range]) + 1 -const uint8_t kVP8NewRange[128] = { - 127, 127, 191, 127, 159, 191, 223, 127, - 143, 159, 175, 191, 207, 223, 239, 127, - 135, 143, 151, 159, 167, 175, 183, 191, - 199, 207, 215, 223, 231, 239, 247, 127, - 131, 135, 139, 143, 147, 151, 155, 159, - 163, 167, 171, 175, 179, 183, 187, 191, - 195, 199, 203, 207, 211, 215, 219, 223, - 227, 231, 235, 239, 243, 247, 251, 127, - 129, 131, 133, 135, 137, 139, 141, 143, - 145, 147, 149, 151, 153, 155, 157, 159, - 161, 163, 165, 167, 169, 171, 173, 175, - 177, 179, 181, 183, 185, 187, 189, 191, - 193, 195, 197, 199, 201, 203, 205, 207, - 209, 211, 213, 215, 217, 219, 221, 223, - 225, 227, 229, 231, 233, 235, 237, 239, - 241, 243, 245, 247, 249, 251, 253, 127 -}; - -void VP8LoadFinalBytes(VP8BitReader* const br) { - assert(br != NULL && br->buf_ != NULL); - // Only read 8bits at a time - if (br->buf_ < br->buf_end_) { - br->bits_ += 8; - br->value_ = (bit_t)(*br->buf_++) | (br->value_ << 8); - } else if (!br->eof_) { - br->value_ <<= 8; - br->bits_ += 8; - br->eof_ = 1; - } else { - br->bits_ = 0; // This is to avoid undefined behaviour with shifts. - } -} - -//------------------------------------------------------------------------------ -// Higher-level calls - -uint32_t VP8GetValue(VP8BitReader* const br, int bits) { - uint32_t v = 0; - while (bits-- > 0) { - v |= VP8GetBit(br, 0x80) << bits; - } - return v; -} - -int32_t VP8GetSignedValue(VP8BitReader* const br, int bits) { - const int value = VP8GetValue(br, bits); - return VP8Get(br) ? -value : value; -} - -//------------------------------------------------------------------------------ -// VP8LBitReader - -#define VP8L_LOG8_WBITS 4 // Number of bytes needed to store VP8L_WBITS bits. - -#if defined(__arm__) || defined(_M_ARM) || defined(__aarch64__) || \ - defined(__i386__) || defined(_M_IX86) || \ - defined(__x86_64__) || defined(_M_X64) -#define VP8L_USE_FAST_LOAD -#endif - -static const uint32_t kBitMask[VP8L_MAX_NUM_BIT_READ + 1] = { - 0, - 0x000001, 0x000003, 0x000007, 0x00000f, - 0x00001f, 0x00003f, 0x00007f, 0x0000ff, - 0x0001ff, 0x0003ff, 0x0007ff, 0x000fff, - 0x001fff, 0x003fff, 0x007fff, 0x00ffff, - 0x01ffff, 0x03ffff, 0x07ffff, 0x0fffff, - 0x1fffff, 0x3fffff, 0x7fffff, 0xffffff -}; - -void VP8LInitBitReader(VP8LBitReader* const br, const uint8_t* const start, - size_t length) { - size_t i; - vp8l_val_t value = 0; - assert(br != NULL); - assert(start != NULL); - assert(length < 0xfffffff8u); // can't happen with a RIFF chunk. - - br->len_ = length; - br->val_ = 0; - br->bit_pos_ = 0; - br->eos_ = 0; - - if (length > sizeof(br->val_)) { - length = sizeof(br->val_); - } - for (i = 0; i < length; ++i) { - value |= (vp8l_val_t)start[i] << (8 * i); - } - br->val_ = value; - br->pos_ = length; - br->buf_ = start; -} - -void VP8LBitReaderSetBuffer(VP8LBitReader* const br, - const uint8_t* const buf, size_t len) { - assert(br != NULL); - assert(buf != NULL); - assert(len < 0xfffffff8u); // can't happen with a RIFF chunk. - br->buf_ = buf; - br->len_ = len; - // pos_ > len_ should be considered a param error. - br->eos_ = (br->pos_ > br->len_) || VP8LIsEndOfStream(br); -} - -static void VP8LSetEndOfStream(VP8LBitReader* const br) { - br->eos_ = 1; - br->bit_pos_ = 0; // To avoid undefined behaviour with shifts. -} - -// If not at EOS, reload up to VP8L_LBITS byte-by-byte -static void ShiftBytes(VP8LBitReader* const br) { - while (br->bit_pos_ >= 8 && br->pos_ < br->len_) { - br->val_ >>= 8; - br->val_ |= ((vp8l_val_t)br->buf_[br->pos_]) << (VP8L_LBITS - 8); - ++br->pos_; - br->bit_pos_ -= 8; - } - if (VP8LIsEndOfStream(br)) { - VP8LSetEndOfStream(br); - } -} - -void VP8LDoFillBitWindow(VP8LBitReader* const br) { - assert(br->bit_pos_ >= VP8L_WBITS); -#if defined(VP8L_USE_FAST_LOAD) - if (br->pos_ + sizeof(br->val_) < br->len_) { - br->val_ >>= VP8L_WBITS; - br->bit_pos_ -= VP8L_WBITS; - br->val_ |= (vp8l_val_t)HToLE32(WebPMemToUint32(br->buf_ + br->pos_)) << - (VP8L_LBITS - VP8L_WBITS); - br->pos_ += VP8L_LOG8_WBITS; - return; - } -#endif - ShiftBytes(br); // Slow path. -} - -uint32_t VP8LReadBits(VP8LBitReader* const br, int n_bits) { - assert(n_bits >= 0); - // Flag an error if end_of_stream or n_bits is more than allowed limit. - if (!br->eos_ && n_bits <= VP8L_MAX_NUM_BIT_READ) { - const uint32_t val = VP8LPrefetchBits(br) & kBitMask[n_bits]; - const int new_bits = br->bit_pos_ + n_bits; - br->bit_pos_ = new_bits; - ShiftBytes(br); - return val; - } else { - VP8LSetEndOfStream(br); - return 0; - } -} - -//------------------------------------------------------------------------------ diff --git a/thirdparty/libwebp/utils/bit_reader.h b/thirdparty/libwebp/utils/bit_reader.h deleted file mode 100644 index ea5c584eb4..0000000000 --- a/thirdparty/libwebp/utils/bit_reader.h +++ /dev/null @@ -1,184 +0,0 @@ -// Copyright 2010 Google Inc. All Rights Reserved. -// -// Use of this source code is governed by a BSD-style license -// that can be found in the COPYING file in the root of the source -// tree. An additional intellectual property rights grant can be found -// in the file PATENTS. All contributing project authors may -// be found in the AUTHORS file in the root of the source tree. -// ----------------------------------------------------------------------------- -// -// Boolean decoder -// -// Author: Skal (pascal.massimino@gmail.com) -// Vikas Arora (vikaas.arora@gmail.com) - -#ifndef WEBP_UTILS_BIT_READER_H_ -#define WEBP_UTILS_BIT_READER_H_ - -#include -#ifdef _MSC_VER -#include // _byteswap_ulong -#endif -#include "../webp/types.h" - -#ifdef __cplusplus -extern "C" { -#endif - -// The Boolean decoder needs to maintain infinite precision on the value_ field. -// However, since range_ is only 8bit, we only need an active window of 8 bits -// for value_. Left bits (MSB) gets zeroed and shifted away when value_ falls -// below 128, range_ is updated, and fresh bits read from the bitstream are -// brought in as LSB. To avoid reading the fresh bits one by one (slow), we -// cache BITS of them ahead. The total of (BITS + 8) bits must fit into a -// natural register (with type bit_t). To fetch BITS bits from bitstream we -// use a type lbit_t. -// -// BITS can be any multiple of 8 from 8 to 56 (inclusive). -// Pick values that fit natural register size. - -// -- GODOT -- start -#ifdef JAVASCRIPT_ENABLED -#define BITS 16 -#else -// -- GODOT -- end - -#if defined(__i386__) || defined(_M_IX86) // x86 32bit -#define BITS 24 -#elif defined(__x86_64__) || defined(_M_X64) // x86 64bit -#define BITS 56 -#elif defined(__arm__) || defined(_M_ARM) // ARM -#define BITS 24 -#elif defined(__aarch64__) // ARM 64bit -#define BITS 56 -#elif defined(__mips__) // MIPS -#define BITS 24 -#else // reasonable default -#define BITS 24 -#endif - -// -- GODOT -- start -#endif -// -- GODOT -- end - -//------------------------------------------------------------------------------ -// Derived types and constants: -// bit_t = natural register type for storing 'value_' (which is BITS+8 bits) -// range_t = register for 'range_' (which is 8bits only) - -#if (BITS > 24) -typedef uint64_t bit_t; -#else -typedef uint32_t bit_t; -#endif - -typedef uint32_t range_t; - -//------------------------------------------------------------------------------ -// Bitreader - -typedef struct VP8BitReader VP8BitReader; -struct VP8BitReader { - // boolean decoder (keep the field ordering as is!) - bit_t value_; // current value - range_t range_; // current range minus 1. In [127, 254] interval. - int bits_; // number of valid bits left - // read buffer - const uint8_t* buf_; // next byte to be read - const uint8_t* buf_end_; // end of read buffer - const uint8_t* buf_max_; // max packed-read position on buffer - int eof_; // true if input is exhausted -}; - -// Initialize the bit reader and the boolean decoder. -void VP8InitBitReader(VP8BitReader* const br, - const uint8_t* const start, size_t size); -// Sets the working read buffer. -void VP8BitReaderSetBuffer(VP8BitReader* const br, - const uint8_t* const start, size_t size); - -// Update internal pointers to displace the byte buffer by the -// relative offset 'offset'. -void VP8RemapBitReader(VP8BitReader* const br, ptrdiff_t offset); - -// return the next value made of 'num_bits' bits -uint32_t VP8GetValue(VP8BitReader* const br, int num_bits); -static WEBP_INLINE uint32_t VP8Get(VP8BitReader* const br) { - return VP8GetValue(br, 1); -} - -// return the next value with sign-extension. -int32_t VP8GetSignedValue(VP8BitReader* const br, int num_bits); - -// bit_reader_inl.h will implement the following methods: -// static WEBP_INLINE int VP8GetBit(VP8BitReader* const br, int prob) -// static WEBP_INLINE int VP8GetSigned(VP8BitReader* const br, int v) -// and should be included by the .c files that actually need them. -// This is to avoid recompiling the whole library whenever this file is touched, -// and also allowing platform-specific ad-hoc hacks. - -// ----------------------------------------------------------------------------- -// Bitreader for lossless format - -// maximum number of bits (inclusive) the bit-reader can handle: -#define VP8L_MAX_NUM_BIT_READ 24 - -#define VP8L_LBITS 64 // Number of bits prefetched (= bit-size of vp8l_val_t). -#define VP8L_WBITS 32 // Minimum number of bytes ready after VP8LFillBitWindow. - -typedef uint64_t vp8l_val_t; // right now, this bit-reader can only use 64bit. - -typedef struct { - vp8l_val_t val_; // pre-fetched bits - const uint8_t* buf_; // input byte buffer - size_t len_; // buffer length - size_t pos_; // byte position in buf_ - int bit_pos_; // current bit-reading position in val_ - int eos_; // true if a bit was read past the end of buffer -} VP8LBitReader; - -void VP8LInitBitReader(VP8LBitReader* const br, - const uint8_t* const start, - size_t length); - -// Sets a new data buffer. -void VP8LBitReaderSetBuffer(VP8LBitReader* const br, - const uint8_t* const buffer, size_t length); - -// Reads the specified number of bits from read buffer. -// Flags an error in case end_of_stream or n_bits is more than the allowed limit -// of VP8L_MAX_NUM_BIT_READ (inclusive). -// Flags eos_ if this read attempt is going to cross the read buffer. -uint32_t VP8LReadBits(VP8LBitReader* const br, int n_bits); - -// Return the prefetched bits, so they can be looked up. -static WEBP_INLINE uint32_t VP8LPrefetchBits(VP8LBitReader* const br) { - return (uint32_t)(br->val_ >> (br->bit_pos_ & (VP8L_LBITS - 1))); -} - -// Returns true if there was an attempt at reading bit past the end of -// the buffer. Doesn't set br->eos_ flag. -static WEBP_INLINE int VP8LIsEndOfStream(const VP8LBitReader* const br) { - assert(br->pos_ <= br->len_); - return br->eos_ || ((br->pos_ == br->len_) && (br->bit_pos_ > VP8L_LBITS)); -} - -// For jumping over a number of bits in the bit stream when accessed with -// VP8LPrefetchBits and VP8LFillBitWindow. -static WEBP_INLINE void VP8LSetBitPos(VP8LBitReader* const br, int val) { - br->bit_pos_ = val; - br->eos_ = VP8LIsEndOfStream(br); -} - -// Advances the read buffer by 4 bytes to make room for reading next 32 bits. -// Speed critical, but infrequent part of the code can be non-inlined. -extern void VP8LDoFillBitWindow(VP8LBitReader* const br); -static WEBP_INLINE void VP8LFillBitWindow(VP8LBitReader* const br) { - if (br->bit_pos_ >= VP8L_WBITS) VP8LDoFillBitWindow(br); -} - -#ifdef __cplusplus -} // extern "C" -#endif - -#endif /* WEBP_UTILS_BIT_READER_H_ */ diff --git a/thirdparty/libwebp/utils/bit_reader_inl.h b/thirdparty/libwebp/utils/bit_reader_inl.h deleted file mode 100644 index 99ed3137d2..0000000000 --- a/thirdparty/libwebp/utils/bit_reader_inl.h +++ /dev/null @@ -1,173 +0,0 @@ -// Copyright 2014 Google Inc. All Rights Reserved. -// -// Use of this source code is governed by a BSD-style license -// that can be found in the COPYING file in the root of the source -// tree. An additional intellectual property rights grant can be found -// in the file PATENTS. All contributing project authors may -// be found in the AUTHORS file in the root of the source tree. -// ----------------------------------------------------------------------------- -// -// Specific inlined methods for boolean decoder [VP8GetBit() ...] -// This file should be included by the .c sources that actually need to call -// these methods. -// -// Author: Skal (pascal.massimino@gmail.com) - -#ifndef WEBP_UTILS_BIT_READER_INL_H_ -#define WEBP_UTILS_BIT_READER_INL_H_ - -#ifdef HAVE_CONFIG_H -#include "../webp/config.h" -#endif - -#ifdef WEBP_FORCE_ALIGNED -#include // memcpy -#endif - -#include "../dsp/dsp.h" -#include "./bit_reader.h" -#include "./endian_inl.h" - -#ifdef __cplusplus -extern "C" { -#endif - -//------------------------------------------------------------------------------ -// Derived type lbit_t = natural type for memory I/O - -#if (BITS > 32) -typedef uint64_t lbit_t; -#elif (BITS > 16) -typedef uint32_t lbit_t; -#elif (BITS > 8) -typedef uint16_t lbit_t; -#else -typedef uint8_t lbit_t; -#endif - -extern const uint8_t kVP8Log2Range[128]; -extern const uint8_t kVP8NewRange[128]; - -// special case for the tail byte-reading -void VP8LoadFinalBytes(VP8BitReader* const br); - -//------------------------------------------------------------------------------ -// Inlined critical functions - -// makes sure br->value_ has at least BITS bits worth of data -static WEBP_UBSAN_IGNORE_UNDEF WEBP_INLINE -void VP8LoadNewBytes(VP8BitReader* const br) { - assert(br != NULL && br->buf_ != NULL); - // Read 'BITS' bits at a time if possible. - if (br->buf_ < br->buf_max_) { - // convert memory type to register type (with some zero'ing!) - bit_t bits; -#if defined(WEBP_FORCE_ALIGNED) - lbit_t in_bits; - memcpy(&in_bits, br->buf_, sizeof(in_bits)); -#elif defined(WEBP_USE_MIPS32) - // This is needed because of un-aligned read. - lbit_t in_bits; - lbit_t* p_buf_ = (lbit_t*)br->buf_; - __asm__ volatile( - ".set push \n\t" - ".set at \n\t" - ".set macro \n\t" - "ulw %[in_bits], 0(%[p_buf_]) \n\t" - ".set pop \n\t" - : [in_bits]"=r"(in_bits) - : [p_buf_]"r"(p_buf_) - : "memory", "at" - ); -#else - const lbit_t in_bits = *(const lbit_t*)br->buf_; -#endif - br->buf_ += BITS >> 3; -#if !defined(WORDS_BIGENDIAN) -#if (BITS > 32) - bits = BSwap64(in_bits); - bits >>= 64 - BITS; -#elif (BITS >= 24) - bits = BSwap32(in_bits); - bits >>= (32 - BITS); -#elif (BITS == 16) - bits = BSwap16(in_bits); -#else // BITS == 8 - bits = (bit_t)in_bits; -#endif // BITS > 32 -#else // WORDS_BIGENDIAN - bits = (bit_t)in_bits; - if (BITS != 8 * sizeof(bit_t)) bits >>= (8 * sizeof(bit_t) - BITS); -#endif - br->value_ = bits | (br->value_ << BITS); - br->bits_ += BITS; - } else { - VP8LoadFinalBytes(br); // no need to be inlined - } -} - -// Read a bit with proba 'prob'. Speed-critical function! -static WEBP_INLINE int VP8GetBit(VP8BitReader* const br, int prob) { - // Don't move this declaration! It makes a big speed difference to store - // 'range' *before* calling VP8LoadNewBytes(), even if this function doesn't - // alter br->range_ value. - range_t range = br->range_; - if (br->bits_ < 0) { - VP8LoadNewBytes(br); - } - { - const int pos = br->bits_; - const range_t split = (range * prob) >> 8; - const range_t value = (range_t)(br->value_ >> pos); -#if defined(__arm__) || defined(_M_ARM) // ARM-specific - const int bit = ((int)(split - value) >> 31) & 1; - if (value > split) { - range -= split + 1; - br->value_ -= (bit_t)(split + 1) << pos; - } else { - range = split; - } -#else // faster version on x86 - int bit; // Don't use 'const int bit = (value > split);", it's slower. - if (value > split) { - range -= split + 1; - br->value_ -= (bit_t)(split + 1) << pos; - bit = 1; - } else { - range = split; - bit = 0; - } -#endif - if (range <= (range_t)0x7e) { - const int shift = kVP8Log2Range[range]; - range = kVP8NewRange[range]; - br->bits_ -= shift; - } - br->range_ = range; - return bit; - } -} - -// simplified version of VP8GetBit() for prob=0x80 (note shift is always 1 here) -static WEBP_INLINE int VP8GetSigned(VP8BitReader* const br, int v) { - if (br->bits_ < 0) { - VP8LoadNewBytes(br); - } - { - const int pos = br->bits_; - const range_t split = br->range_ >> 1; - const range_t value = (range_t)(br->value_ >> pos); - const int32_t mask = (int32_t)(split - value) >> 31; // -1 or 0 - br->bits_ -= 1; - br->range_ += mask; - br->range_ |= 1; - br->value_ -= (bit_t)((split + 1) & mask) << pos; - return (v ^ mask) - mask; - } -} - -#ifdef __cplusplus -} // extern "C" -#endif - -#endif // WEBP_UTILS_BIT_READER_INL_H_ diff --git a/thirdparty/libwebp/utils/bit_reader_inl_utils.h b/thirdparty/libwebp/utils/bit_reader_inl_utils.h new file mode 100644 index 0000000000..fd7fb0446c --- /dev/null +++ b/thirdparty/libwebp/utils/bit_reader_inl_utils.h @@ -0,0 +1,190 @@ +// Copyright 2014 Google Inc. All Rights Reserved. +// +// Use of this source code is governed by a BSD-style license +// that can be found in the COPYING file in the root of the source +// tree. An additional intellectual property rights grant can be found +// in the file PATENTS. All contributing project authors may +// be found in the AUTHORS file in the root of the source tree. +// ----------------------------------------------------------------------------- +// +// Specific inlined methods for boolean decoder [VP8GetBit() ...] +// This file should be included by the .c sources that actually need to call +// these methods. +// +// Author: Skal (pascal.massimino@gmail.com) + +#ifndef WEBP_UTILS_BIT_READER_INL_H_ +#define WEBP_UTILS_BIT_READER_INL_H_ + +#ifdef HAVE_CONFIG_H +#include "../webp/config.h" +#endif + +#include // for memcpy + +#include "../dsp/dsp.h" +#include "./bit_reader_utils.h" +#include "./endian_inl_utils.h" +#include "./utils.h" + +#ifdef __cplusplus +extern "C" { +#endif + +//------------------------------------------------------------------------------ +// Derived type lbit_t = natural type for memory I/O + +#if (BITS > 32) +typedef uint64_t lbit_t; +#elif (BITS > 16) +typedef uint32_t lbit_t; +#elif (BITS > 8) +typedef uint16_t lbit_t; +#else +typedef uint8_t lbit_t; +#endif + +extern const uint8_t kVP8Log2Range[128]; +extern const uint8_t kVP8NewRange[128]; + +// special case for the tail byte-reading +void VP8LoadFinalBytes(VP8BitReader* const br); + +//------------------------------------------------------------------------------ +// Inlined critical functions + +// makes sure br->value_ has at least BITS bits worth of data +static WEBP_UBSAN_IGNORE_UNDEF WEBP_INLINE +void VP8LoadNewBytes(VP8BitReader* const br) { + assert(br != NULL && br->buf_ != NULL); + // Read 'BITS' bits at a time if possible. + if (br->buf_ < br->buf_max_) { + // convert memory type to register type (with some zero'ing!) + bit_t bits; +#if defined(WEBP_USE_MIPS32) + // This is needed because of un-aligned read. + lbit_t in_bits; + lbit_t* p_buf_ = (lbit_t*)br->buf_; + __asm__ volatile( + ".set push \n\t" + ".set at \n\t" + ".set macro \n\t" + "ulw %[in_bits], 0(%[p_buf_]) \n\t" + ".set pop \n\t" + : [in_bits]"=r"(in_bits) + : [p_buf_]"r"(p_buf_) + : "memory", "at" + ); +#else + lbit_t in_bits; + memcpy(&in_bits, br->buf_, sizeof(in_bits)); +#endif + br->buf_ += BITS >> 3; +#if !defined(WORDS_BIGENDIAN) +#if (BITS > 32) + bits = BSwap64(in_bits); + bits >>= 64 - BITS; +#elif (BITS >= 24) + bits = BSwap32(in_bits); + bits >>= (32 - BITS); +#elif (BITS == 16) + bits = BSwap16(in_bits); +#else // BITS == 8 + bits = (bit_t)in_bits; +#endif // BITS > 32 +#else // WORDS_BIGENDIAN + bits = (bit_t)in_bits; + if (BITS != 8 * sizeof(bit_t)) bits >>= (8 * sizeof(bit_t) - BITS); +#endif + br->value_ = bits | (br->value_ << BITS); + br->bits_ += BITS; + } else { + VP8LoadFinalBytes(br); // no need to be inlined + } +} + +// Read a bit with proba 'prob'. Speed-critical function! +static WEBP_INLINE int VP8GetBit(VP8BitReader* const br, int prob) { + // Don't move this declaration! It makes a big speed difference to store + // 'range' *before* calling VP8LoadNewBytes(), even if this function doesn't + // alter br->range_ value. + range_t range = br->range_; + if (br->bits_ < 0) { + VP8LoadNewBytes(br); + } + { + const int pos = br->bits_; + const range_t split = (range * prob) >> 8; + const range_t value = (range_t)(br->value_ >> pos); + const int bit = (value > split); + if (bit) { + range -= split; + br->value_ -= (bit_t)(split + 1) << pos; + } else { + range = split + 1; + } + { + const int shift = 7 ^ BitsLog2Floor(range); + range <<= shift; + br->bits_ -= shift; + } + br->range_ = range - 1; + return bit; + } +} + +// simplified version of VP8GetBit() for prob=0x80 (note shift is always 1 here) +static WEBP_UBSAN_IGNORE_UNSIGNED_OVERFLOW WEBP_INLINE +int VP8GetSigned(VP8BitReader* const br, int v) { + if (br->bits_ < 0) { + VP8LoadNewBytes(br); + } + { + const int pos = br->bits_; + const range_t split = br->range_ >> 1; + const range_t value = (range_t)(br->value_ >> pos); + const int32_t mask = (int32_t)(split - value) >> 31; // -1 or 0 + br->bits_ -= 1; + br->range_ += mask; + br->range_ |= 1; + br->value_ -= (bit_t)((split + 1) & mask) << pos; + return (v ^ mask) - mask; + } +} + +static WEBP_INLINE int VP8GetBitAlt(VP8BitReader* const br, int prob) { + // Don't move this declaration! It makes a big speed difference to store + // 'range' *before* calling VP8LoadNewBytes(), even if this function doesn't + // alter br->range_ value. + range_t range = br->range_; + if (br->bits_ < 0) { + VP8LoadNewBytes(br); + } + { + const int pos = br->bits_; + const range_t split = (range * prob) >> 8; + const range_t value = (range_t)(br->value_ >> pos); + int bit; // Don't use 'const int bit = (value > split);", it's slower. + if (value > split) { + range -= split + 1; + br->value_ -= (bit_t)(split + 1) << pos; + bit = 1; + } else { + range = split; + bit = 0; + } + if (range <= (range_t)0x7e) { + const int shift = kVP8Log2Range[range]; + range = kVP8NewRange[range]; + br->bits_ -= shift; + } + br->range_ = range; + return bit; + } +} + +#ifdef __cplusplus +} // extern "C" +#endif + +#endif // WEBP_UTILS_BIT_READER_INL_H_ diff --git a/thirdparty/libwebp/utils/bit_reader_utils.c b/thirdparty/libwebp/utils/bit_reader_utils.c new file mode 100644 index 0000000000..053b710bb8 --- /dev/null +++ b/thirdparty/libwebp/utils/bit_reader_utils.c @@ -0,0 +1,229 @@ +// Copyright 2010 Google Inc. All Rights Reserved. +// +// Use of this source code is governed by a BSD-style license +// that can be found in the COPYING file in the root of the source +// tree. An additional intellectual property rights grant can be found +// in the file PATENTS. All contributing project authors may +// be found in the AUTHORS file in the root of the source tree. +// ----------------------------------------------------------------------------- +// +// Boolean decoder non-inlined methods +// +// Author: Skal (pascal.massimino@gmail.com) + +#ifdef HAVE_CONFIG_H +#include "../webp/config.h" +#endif + +#include "./bit_reader_inl_utils.h" +#include "../utils/utils.h" + +//------------------------------------------------------------------------------ +// VP8BitReader + +void VP8BitReaderSetBuffer(VP8BitReader* const br, + const uint8_t* const start, + size_t size) { + br->buf_ = start; + br->buf_end_ = start + size; + br->buf_max_ = + (size >= sizeof(lbit_t)) ? start + size - sizeof(lbit_t) + 1 + : start; +} + +void VP8InitBitReader(VP8BitReader* const br, + const uint8_t* const start, size_t size) { + assert(br != NULL); + assert(start != NULL); + assert(size < (1u << 31)); // limit ensured by format and upstream checks + br->range_ = 255 - 1; + br->value_ = 0; + br->bits_ = -8; // to load the very first 8bits + br->eof_ = 0; + VP8BitReaderSetBuffer(br, start, size); +// -- GODOT -- begin +#ifdef JAVASCRIPT_ENABLED // html5 required aligned reads + while(((uintptr_t)br->buf_ & 1) != 0 && !br->eof_) + VP8LoadFinalBytes(br); +#else + VP8LoadNewBytes(br); +#endif +// -- GODOT -- end +} + +void VP8RemapBitReader(VP8BitReader* const br, ptrdiff_t offset) { + if (br->buf_ != NULL) { + br->buf_ += offset; + br->buf_end_ += offset; + br->buf_max_ += offset; + } +} + +const uint8_t kVP8Log2Range[128] = { + 7, 6, 6, 5, 5, 5, 5, 4, 4, 4, 4, 4, 4, 4, 4, + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 0 +}; + +// range = ((range - 1) << kVP8Log2Range[range]) + 1 +const uint8_t kVP8NewRange[128] = { + 127, 127, 191, 127, 159, 191, 223, 127, + 143, 159, 175, 191, 207, 223, 239, 127, + 135, 143, 151, 159, 167, 175, 183, 191, + 199, 207, 215, 223, 231, 239, 247, 127, + 131, 135, 139, 143, 147, 151, 155, 159, + 163, 167, 171, 175, 179, 183, 187, 191, + 195, 199, 203, 207, 211, 215, 219, 223, + 227, 231, 235, 239, 243, 247, 251, 127, + 129, 131, 133, 135, 137, 139, 141, 143, + 145, 147, 149, 151, 153, 155, 157, 159, + 161, 163, 165, 167, 169, 171, 173, 175, + 177, 179, 181, 183, 185, 187, 189, 191, + 193, 195, 197, 199, 201, 203, 205, 207, + 209, 211, 213, 215, 217, 219, 221, 223, + 225, 227, 229, 231, 233, 235, 237, 239, + 241, 243, 245, 247, 249, 251, 253, 127 +}; + +void VP8LoadFinalBytes(VP8BitReader* const br) { + assert(br != NULL && br->buf_ != NULL); + // Only read 8bits at a time + if (br->buf_ < br->buf_end_) { + br->bits_ += 8; + br->value_ = (bit_t)(*br->buf_++) | (br->value_ << 8); + } else if (!br->eof_) { + br->value_ <<= 8; + br->bits_ += 8; + br->eof_ = 1; + } else { + br->bits_ = 0; // This is to avoid undefined behaviour with shifts. + } +} + +//------------------------------------------------------------------------------ +// Higher-level calls + +uint32_t VP8GetValue(VP8BitReader* const br, int bits) { + uint32_t v = 0; + while (bits-- > 0) { + v |= VP8GetBit(br, 0x80) << bits; + } + return v; +} + +int32_t VP8GetSignedValue(VP8BitReader* const br, int bits) { + const int value = VP8GetValue(br, bits); + return VP8Get(br) ? -value : value; +} + +//------------------------------------------------------------------------------ +// VP8LBitReader + +#define VP8L_LOG8_WBITS 4 // Number of bytes needed to store VP8L_WBITS bits. + +#if defined(__arm__) || defined(_M_ARM) || defined(__aarch64__) || \ + defined(__i386__) || defined(_M_IX86) || \ + defined(__x86_64__) || defined(_M_X64) +#define VP8L_USE_FAST_LOAD +#endif + +static const uint32_t kBitMask[VP8L_MAX_NUM_BIT_READ + 1] = { + 0, + 0x000001, 0x000003, 0x000007, 0x00000f, + 0x00001f, 0x00003f, 0x00007f, 0x0000ff, + 0x0001ff, 0x0003ff, 0x0007ff, 0x000fff, + 0x001fff, 0x003fff, 0x007fff, 0x00ffff, + 0x01ffff, 0x03ffff, 0x07ffff, 0x0fffff, + 0x1fffff, 0x3fffff, 0x7fffff, 0xffffff +}; + +void VP8LInitBitReader(VP8LBitReader* const br, const uint8_t* const start, + size_t length) { + size_t i; + vp8l_val_t value = 0; + assert(br != NULL); + assert(start != NULL); + assert(length < 0xfffffff8u); // can't happen with a RIFF chunk. + + br->len_ = length; + br->val_ = 0; + br->bit_pos_ = 0; + br->eos_ = 0; + + if (length > sizeof(br->val_)) { + length = sizeof(br->val_); + } + for (i = 0; i < length; ++i) { + value |= (vp8l_val_t)start[i] << (8 * i); + } + br->val_ = value; + br->pos_ = length; + br->buf_ = start; +} + +void VP8LBitReaderSetBuffer(VP8LBitReader* const br, + const uint8_t* const buf, size_t len) { + assert(br != NULL); + assert(buf != NULL); + assert(len < 0xfffffff8u); // can't happen with a RIFF chunk. + br->buf_ = buf; + br->len_ = len; + // pos_ > len_ should be considered a param error. + br->eos_ = (br->pos_ > br->len_) || VP8LIsEndOfStream(br); +} + +static void VP8LSetEndOfStream(VP8LBitReader* const br) { + br->eos_ = 1; + br->bit_pos_ = 0; // To avoid undefined behaviour with shifts. +} + +// If not at EOS, reload up to VP8L_LBITS byte-by-byte +static void ShiftBytes(VP8LBitReader* const br) { + while (br->bit_pos_ >= 8 && br->pos_ < br->len_) { + br->val_ >>= 8; + br->val_ |= ((vp8l_val_t)br->buf_[br->pos_]) << (VP8L_LBITS - 8); + ++br->pos_; + br->bit_pos_ -= 8; + } + if (VP8LIsEndOfStream(br)) { + VP8LSetEndOfStream(br); + } +} + +void VP8LDoFillBitWindow(VP8LBitReader* const br) { + assert(br->bit_pos_ >= VP8L_WBITS); +#if defined(VP8L_USE_FAST_LOAD) + if (br->pos_ + sizeof(br->val_) < br->len_) { + br->val_ >>= VP8L_WBITS; + br->bit_pos_ -= VP8L_WBITS; + br->val_ |= (vp8l_val_t)HToLE32(WebPMemToUint32(br->buf_ + br->pos_)) << + (VP8L_LBITS - VP8L_WBITS); + br->pos_ += VP8L_LOG8_WBITS; + return; + } +#endif + ShiftBytes(br); // Slow path. +} + +uint32_t VP8LReadBits(VP8LBitReader* const br, int n_bits) { + assert(n_bits >= 0); + // Flag an error if end_of_stream or n_bits is more than allowed limit. + if (!br->eos_ && n_bits <= VP8L_MAX_NUM_BIT_READ) { + const uint32_t val = VP8LPrefetchBits(br) & kBitMask[n_bits]; + const int new_bits = br->bit_pos_ + n_bits; + br->bit_pos_ = new_bits; + ShiftBytes(br); + return val; + } else { + VP8LSetEndOfStream(br); + return 0; + } +} + +//------------------------------------------------------------------------------ diff --git a/thirdparty/libwebp/utils/bit_reader_utils.h b/thirdparty/libwebp/utils/bit_reader_utils.h new file mode 100644 index 0000000000..ea5c584eb4 --- /dev/null +++ b/thirdparty/libwebp/utils/bit_reader_utils.h @@ -0,0 +1,184 @@ +// Copyright 2010 Google Inc. All Rights Reserved. +// +// Use of this source code is governed by a BSD-style license +// that can be found in the COPYING file in the root of the source +// tree. An additional intellectual property rights grant can be found +// in the file PATENTS. All contributing project authors may +// be found in the AUTHORS file in the root of the source tree. +// ----------------------------------------------------------------------------- +// +// Boolean decoder +// +// Author: Skal (pascal.massimino@gmail.com) +// Vikas Arora (vikaas.arora@gmail.com) + +#ifndef WEBP_UTILS_BIT_READER_H_ +#define WEBP_UTILS_BIT_READER_H_ + +#include +#ifdef _MSC_VER +#include // _byteswap_ulong +#endif +#include "../webp/types.h" + +#ifdef __cplusplus +extern "C" { +#endif + +// The Boolean decoder needs to maintain infinite precision on the value_ field. +// However, since range_ is only 8bit, we only need an active window of 8 bits +// for value_. Left bits (MSB) gets zeroed and shifted away when value_ falls +// below 128, range_ is updated, and fresh bits read from the bitstream are +// brought in as LSB. To avoid reading the fresh bits one by one (slow), we +// cache BITS of them ahead. The total of (BITS + 8) bits must fit into a +// natural register (with type bit_t). To fetch BITS bits from bitstream we +// use a type lbit_t. +// +// BITS can be any multiple of 8 from 8 to 56 (inclusive). +// Pick values that fit natural register size. + +// -- GODOT -- start +#ifdef JAVASCRIPT_ENABLED +#define BITS 16 +#else +// -- GODOT -- end + +#if defined(__i386__) || defined(_M_IX86) // x86 32bit +#define BITS 24 +#elif defined(__x86_64__) || defined(_M_X64) // x86 64bit +#define BITS 56 +#elif defined(__arm__) || defined(_M_ARM) // ARM +#define BITS 24 +#elif defined(__aarch64__) // ARM 64bit +#define BITS 56 +#elif defined(__mips__) // MIPS +#define BITS 24 +#else // reasonable default +#define BITS 24 +#endif + +// -- GODOT -- start +#endif +// -- GODOT -- end + +//------------------------------------------------------------------------------ +// Derived types and constants: +// bit_t = natural register type for storing 'value_' (which is BITS+8 bits) +// range_t = register for 'range_' (which is 8bits only) + +#if (BITS > 24) +typedef uint64_t bit_t; +#else +typedef uint32_t bit_t; +#endif + +typedef uint32_t range_t; + +//------------------------------------------------------------------------------ +// Bitreader + +typedef struct VP8BitReader VP8BitReader; +struct VP8BitReader { + // boolean decoder (keep the field ordering as is!) + bit_t value_; // current value + range_t range_; // current range minus 1. In [127, 254] interval. + int bits_; // number of valid bits left + // read buffer + const uint8_t* buf_; // next byte to be read + const uint8_t* buf_end_; // end of read buffer + const uint8_t* buf_max_; // max packed-read position on buffer + int eof_; // true if input is exhausted +}; + +// Initialize the bit reader and the boolean decoder. +void VP8InitBitReader(VP8BitReader* const br, + const uint8_t* const start, size_t size); +// Sets the working read buffer. +void VP8BitReaderSetBuffer(VP8BitReader* const br, + const uint8_t* const start, size_t size); + +// Update internal pointers to displace the byte buffer by the +// relative offset 'offset'. +void VP8RemapBitReader(VP8BitReader* const br, ptrdiff_t offset); + +// return the next value made of 'num_bits' bits +uint32_t VP8GetValue(VP8BitReader* const br, int num_bits); +static WEBP_INLINE uint32_t VP8Get(VP8BitReader* const br) { + return VP8GetValue(br, 1); +} + +// return the next value with sign-extension. +int32_t VP8GetSignedValue(VP8BitReader* const br, int num_bits); + +// bit_reader_inl.h will implement the following methods: +// static WEBP_INLINE int VP8GetBit(VP8BitReader* const br, int prob) +// static WEBP_INLINE int VP8GetSigned(VP8BitReader* const br, int v) +// and should be included by the .c files that actually need them. +// This is to avoid recompiling the whole library whenever this file is touched, +// and also allowing platform-specific ad-hoc hacks. + +// ----------------------------------------------------------------------------- +// Bitreader for lossless format + +// maximum number of bits (inclusive) the bit-reader can handle: +#define VP8L_MAX_NUM_BIT_READ 24 + +#define VP8L_LBITS 64 // Number of bits prefetched (= bit-size of vp8l_val_t). +#define VP8L_WBITS 32 // Minimum number of bytes ready after VP8LFillBitWindow. + +typedef uint64_t vp8l_val_t; // right now, this bit-reader can only use 64bit. + +typedef struct { + vp8l_val_t val_; // pre-fetched bits + const uint8_t* buf_; // input byte buffer + size_t len_; // buffer length + size_t pos_; // byte position in buf_ + int bit_pos_; // current bit-reading position in val_ + int eos_; // true if a bit was read past the end of buffer +} VP8LBitReader; + +void VP8LInitBitReader(VP8LBitReader* const br, + const uint8_t* const start, + size_t length); + +// Sets a new data buffer. +void VP8LBitReaderSetBuffer(VP8LBitReader* const br, + const uint8_t* const buffer, size_t length); + +// Reads the specified number of bits from read buffer. +// Flags an error in case end_of_stream or n_bits is more than the allowed limit +// of VP8L_MAX_NUM_BIT_READ (inclusive). +// Flags eos_ if this read attempt is going to cross the read buffer. +uint32_t VP8LReadBits(VP8LBitReader* const br, int n_bits); + +// Return the prefetched bits, so they can be looked up. +static WEBP_INLINE uint32_t VP8LPrefetchBits(VP8LBitReader* const br) { + return (uint32_t)(br->val_ >> (br->bit_pos_ & (VP8L_LBITS - 1))); +} + +// Returns true if there was an attempt at reading bit past the end of +// the buffer. Doesn't set br->eos_ flag. +static WEBP_INLINE int VP8LIsEndOfStream(const VP8LBitReader* const br) { + assert(br->pos_ <= br->len_); + return br->eos_ || ((br->pos_ == br->len_) && (br->bit_pos_ > VP8L_LBITS)); +} + +// For jumping over a number of bits in the bit stream when accessed with +// VP8LPrefetchBits and VP8LFillBitWindow. +static WEBP_INLINE void VP8LSetBitPos(VP8LBitReader* const br, int val) { + br->bit_pos_ = val; + br->eos_ = VP8LIsEndOfStream(br); +} + +// Advances the read buffer by 4 bytes to make room for reading next 32 bits. +// Speed critical, but infrequent part of the code can be non-inlined. +extern void VP8LDoFillBitWindow(VP8LBitReader* const br); +static WEBP_INLINE void VP8LFillBitWindow(VP8LBitReader* const br) { + if (br->bit_pos_ >= VP8L_WBITS) VP8LDoFillBitWindow(br); +} + +#ifdef __cplusplus +} // extern "C" +#endif + +#endif /* WEBP_UTILS_BIT_READER_H_ */ diff --git a/thirdparty/libwebp/utils/bit_writer.c b/thirdparty/libwebp/utils/bit_writer.c deleted file mode 100644 index 064428691b..0000000000 --- a/thirdparty/libwebp/utils/bit_writer.c +++ /dev/null @@ -1,319 +0,0 @@ -// Copyright 2011 Google Inc. All Rights Reserved. -// -// Use of this source code is governed by a BSD-style license -// that can be found in the COPYING file in the root of the source -// tree. An additional intellectual property rights grant can be found -// in the file PATENTS. All contributing project authors may -// be found in the AUTHORS file in the root of the source tree. -// ----------------------------------------------------------------------------- -// -// Bit writing and boolean coder -// -// Author: Skal (pascal.massimino@gmail.com) -// Vikas Arora (vikaas.arora@gmail.com) - -#include -#include // for memcpy() -#include - -#include "./bit_writer.h" -#include "./endian_inl.h" -#include "./utils.h" - -//------------------------------------------------------------------------------ -// VP8BitWriter - -static int BitWriterResize(VP8BitWriter* const bw, size_t extra_size) { - uint8_t* new_buf; - size_t new_size; - const uint64_t needed_size_64b = (uint64_t)bw->pos_ + extra_size; - const size_t needed_size = (size_t)needed_size_64b; - if (needed_size_64b != needed_size) { - bw->error_ = 1; - return 0; - } - if (needed_size <= bw->max_pos_) return 1; - // If the following line wraps over 32bit, the test just after will catch it. - new_size = 2 * bw->max_pos_; - if (new_size < needed_size) new_size = needed_size; - if (new_size < 1024) new_size = 1024; - new_buf = (uint8_t*)WebPSafeMalloc(1ULL, new_size); - if (new_buf == NULL) { - bw->error_ = 1; - return 0; - } - if (bw->pos_ > 0) { - assert(bw->buf_ != NULL); - memcpy(new_buf, bw->buf_, bw->pos_); - } - WebPSafeFree(bw->buf_); - bw->buf_ = new_buf; - bw->max_pos_ = new_size; - return 1; -} - -static void Flush(VP8BitWriter* const bw) { - const int s = 8 + bw->nb_bits_; - const int32_t bits = bw->value_ >> s; - assert(bw->nb_bits_ >= 0); - bw->value_ -= bits << s; - bw->nb_bits_ -= 8; - if ((bits & 0xff) != 0xff) { - size_t pos = bw->pos_; - if (!BitWriterResize(bw, bw->run_ + 1)) { - return; - } - if (bits & 0x100) { // overflow -> propagate carry over pending 0xff's - if (pos > 0) bw->buf_[pos - 1]++; - } - if (bw->run_ > 0) { - const int value = (bits & 0x100) ? 0x00 : 0xff; - for (; bw->run_ > 0; --bw->run_) bw->buf_[pos++] = value; - } - bw->buf_[pos++] = bits; - bw->pos_ = pos; - } else { - bw->run_++; // delay writing of bytes 0xff, pending eventual carry. - } -} - -//------------------------------------------------------------------------------ -// renormalization - -static const uint8_t kNorm[128] = { // renorm_sizes[i] = 8 - log2(i) - 7, 6, 6, 5, 5, 5, 5, 4, 4, 4, 4, 4, 4, 4, 4, - 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, - 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, - 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 0 -}; - -// range = ((range + 1) << kVP8Log2Range[range]) - 1 -static const uint8_t kNewRange[128] = { - 127, 127, 191, 127, 159, 191, 223, 127, 143, 159, 175, 191, 207, 223, 239, - 127, 135, 143, 151, 159, 167, 175, 183, 191, 199, 207, 215, 223, 231, 239, - 247, 127, 131, 135, 139, 143, 147, 151, 155, 159, 163, 167, 171, 175, 179, - 183, 187, 191, 195, 199, 203, 207, 211, 215, 219, 223, 227, 231, 235, 239, - 243, 247, 251, 127, 129, 131, 133, 135, 137, 139, 141, 143, 145, 147, 149, - 151, 153, 155, 157, 159, 161, 163, 165, 167, 169, 171, 173, 175, 177, 179, - 181, 183, 185, 187, 189, 191, 193, 195, 197, 199, 201, 203, 205, 207, 209, - 211, 213, 215, 217, 219, 221, 223, 225, 227, 229, 231, 233, 235, 237, 239, - 241, 243, 245, 247, 249, 251, 253, 127 -}; - -int VP8PutBit(VP8BitWriter* const bw, int bit, int prob) { - const int split = (bw->range_ * prob) >> 8; - if (bit) { - bw->value_ += split + 1; - bw->range_ -= split + 1; - } else { - bw->range_ = split; - } - if (bw->range_ < 127) { // emit 'shift' bits out and renormalize - const int shift = kNorm[bw->range_]; - bw->range_ = kNewRange[bw->range_]; - bw->value_ <<= shift; - bw->nb_bits_ += shift; - if (bw->nb_bits_ > 0) Flush(bw); - } - return bit; -} - -int VP8PutBitUniform(VP8BitWriter* const bw, int bit) { - const int split = bw->range_ >> 1; - if (bit) { - bw->value_ += split + 1; - bw->range_ -= split + 1; - } else { - bw->range_ = split; - } - if (bw->range_ < 127) { - bw->range_ = kNewRange[bw->range_]; - bw->value_ <<= 1; - bw->nb_bits_ += 1; - if (bw->nb_bits_ > 0) Flush(bw); - } - return bit; -} - -void VP8PutBits(VP8BitWriter* const bw, uint32_t value, int nb_bits) { - uint32_t mask; - assert(nb_bits > 0 && nb_bits < 32); - for (mask = 1u << (nb_bits - 1); mask; mask >>= 1) - VP8PutBitUniform(bw, value & mask); -} - -void VP8PutSignedBits(VP8BitWriter* const bw, int value, int nb_bits) { - if (!VP8PutBitUniform(bw, value != 0)) - return; - if (value < 0) { - VP8PutBits(bw, ((-value) << 1) | 1, nb_bits + 1); - } else { - VP8PutBits(bw, value << 1, nb_bits + 1); - } -} - -//------------------------------------------------------------------------------ - -int VP8BitWriterInit(VP8BitWriter* const bw, size_t expected_size) { - bw->range_ = 255 - 1; - bw->value_ = 0; - bw->run_ = 0; - bw->nb_bits_ = -8; - bw->pos_ = 0; - bw->max_pos_ = 0; - bw->error_ = 0; - bw->buf_ = NULL; - return (expected_size > 0) ? BitWriterResize(bw, expected_size) : 1; -} - -uint8_t* VP8BitWriterFinish(VP8BitWriter* const bw) { - VP8PutBits(bw, 0, 9 - bw->nb_bits_); - bw->nb_bits_ = 0; // pad with zeroes - Flush(bw); - return bw->buf_; -} - -int VP8BitWriterAppend(VP8BitWriter* const bw, - const uint8_t* data, size_t size) { - assert(data != NULL); - if (bw->nb_bits_ != -8) return 0; // Flush() must have been called - if (!BitWriterResize(bw, size)) return 0; - memcpy(bw->buf_ + bw->pos_, data, size); - bw->pos_ += size; - return 1; -} - -void VP8BitWriterWipeOut(VP8BitWriter* const bw) { - if (bw != NULL) { - WebPSafeFree(bw->buf_); - memset(bw, 0, sizeof(*bw)); - } -} - -//------------------------------------------------------------------------------ -// VP8LBitWriter - -// This is the minimum amount of size the memory buffer is guaranteed to grow -// when extra space is needed. -#define MIN_EXTRA_SIZE (32768ULL) - -// Returns 1 on success. -static int VP8LBitWriterResize(VP8LBitWriter* const bw, size_t extra_size) { - uint8_t* allocated_buf; - size_t allocated_size; - const size_t max_bytes = bw->end_ - bw->buf_; - const size_t current_size = bw->cur_ - bw->buf_; - const uint64_t size_required_64b = (uint64_t)current_size + extra_size; - const size_t size_required = (size_t)size_required_64b; - if (size_required != size_required_64b) { - bw->error_ = 1; - return 0; - } - if (max_bytes > 0 && size_required <= max_bytes) return 1; - allocated_size = (3 * max_bytes) >> 1; - if (allocated_size < size_required) allocated_size = size_required; - // make allocated size multiple of 1k - allocated_size = (((allocated_size >> 10) + 1) << 10); - allocated_buf = (uint8_t*)WebPSafeMalloc(1ULL, allocated_size); - if (allocated_buf == NULL) { - bw->error_ = 1; - return 0; - } - if (current_size > 0) { - memcpy(allocated_buf, bw->buf_, current_size); - } - WebPSafeFree(bw->buf_); - bw->buf_ = allocated_buf; - bw->cur_ = bw->buf_ + current_size; - bw->end_ = bw->buf_ + allocated_size; - return 1; -} - -int VP8LBitWriterInit(VP8LBitWriter* const bw, size_t expected_size) { - memset(bw, 0, sizeof(*bw)); - return VP8LBitWriterResize(bw, expected_size); -} - -void VP8LBitWriterWipeOut(VP8LBitWriter* const bw) { - if (bw != NULL) { - WebPSafeFree(bw->buf_); - memset(bw, 0, sizeof(*bw)); - } -} - -void VP8LPutBitsFlushBits(VP8LBitWriter* const bw) { - // If needed, make some room by flushing some bits out. - if (bw->cur_ + VP8L_WRITER_BYTES > bw->end_) { - const uint64_t extra_size = (bw->end_ - bw->buf_) + MIN_EXTRA_SIZE; - if (extra_size != (size_t)extra_size || - !VP8LBitWriterResize(bw, (size_t)extra_size)) { - bw->cur_ = bw->buf_; - bw->error_ = 1; - return; - } - } - *(vp8l_wtype_t*)bw->cur_ = (vp8l_wtype_t)WSWAP((vp8l_wtype_t)bw->bits_); - bw->cur_ += VP8L_WRITER_BYTES; - bw->bits_ >>= VP8L_WRITER_BITS; - bw->used_ -= VP8L_WRITER_BITS; -} - -void VP8LPutBitsInternal(VP8LBitWriter* const bw, uint32_t bits, int n_bits) { - assert(n_bits <= 32); - // That's the max we can handle: - assert(sizeof(vp8l_wtype_t) == 2); - if (n_bits > 0) { - vp8l_atype_t lbits = bw->bits_; - int used = bw->used_; - // Special case of overflow handling for 32bit accumulator (2-steps flush). -#if VP8L_WRITER_BITS == 16 - if (used + n_bits >= VP8L_WRITER_MAX_BITS) { - // Fill up all the VP8L_WRITER_MAX_BITS so it can be flushed out below. - const int shift = VP8L_WRITER_MAX_BITS - used; - lbits |= (vp8l_atype_t)bits << used; - used = VP8L_WRITER_MAX_BITS; - n_bits -= shift; - bits >>= shift; - assert(n_bits <= VP8L_WRITER_MAX_BITS); - } -#endif - // If needed, make some room by flushing some bits out. - while (used >= VP8L_WRITER_BITS) { - if (bw->cur_ + VP8L_WRITER_BYTES > bw->end_) { - const uint64_t extra_size = (bw->end_ - bw->buf_) + MIN_EXTRA_SIZE; - if (extra_size != (size_t)extra_size || - !VP8LBitWriterResize(bw, (size_t)extra_size)) { - bw->cur_ = bw->buf_; - bw->error_ = 1; - return; - } - } - *(vp8l_wtype_t*)bw->cur_ = (vp8l_wtype_t)WSWAP((vp8l_wtype_t)lbits); - bw->cur_ += VP8L_WRITER_BYTES; - lbits >>= VP8L_WRITER_BITS; - used -= VP8L_WRITER_BITS; - } - bw->bits_ = lbits | ((vp8l_atype_t)bits << used); - bw->used_ = used + n_bits; - } -} - -uint8_t* VP8LBitWriterFinish(VP8LBitWriter* const bw) { - // flush leftover bits - if (VP8LBitWriterResize(bw, (bw->used_ + 7) >> 3)) { - while (bw->used_ > 0) { - *bw->cur_++ = (uint8_t)bw->bits_; - bw->bits_ >>= 8; - bw->used_ -= 8; - } - bw->used_ = 0; - } - return bw->buf_; -} - -//------------------------------------------------------------------------------ diff --git a/thirdparty/libwebp/utils/bit_writer.h b/thirdparty/libwebp/utils/bit_writer.h deleted file mode 100644 index ef360d1dc6..0000000000 --- a/thirdparty/libwebp/utils/bit_writer.h +++ /dev/null @@ -1,145 +0,0 @@ -// Copyright 2011 Google Inc. All Rights Reserved. -// -// Use of this source code is governed by a BSD-style license -// that can be found in the COPYING file in the root of the source -// tree. An additional intellectual property rights grant can be found -// in the file PATENTS. All contributing project authors may -// be found in the AUTHORS file in the root of the source tree. -// ----------------------------------------------------------------------------- -// -// Bit writing and boolean coder -// -// Author: Skal (pascal.massimino@gmail.com) - -#ifndef WEBP_UTILS_BIT_WRITER_H_ -#define WEBP_UTILS_BIT_WRITER_H_ - -#include "../webp/types.h" - -#ifdef __cplusplus -extern "C" { -#endif - -//------------------------------------------------------------------------------ -// Bit-writing - -typedef struct VP8BitWriter VP8BitWriter; -struct VP8BitWriter { - int32_t range_; // range-1 - int32_t value_; - int run_; // number of outstanding bits - int nb_bits_; // number of pending bits - uint8_t* buf_; // internal buffer. Re-allocated regularly. Not owned. - size_t pos_; - size_t max_pos_; - int error_; // true in case of error -}; - -// Initialize the object. Allocates some initial memory based on expected_size. -int VP8BitWriterInit(VP8BitWriter* const bw, size_t expected_size); -// Finalize the bitstream coding. Returns a pointer to the internal buffer. -uint8_t* VP8BitWriterFinish(VP8BitWriter* const bw); -// Release any pending memory and zeroes the object. Not a mandatory call. -// Only useful in case of error, when the internal buffer hasn't been grabbed! -void VP8BitWriterWipeOut(VP8BitWriter* const bw); - -int VP8PutBit(VP8BitWriter* const bw, int bit, int prob); -int VP8PutBitUniform(VP8BitWriter* const bw, int bit); -void VP8PutBits(VP8BitWriter* const bw, uint32_t value, int nb_bits); -void VP8PutSignedBits(VP8BitWriter* const bw, int value, int nb_bits); - -// Appends some bytes to the internal buffer. Data is copied. -int VP8BitWriterAppend(VP8BitWriter* const bw, - const uint8_t* data, size_t size); - -// return approximate write position (in bits) -static WEBP_INLINE uint64_t VP8BitWriterPos(const VP8BitWriter* const bw) { - return (uint64_t)(bw->pos_ + bw->run_) * 8 + 8 + bw->nb_bits_; -} - -// Returns a pointer to the internal buffer. -static WEBP_INLINE uint8_t* VP8BitWriterBuf(const VP8BitWriter* const bw) { - return bw->buf_; -} -// Returns the size of the internal buffer. -static WEBP_INLINE size_t VP8BitWriterSize(const VP8BitWriter* const bw) { - return bw->pos_; -} - -//------------------------------------------------------------------------------ -// VP8LBitWriter - -#if defined(__x86_64__) || defined(_M_X64) // 64bit -typedef uint64_t vp8l_atype_t; // accumulator type -typedef uint32_t vp8l_wtype_t; // writing type -#define WSWAP HToLE32 -#define VP8L_WRITER_BYTES 4 // sizeof(vp8l_wtype_t) -#define VP8L_WRITER_BITS 32 // 8 * sizeof(vp8l_wtype_t) -#define VP8L_WRITER_MAX_BITS 64 // 8 * sizeof(vp8l_atype_t) -#else -typedef uint32_t vp8l_atype_t; -typedef uint16_t vp8l_wtype_t; -#define WSWAP HToLE16 -#define VP8L_WRITER_BYTES 2 -#define VP8L_WRITER_BITS 16 -#define VP8L_WRITER_MAX_BITS 32 -#endif - -typedef struct { - vp8l_atype_t bits_; // bit accumulator - int used_; // number of bits used in accumulator - uint8_t* buf_; // start of buffer - uint8_t* cur_; // current write position - uint8_t* end_; // end of buffer - - // After all bits are written (VP8LBitWriterFinish()), the caller must observe - // the state of error_. A value of 1 indicates that a memory allocation - // failure has happened during bit writing. A value of 0 indicates successful - // writing of bits. - int error_; -} VP8LBitWriter; - -static WEBP_INLINE size_t VP8LBitWriterNumBytes(VP8LBitWriter* const bw) { - return (bw->cur_ - bw->buf_) + ((bw->used_ + 7) >> 3); -} - -// Returns false in case of memory allocation error. -int VP8LBitWriterInit(VP8LBitWriter* const bw, size_t expected_size); -// Finalize the bitstream coding. Returns a pointer to the internal buffer. -uint8_t* VP8LBitWriterFinish(VP8LBitWriter* const bw); -// Release any pending memory and zeroes the object. -void VP8LBitWriterWipeOut(VP8LBitWriter* const bw); - -// Internal function for VP8LPutBits flushing 32 bits from the written state. -void VP8LPutBitsFlushBits(VP8LBitWriter* const bw); - -// PutBits internal function used in the 16 bit vp8l_wtype_t case. -void VP8LPutBitsInternal(VP8LBitWriter* const bw, uint32_t bits, int n_bits); - -// This function writes bits into bytes in increasing addresses (little endian), -// and within a byte least-significant-bit first. -// This function can write up to 32 bits in one go, but VP8LBitReader can only -// read 24 bits max (VP8L_MAX_NUM_BIT_READ). -// VP8LBitWriter's error_ flag is set in case of memory allocation error. -static WEBP_INLINE void VP8LPutBits(VP8LBitWriter* const bw, - uint32_t bits, int n_bits) { - if (sizeof(vp8l_wtype_t) == 4) { - if (n_bits > 0) { - if (bw->used_ >= 32) { - VP8LPutBitsFlushBits(bw); - } - bw->bits_ |= (vp8l_atype_t)bits << bw->used_; - bw->used_ += n_bits; - } - } else { - VP8LPutBitsInternal(bw, bits, n_bits); - } -} - -//------------------------------------------------------------------------------ - -#ifdef __cplusplus -} // extern "C" -#endif - -#endif /* WEBP_UTILS_BIT_WRITER_H_ */ diff --git a/thirdparty/libwebp/utils/bit_writer_utils.c b/thirdparty/libwebp/utils/bit_writer_utils.c new file mode 100644 index 0000000000..ab0c49dce8 --- /dev/null +++ b/thirdparty/libwebp/utils/bit_writer_utils.c @@ -0,0 +1,319 @@ +// Copyright 2011 Google Inc. All Rights Reserved. +// +// Use of this source code is governed by a BSD-style license +// that can be found in the COPYING file in the root of the source +// tree. An additional intellectual property rights grant can be found +// in the file PATENTS. All contributing project authors may +// be found in the AUTHORS file in the root of the source tree. +// ----------------------------------------------------------------------------- +// +// Bit writing and boolean coder +// +// Author: Skal (pascal.massimino@gmail.com) +// Vikas Arora (vikaas.arora@gmail.com) + +#include +#include // for memcpy() +#include + +#include "./bit_writer_utils.h" +#include "./endian_inl_utils.h" +#include "./utils.h" + +//------------------------------------------------------------------------------ +// VP8BitWriter + +static int BitWriterResize(VP8BitWriter* const bw, size_t extra_size) { + uint8_t* new_buf; + size_t new_size; + const uint64_t needed_size_64b = (uint64_t)bw->pos_ + extra_size; + const size_t needed_size = (size_t)needed_size_64b; + if (needed_size_64b != needed_size) { + bw->error_ = 1; + return 0; + } + if (needed_size <= bw->max_pos_) return 1; + // If the following line wraps over 32bit, the test just after will catch it. + new_size = 2 * bw->max_pos_; + if (new_size < needed_size) new_size = needed_size; + if (new_size < 1024) new_size = 1024; + new_buf = (uint8_t*)WebPSafeMalloc(1ULL, new_size); + if (new_buf == NULL) { + bw->error_ = 1; + return 0; + } + if (bw->pos_ > 0) { + assert(bw->buf_ != NULL); + memcpy(new_buf, bw->buf_, bw->pos_); + } + WebPSafeFree(bw->buf_); + bw->buf_ = new_buf; + bw->max_pos_ = new_size; + return 1; +} + +static void Flush(VP8BitWriter* const bw) { + const int s = 8 + bw->nb_bits_; + const int32_t bits = bw->value_ >> s; + assert(bw->nb_bits_ >= 0); + bw->value_ -= bits << s; + bw->nb_bits_ -= 8; + if ((bits & 0xff) != 0xff) { + size_t pos = bw->pos_; + if (!BitWriterResize(bw, bw->run_ + 1)) { + return; + } + if (bits & 0x100) { // overflow -> propagate carry over pending 0xff's + if (pos > 0) bw->buf_[pos - 1]++; + } + if (bw->run_ > 0) { + const int value = (bits & 0x100) ? 0x00 : 0xff; + for (; bw->run_ > 0; --bw->run_) bw->buf_[pos++] = value; + } + bw->buf_[pos++] = bits; + bw->pos_ = pos; + } else { + bw->run_++; // delay writing of bytes 0xff, pending eventual carry. + } +} + +//------------------------------------------------------------------------------ +// renormalization + +static const uint8_t kNorm[128] = { // renorm_sizes[i] = 8 - log2(i) + 7, 6, 6, 5, 5, 5, 5, 4, 4, 4, 4, 4, 4, 4, 4, + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 0 +}; + +// range = ((range + 1) << kVP8Log2Range[range]) - 1 +static const uint8_t kNewRange[128] = { + 127, 127, 191, 127, 159, 191, 223, 127, 143, 159, 175, 191, 207, 223, 239, + 127, 135, 143, 151, 159, 167, 175, 183, 191, 199, 207, 215, 223, 231, 239, + 247, 127, 131, 135, 139, 143, 147, 151, 155, 159, 163, 167, 171, 175, 179, + 183, 187, 191, 195, 199, 203, 207, 211, 215, 219, 223, 227, 231, 235, 239, + 243, 247, 251, 127, 129, 131, 133, 135, 137, 139, 141, 143, 145, 147, 149, + 151, 153, 155, 157, 159, 161, 163, 165, 167, 169, 171, 173, 175, 177, 179, + 181, 183, 185, 187, 189, 191, 193, 195, 197, 199, 201, 203, 205, 207, 209, + 211, 213, 215, 217, 219, 221, 223, 225, 227, 229, 231, 233, 235, 237, 239, + 241, 243, 245, 247, 249, 251, 253, 127 +}; + +int VP8PutBit(VP8BitWriter* const bw, int bit, int prob) { + const int split = (bw->range_ * prob) >> 8; + if (bit) { + bw->value_ += split + 1; + bw->range_ -= split + 1; + } else { + bw->range_ = split; + } + if (bw->range_ < 127) { // emit 'shift' bits out and renormalize + const int shift = kNorm[bw->range_]; + bw->range_ = kNewRange[bw->range_]; + bw->value_ <<= shift; + bw->nb_bits_ += shift; + if (bw->nb_bits_ > 0) Flush(bw); + } + return bit; +} + +int VP8PutBitUniform(VP8BitWriter* const bw, int bit) { + const int split = bw->range_ >> 1; + if (bit) { + bw->value_ += split + 1; + bw->range_ -= split + 1; + } else { + bw->range_ = split; + } + if (bw->range_ < 127) { + bw->range_ = kNewRange[bw->range_]; + bw->value_ <<= 1; + bw->nb_bits_ += 1; + if (bw->nb_bits_ > 0) Flush(bw); + } + return bit; +} + +void VP8PutBits(VP8BitWriter* const bw, uint32_t value, int nb_bits) { + uint32_t mask; + assert(nb_bits > 0 && nb_bits < 32); + for (mask = 1u << (nb_bits - 1); mask; mask >>= 1) { + VP8PutBitUniform(bw, value & mask); + } +} + +void VP8PutSignedBits(VP8BitWriter* const bw, int value, int nb_bits) { + if (!VP8PutBitUniform(bw, value != 0)) return; + if (value < 0) { + VP8PutBits(bw, ((-value) << 1) | 1, nb_bits + 1); + } else { + VP8PutBits(bw, value << 1, nb_bits + 1); + } +} + +//------------------------------------------------------------------------------ + +int VP8BitWriterInit(VP8BitWriter* const bw, size_t expected_size) { + bw->range_ = 255 - 1; + bw->value_ = 0; + bw->run_ = 0; + bw->nb_bits_ = -8; + bw->pos_ = 0; + bw->max_pos_ = 0; + bw->error_ = 0; + bw->buf_ = NULL; + return (expected_size > 0) ? BitWriterResize(bw, expected_size) : 1; +} + +uint8_t* VP8BitWriterFinish(VP8BitWriter* const bw) { + VP8PutBits(bw, 0, 9 - bw->nb_bits_); + bw->nb_bits_ = 0; // pad with zeroes + Flush(bw); + return bw->buf_; +} + +int VP8BitWriterAppend(VP8BitWriter* const bw, + const uint8_t* data, size_t size) { + assert(data != NULL); + if (bw->nb_bits_ != -8) return 0; // Flush() must have been called + if (!BitWriterResize(bw, size)) return 0; + memcpy(bw->buf_ + bw->pos_, data, size); + bw->pos_ += size; + return 1; +} + +void VP8BitWriterWipeOut(VP8BitWriter* const bw) { + if (bw != NULL) { + WebPSafeFree(bw->buf_); + memset(bw, 0, sizeof(*bw)); + } +} + +//------------------------------------------------------------------------------ +// VP8LBitWriter + +// This is the minimum amount of size the memory buffer is guaranteed to grow +// when extra space is needed. +#define MIN_EXTRA_SIZE (32768ULL) + +// Returns 1 on success. +static int VP8LBitWriterResize(VP8LBitWriter* const bw, size_t extra_size) { + uint8_t* allocated_buf; + size_t allocated_size; + const size_t max_bytes = bw->end_ - bw->buf_; + const size_t current_size = bw->cur_ - bw->buf_; + const uint64_t size_required_64b = (uint64_t)current_size + extra_size; + const size_t size_required = (size_t)size_required_64b; + if (size_required != size_required_64b) { + bw->error_ = 1; + return 0; + } + if (max_bytes > 0 && size_required <= max_bytes) return 1; + allocated_size = (3 * max_bytes) >> 1; + if (allocated_size < size_required) allocated_size = size_required; + // make allocated size multiple of 1k + allocated_size = (((allocated_size >> 10) + 1) << 10); + allocated_buf = (uint8_t*)WebPSafeMalloc(1ULL, allocated_size); + if (allocated_buf == NULL) { + bw->error_ = 1; + return 0; + } + if (current_size > 0) { + memcpy(allocated_buf, bw->buf_, current_size); + } + WebPSafeFree(bw->buf_); + bw->buf_ = allocated_buf; + bw->cur_ = bw->buf_ + current_size; + bw->end_ = bw->buf_ + allocated_size; + return 1; +} + +int VP8LBitWriterInit(VP8LBitWriter* const bw, size_t expected_size) { + memset(bw, 0, sizeof(*bw)); + return VP8LBitWriterResize(bw, expected_size); +} + +void VP8LBitWriterWipeOut(VP8LBitWriter* const bw) { + if (bw != NULL) { + WebPSafeFree(bw->buf_); + memset(bw, 0, sizeof(*bw)); + } +} + +void VP8LPutBitsFlushBits(VP8LBitWriter* const bw) { + // If needed, make some room by flushing some bits out. + if (bw->cur_ + VP8L_WRITER_BYTES > bw->end_) { + const uint64_t extra_size = (bw->end_ - bw->buf_) + MIN_EXTRA_SIZE; + if (extra_size != (size_t)extra_size || + !VP8LBitWriterResize(bw, (size_t)extra_size)) { + bw->cur_ = bw->buf_; + bw->error_ = 1; + return; + } + } + *(vp8l_wtype_t*)bw->cur_ = (vp8l_wtype_t)WSWAP((vp8l_wtype_t)bw->bits_); + bw->cur_ += VP8L_WRITER_BYTES; + bw->bits_ >>= VP8L_WRITER_BITS; + bw->used_ -= VP8L_WRITER_BITS; +} + +void VP8LPutBitsInternal(VP8LBitWriter* const bw, uint32_t bits, int n_bits) { + assert(n_bits <= 32); + // That's the max we can handle: + assert(sizeof(vp8l_wtype_t) == 2); + if (n_bits > 0) { + vp8l_atype_t lbits = bw->bits_; + int used = bw->used_; + // Special case of overflow handling for 32bit accumulator (2-steps flush). +#if VP8L_WRITER_BITS == 16 + if (used + n_bits >= VP8L_WRITER_MAX_BITS) { + // Fill up all the VP8L_WRITER_MAX_BITS so it can be flushed out below. + const int shift = VP8L_WRITER_MAX_BITS - used; + lbits |= (vp8l_atype_t)bits << used; + used = VP8L_WRITER_MAX_BITS; + n_bits -= shift; + bits >>= shift; + assert(n_bits <= VP8L_WRITER_MAX_BITS); + } +#endif + // If needed, make some room by flushing some bits out. + while (used >= VP8L_WRITER_BITS) { + if (bw->cur_ + VP8L_WRITER_BYTES > bw->end_) { + const uint64_t extra_size = (bw->end_ - bw->buf_) + MIN_EXTRA_SIZE; + if (extra_size != (size_t)extra_size || + !VP8LBitWriterResize(bw, (size_t)extra_size)) { + bw->cur_ = bw->buf_; + bw->error_ = 1; + return; + } + } + *(vp8l_wtype_t*)bw->cur_ = (vp8l_wtype_t)WSWAP((vp8l_wtype_t)lbits); + bw->cur_ += VP8L_WRITER_BYTES; + lbits >>= VP8L_WRITER_BITS; + used -= VP8L_WRITER_BITS; + } + bw->bits_ = lbits | ((vp8l_atype_t)bits << used); + bw->used_ = used + n_bits; + } +} + +uint8_t* VP8LBitWriterFinish(VP8LBitWriter* const bw) { + // flush leftover bits + if (VP8LBitWriterResize(bw, (bw->used_ + 7) >> 3)) { + while (bw->used_ > 0) { + *bw->cur_++ = (uint8_t)bw->bits_; + bw->bits_ >>= 8; + bw->used_ -= 8; + } + bw->used_ = 0; + } + return bw->buf_; +} + +//------------------------------------------------------------------------------ diff --git a/thirdparty/libwebp/utils/bit_writer_utils.h b/thirdparty/libwebp/utils/bit_writer_utils.h new file mode 100644 index 0000000000..9c02bbc06d --- /dev/null +++ b/thirdparty/libwebp/utils/bit_writer_utils.h @@ -0,0 +1,146 @@ +// Copyright 2011 Google Inc. All Rights Reserved. +// +// Use of this source code is governed by a BSD-style license +// that can be found in the COPYING file in the root of the source +// tree. An additional intellectual property rights grant can be found +// in the file PATENTS. All contributing project authors may +// be found in the AUTHORS file in the root of the source tree. +// ----------------------------------------------------------------------------- +// +// Bit writing and boolean coder +// +// Author: Skal (pascal.massimino@gmail.com) + +#ifndef WEBP_UTILS_BIT_WRITER_H_ +#define WEBP_UTILS_BIT_WRITER_H_ + +#include "../webp/types.h" + +#ifdef __cplusplus +extern "C" { +#endif + +//------------------------------------------------------------------------------ +// Bit-writing + +typedef struct VP8BitWriter VP8BitWriter; +struct VP8BitWriter { + int32_t range_; // range-1 + int32_t value_; + int run_; // number of outstanding bits + int nb_bits_; // number of pending bits + uint8_t* buf_; // internal buffer. Re-allocated regularly. Not owned. + size_t pos_; + size_t max_pos_; + int error_; // true in case of error +}; + +// Initialize the object. Allocates some initial memory based on expected_size. +int VP8BitWriterInit(VP8BitWriter* const bw, size_t expected_size); +// Finalize the bitstream coding. Returns a pointer to the internal buffer. +uint8_t* VP8BitWriterFinish(VP8BitWriter* const bw); +// Release any pending memory and zeroes the object. Not a mandatory call. +// Only useful in case of error, when the internal buffer hasn't been grabbed! +void VP8BitWriterWipeOut(VP8BitWriter* const bw); + +int VP8PutBit(VP8BitWriter* const bw, int bit, int prob); +int VP8PutBitUniform(VP8BitWriter* const bw, int bit); +void VP8PutBits(VP8BitWriter* const bw, uint32_t value, int nb_bits); +void VP8PutSignedBits(VP8BitWriter* const bw, int value, int nb_bits); + +// Appends some bytes to the internal buffer. Data is copied. +int VP8BitWriterAppend(VP8BitWriter* const bw, + const uint8_t* data, size_t size); + +// return approximate write position (in bits) +static WEBP_INLINE uint64_t VP8BitWriterPos(const VP8BitWriter* const bw) { + const uint64_t nb_bits = 8 + bw->nb_bits_; // bw->nb_bits_ is <= 0, note + return (bw->pos_ + bw->run_) * 8 + nb_bits; +} + +// Returns a pointer to the internal buffer. +static WEBP_INLINE uint8_t* VP8BitWriterBuf(const VP8BitWriter* const bw) { + return bw->buf_; +} +// Returns the size of the internal buffer. +static WEBP_INLINE size_t VP8BitWriterSize(const VP8BitWriter* const bw) { + return bw->pos_; +} + +//------------------------------------------------------------------------------ +// VP8LBitWriter + +#if defined(__x86_64__) || defined(_M_X64) // 64bit +typedef uint64_t vp8l_atype_t; // accumulator type +typedef uint32_t vp8l_wtype_t; // writing type +#define WSWAP HToLE32 +#define VP8L_WRITER_BYTES 4 // sizeof(vp8l_wtype_t) +#define VP8L_WRITER_BITS 32 // 8 * sizeof(vp8l_wtype_t) +#define VP8L_WRITER_MAX_BITS 64 // 8 * sizeof(vp8l_atype_t) +#else +typedef uint32_t vp8l_atype_t; +typedef uint16_t vp8l_wtype_t; +#define WSWAP HToLE16 +#define VP8L_WRITER_BYTES 2 +#define VP8L_WRITER_BITS 16 +#define VP8L_WRITER_MAX_BITS 32 +#endif + +typedef struct { + vp8l_atype_t bits_; // bit accumulator + int used_; // number of bits used in accumulator + uint8_t* buf_; // start of buffer + uint8_t* cur_; // current write position + uint8_t* end_; // end of buffer + + // After all bits are written (VP8LBitWriterFinish()), the caller must observe + // the state of error_. A value of 1 indicates that a memory allocation + // failure has happened during bit writing. A value of 0 indicates successful + // writing of bits. + int error_; +} VP8LBitWriter; + +static WEBP_INLINE size_t VP8LBitWriterNumBytes(VP8LBitWriter* const bw) { + return (bw->cur_ - bw->buf_) + ((bw->used_ + 7) >> 3); +} + +// Returns false in case of memory allocation error. +int VP8LBitWriterInit(VP8LBitWriter* const bw, size_t expected_size); +// Finalize the bitstream coding. Returns a pointer to the internal buffer. +uint8_t* VP8LBitWriterFinish(VP8LBitWriter* const bw); +// Release any pending memory and zeroes the object. +void VP8LBitWriterWipeOut(VP8LBitWriter* const bw); + +// Internal function for VP8LPutBits flushing 32 bits from the written state. +void VP8LPutBitsFlushBits(VP8LBitWriter* const bw); + +// PutBits internal function used in the 16 bit vp8l_wtype_t case. +void VP8LPutBitsInternal(VP8LBitWriter* const bw, uint32_t bits, int n_bits); + +// This function writes bits into bytes in increasing addresses (little endian), +// and within a byte least-significant-bit first. +// This function can write up to 32 bits in one go, but VP8LBitReader can only +// read 24 bits max (VP8L_MAX_NUM_BIT_READ). +// VP8LBitWriter's error_ flag is set in case of memory allocation error. +static WEBP_INLINE void VP8LPutBits(VP8LBitWriter* const bw, + uint32_t bits, int n_bits) { + if (sizeof(vp8l_wtype_t) == 4) { + if (n_bits > 0) { + if (bw->used_ >= 32) { + VP8LPutBitsFlushBits(bw); + } + bw->bits_ |= (vp8l_atype_t)bits << bw->used_; + bw->used_ += n_bits; + } + } else { + VP8LPutBitsInternal(bw, bits, n_bits); + } +} + +//------------------------------------------------------------------------------ + +#ifdef __cplusplus +} // extern "C" +#endif + +#endif /* WEBP_UTILS_BIT_WRITER_H_ */ diff --git a/thirdparty/libwebp/utils/color_cache.c b/thirdparty/libwebp/utils/color_cache.c deleted file mode 100644 index c34b2e7f1a..0000000000 --- a/thirdparty/libwebp/utils/color_cache.c +++ /dev/null @@ -1,49 +0,0 @@ -// Copyright 2012 Google Inc. All Rights Reserved. -// -// Use of this source code is governed by a BSD-style license -// that can be found in the COPYING file in the root of the source -// tree. An additional intellectual property rights grant can be found -// in the file PATENTS. All contributing project authors may -// be found in the AUTHORS file in the root of the source tree. -// ----------------------------------------------------------------------------- -// -// Color Cache for WebP Lossless -// -// Author: Jyrki Alakuijala (jyrki@google.com) - -#include -#include -#include -#include "./color_cache.h" -#include "./utils.h" - -//------------------------------------------------------------------------------ -// VP8LColorCache. - -int VP8LColorCacheInit(VP8LColorCache* const cc, int hash_bits) { - const int hash_size = 1 << hash_bits; - assert(cc != NULL); - assert(hash_bits > 0); - cc->colors_ = (uint32_t*)WebPSafeCalloc((uint64_t)hash_size, - sizeof(*cc->colors_)); - if (cc->colors_ == NULL) return 0; - cc->hash_shift_ = 32 - hash_bits; - cc->hash_bits_ = hash_bits; - return 1; -} - -void VP8LColorCacheClear(VP8LColorCache* const cc) { - if (cc != NULL) { - WebPSafeFree(cc->colors_); - cc->colors_ = NULL; - } -} - -void VP8LColorCacheCopy(const VP8LColorCache* const src, - VP8LColorCache* const dst) { - assert(src != NULL); - assert(dst != NULL); - assert(src->hash_bits_ == dst->hash_bits_); - memcpy(dst->colors_, src->colors_, - ((size_t)1u << dst->hash_bits_) * sizeof(*dst->colors_)); -} diff --git a/thirdparty/libwebp/utils/color_cache.h b/thirdparty/libwebp/utils/color_cache.h deleted file mode 100644 index a9a9f64270..0000000000 --- a/thirdparty/libwebp/utils/color_cache.h +++ /dev/null @@ -1,80 +0,0 @@ -// Copyright 2012 Google Inc. All Rights Reserved. -// -// Use of this source code is governed by a BSD-style license -// that can be found in the COPYING file in the root of the source -// tree. An additional intellectual property rights grant can be found -// in the file PATENTS. All contributing project authors may -// be found in the AUTHORS file in the root of the source tree. -// ----------------------------------------------------------------------------- -// -// Color Cache for WebP Lossless -// -// Authors: Jyrki Alakuijala (jyrki@google.com) -// Urvang Joshi (urvang@google.com) - -#ifndef WEBP_UTILS_COLOR_CACHE_H_ -#define WEBP_UTILS_COLOR_CACHE_H_ - -#include "../webp/types.h" - -#ifdef __cplusplus -extern "C" { -#endif - -// Main color cache struct. -typedef struct { - uint32_t *colors_; // color entries - int hash_shift_; // Hash shift: 32 - hash_bits_. - int hash_bits_; -} VP8LColorCache; - -static const uint32_t kHashMul = 0x1e35a7bd; - -static WEBP_INLINE uint32_t VP8LColorCacheLookup( - const VP8LColorCache* const cc, uint32_t key) { - assert((key >> cc->hash_bits_) == 0u); - return cc->colors_[key]; -} - -static WEBP_INLINE void VP8LColorCacheSet(const VP8LColorCache* const cc, - uint32_t key, uint32_t argb) { - assert((key >> cc->hash_bits_) == 0u); - cc->colors_[key] = argb; -} - -static WEBP_INLINE void VP8LColorCacheInsert(const VP8LColorCache* const cc, - uint32_t argb) { - const uint32_t key = (kHashMul * argb) >> cc->hash_shift_; - cc->colors_[key] = argb; -} - -static WEBP_INLINE int VP8LColorCacheGetIndex(const VP8LColorCache* const cc, - uint32_t argb) { - return (kHashMul * argb) >> cc->hash_shift_; -} - -static WEBP_INLINE int VP8LColorCacheContains(const VP8LColorCache* const cc, - uint32_t argb) { - const uint32_t key = (kHashMul * argb) >> cc->hash_shift_; - return (cc->colors_[key] == argb); -} - -//------------------------------------------------------------------------------ - -// Initializes the color cache with 'hash_bits' bits for the keys. -// Returns false in case of memory error. -int VP8LColorCacheInit(VP8LColorCache* const color_cache, int hash_bits); - -void VP8LColorCacheCopy(const VP8LColorCache* const src, - VP8LColorCache* const dst); - -// Delete the memory associated to color cache. -void VP8LColorCacheClear(VP8LColorCache* const color_cache); - -//------------------------------------------------------------------------------ - -#ifdef __cplusplus -} -#endif - -#endif // WEBP_UTILS_COLOR_CACHE_H_ diff --git a/thirdparty/libwebp/utils/color_cache_utils.c b/thirdparty/libwebp/utils/color_cache_utils.c new file mode 100644 index 0000000000..0172590c48 --- /dev/null +++ b/thirdparty/libwebp/utils/color_cache_utils.c @@ -0,0 +1,49 @@ +// Copyright 2012 Google Inc. All Rights Reserved. +// +// Use of this source code is governed by a BSD-style license +// that can be found in the COPYING file in the root of the source +// tree. An additional intellectual property rights grant can be found +// in the file PATENTS. All contributing project authors may +// be found in the AUTHORS file in the root of the source tree. +// ----------------------------------------------------------------------------- +// +// Color Cache for WebP Lossless +// +// Author: Jyrki Alakuijala (jyrki@google.com) + +#include +#include +#include +#include "./color_cache_utils.h" +#include "./utils.h" + +//------------------------------------------------------------------------------ +// VP8LColorCache. + +int VP8LColorCacheInit(VP8LColorCache* const cc, int hash_bits) { + const int hash_size = 1 << hash_bits; + assert(cc != NULL); + assert(hash_bits > 0); + cc->colors_ = (uint32_t*)WebPSafeCalloc((uint64_t)hash_size, + sizeof(*cc->colors_)); + if (cc->colors_ == NULL) return 0; + cc->hash_shift_ = 32 - hash_bits; + cc->hash_bits_ = hash_bits; + return 1; +} + +void VP8LColorCacheClear(VP8LColorCache* const cc) { + if (cc != NULL) { + WebPSafeFree(cc->colors_); + cc->colors_ = NULL; + } +} + +void VP8LColorCacheCopy(const VP8LColorCache* const src, + VP8LColorCache* const dst) { + assert(src != NULL); + assert(dst != NULL); + assert(src->hash_bits_ == dst->hash_bits_); + memcpy(dst->colors_, src->colors_, + ((size_t)1u << dst->hash_bits_) * sizeof(*dst->colors_)); +} diff --git a/thirdparty/libwebp/utils/color_cache_utils.h b/thirdparty/libwebp/utils/color_cache_utils.h new file mode 100644 index 0000000000..c373e6b361 --- /dev/null +++ b/thirdparty/libwebp/utils/color_cache_utils.h @@ -0,0 +1,85 @@ +// Copyright 2012 Google Inc. All Rights Reserved. +// +// Use of this source code is governed by a BSD-style license +// that can be found in the COPYING file in the root of the source +// tree. An additional intellectual property rights grant can be found +// in the file PATENTS. All contributing project authors may +// be found in the AUTHORS file in the root of the source tree. +// ----------------------------------------------------------------------------- +// +// Color Cache for WebP Lossless +// +// Authors: Jyrki Alakuijala (jyrki@google.com) +// Urvang Joshi (urvang@google.com) + +#ifndef WEBP_UTILS_COLOR_CACHE_H_ +#define WEBP_UTILS_COLOR_CACHE_H_ + +#include "../webp/types.h" + +#ifdef __cplusplus +extern "C" { +#endif + +// Main color cache struct. +typedef struct { + uint32_t *colors_; // color entries + int hash_shift_; // Hash shift: 32 - hash_bits_. + int hash_bits_; +} VP8LColorCache; + +static const uint64_t kHashMul = 0x1e35a7bdull; + +static WEBP_INLINE int HashPix(uint32_t argb, int shift) { + return (int)(((argb * kHashMul) & 0xffffffffu) >> shift); +} + +static WEBP_INLINE uint32_t VP8LColorCacheLookup( + const VP8LColorCache* const cc, uint32_t key) { + assert((key >> cc->hash_bits_) == 0u); + return cc->colors_[key]; +} + +static WEBP_INLINE void VP8LColorCacheSet(const VP8LColorCache* const cc, + uint32_t key, uint32_t argb) { + assert((key >> cc->hash_bits_) == 0u); + cc->colors_[key] = argb; +} + +static WEBP_INLINE void VP8LColorCacheInsert(const VP8LColorCache* const cc, + uint32_t argb) { + const int key = HashPix(argb, cc->hash_shift_); + cc->colors_[key] = argb; +} + +static WEBP_INLINE int VP8LColorCacheGetIndex(const VP8LColorCache* const cc, + uint32_t argb) { + return HashPix(argb, cc->hash_shift_); +} + +// Return the key if cc contains argb, and -1 otherwise. +static WEBP_INLINE int VP8LColorCacheContains(const VP8LColorCache* const cc, + uint32_t argb) { + const int key = HashPix(argb, cc->hash_shift_); + return (cc->colors_[key] == argb) ? key : -1; +} + +//------------------------------------------------------------------------------ + +// Initializes the color cache with 'hash_bits' bits for the keys. +// Returns false in case of memory error. +int VP8LColorCacheInit(VP8LColorCache* const color_cache, int hash_bits); + +void VP8LColorCacheCopy(const VP8LColorCache* const src, + VP8LColorCache* const dst); + +// Delete the memory associated to color cache. +void VP8LColorCacheClear(VP8LColorCache* const color_cache); + +//------------------------------------------------------------------------------ + +#ifdef __cplusplus +} +#endif + +#endif // WEBP_UTILS_COLOR_CACHE_H_ diff --git a/thirdparty/libwebp/utils/endian_inl.h b/thirdparty/libwebp/utils/endian_inl.h deleted file mode 100644 index e11260ff7d..0000000000 --- a/thirdparty/libwebp/utils/endian_inl.h +++ /dev/null @@ -1,100 +0,0 @@ -// Copyright 2014 Google Inc. All Rights Reserved. -// -// Use of this source code is governed by a BSD-style license -// that can be found in the COPYING file in the root of the source -// tree. An additional intellectual property rights grant can be found -// in the file PATENTS. All contributing project authors may -// be found in the AUTHORS file in the root of the source tree. -// ----------------------------------------------------------------------------- -// -// Endian related functions. - -#ifndef WEBP_UTILS_ENDIAN_INL_H_ -#define WEBP_UTILS_ENDIAN_INL_H_ - -#ifdef HAVE_CONFIG_H -#include "../webp/config.h" -#endif - -#include "../dsp/dsp.h" -#include "../webp/types.h" - -// some endian fix (e.g.: mips-gcc doesn't define __BIG_ENDIAN__) -#if !defined(WORDS_BIGENDIAN) && \ - (defined(__BIG_ENDIAN__) || defined(_M_PPC) || \ - (defined(__BYTE_ORDER__) && (__BYTE_ORDER__ == __ORDER_BIG_ENDIAN__))) -#define WORDS_BIGENDIAN -#endif - -#if defined(WORDS_BIGENDIAN) -#define HToLE32 BSwap32 -#define HToLE16 BSwap16 -#else -#define HToLE32(x) (x) -#define HToLE16(x) (x) -#endif - -#if !defined(HAVE_CONFIG_H) -#if LOCAL_GCC_PREREQ(4,8) || __has_builtin(__builtin_bswap16) -#define HAVE_BUILTIN_BSWAP16 -#endif -#if LOCAL_GCC_PREREQ(4,3) || __has_builtin(__builtin_bswap32) -#define HAVE_BUILTIN_BSWAP32 -#endif -#if LOCAL_GCC_PREREQ(4,3) || __has_builtin(__builtin_bswap64) -#define HAVE_BUILTIN_BSWAP64 -#endif -#endif // !HAVE_CONFIG_H - -static WEBP_INLINE uint16_t BSwap16(uint16_t x) { -#if defined(HAVE_BUILTIN_BSWAP16) - return __builtin_bswap16(x); -#elif defined(_MSC_VER) - return _byteswap_ushort(x); -#else - // gcc will recognize a 'rorw $8, ...' here: - return (x >> 8) | ((x & 0xff) << 8); -#endif // HAVE_BUILTIN_BSWAP16 -} - -static WEBP_INLINE uint32_t BSwap32(uint32_t x) { -#if defined(WEBP_USE_MIPS32_R2) - uint32_t ret; - __asm__ volatile ( - "wsbh %[ret], %[x] \n\t" - "rotr %[ret], %[ret], 16 \n\t" - : [ret]"=r"(ret) - : [x]"r"(x) - ); - return ret; -#elif defined(HAVE_BUILTIN_BSWAP32) - return __builtin_bswap32(x); -#elif defined(__i386__) || defined(__x86_64__) - uint32_t swapped_bytes; - __asm__ volatile("bswap %0" : "=r"(swapped_bytes) : "0"(x)); - return swapped_bytes; -#elif defined(_MSC_VER) - return (uint32_t)_byteswap_ulong(x); -#else - return (x >> 24) | ((x >> 8) & 0xff00) | ((x << 8) & 0xff0000) | (x << 24); -#endif // HAVE_BUILTIN_BSWAP32 -} - -static WEBP_INLINE uint64_t BSwap64(uint64_t x) { -#if defined(HAVE_BUILTIN_BSWAP64) - return __builtin_bswap64(x); -#elif defined(__x86_64__) - uint64_t swapped_bytes; - __asm__ volatile("bswapq %0" : "=r"(swapped_bytes) : "0"(x)); - return swapped_bytes; -#elif defined(_MSC_VER) - return (uint64_t)_byteswap_uint64(x); -#else // generic code for swapping 64-bit values (suggested by bdb@) - x = ((x & 0xffffffff00000000ull) >> 32) | ((x & 0x00000000ffffffffull) << 32); - x = ((x & 0xffff0000ffff0000ull) >> 16) | ((x & 0x0000ffff0000ffffull) << 16); - x = ((x & 0xff00ff00ff00ff00ull) >> 8) | ((x & 0x00ff00ff00ff00ffull) << 8); - return x; -#endif // HAVE_BUILTIN_BSWAP64 -} - -#endif // WEBP_UTILS_ENDIAN_INL_H_ diff --git a/thirdparty/libwebp/utils/endian_inl_utils.h b/thirdparty/libwebp/utils/endian_inl_utils.h new file mode 100644 index 0000000000..e11260ff7d --- /dev/null +++ b/thirdparty/libwebp/utils/endian_inl_utils.h @@ -0,0 +1,100 @@ +// Copyright 2014 Google Inc. All Rights Reserved. +// +// Use of this source code is governed by a BSD-style license +// that can be found in the COPYING file in the root of the source +// tree. An additional intellectual property rights grant can be found +// in the file PATENTS. All contributing project authors may +// be found in the AUTHORS file in the root of the source tree. +// ----------------------------------------------------------------------------- +// +// Endian related functions. + +#ifndef WEBP_UTILS_ENDIAN_INL_H_ +#define WEBP_UTILS_ENDIAN_INL_H_ + +#ifdef HAVE_CONFIG_H +#include "../webp/config.h" +#endif + +#include "../dsp/dsp.h" +#include "../webp/types.h" + +// some endian fix (e.g.: mips-gcc doesn't define __BIG_ENDIAN__) +#if !defined(WORDS_BIGENDIAN) && \ + (defined(__BIG_ENDIAN__) || defined(_M_PPC) || \ + (defined(__BYTE_ORDER__) && (__BYTE_ORDER__ == __ORDER_BIG_ENDIAN__))) +#define WORDS_BIGENDIAN +#endif + +#if defined(WORDS_BIGENDIAN) +#define HToLE32 BSwap32 +#define HToLE16 BSwap16 +#else +#define HToLE32(x) (x) +#define HToLE16(x) (x) +#endif + +#if !defined(HAVE_CONFIG_H) +#if LOCAL_GCC_PREREQ(4,8) || __has_builtin(__builtin_bswap16) +#define HAVE_BUILTIN_BSWAP16 +#endif +#if LOCAL_GCC_PREREQ(4,3) || __has_builtin(__builtin_bswap32) +#define HAVE_BUILTIN_BSWAP32 +#endif +#if LOCAL_GCC_PREREQ(4,3) || __has_builtin(__builtin_bswap64) +#define HAVE_BUILTIN_BSWAP64 +#endif +#endif // !HAVE_CONFIG_H + +static WEBP_INLINE uint16_t BSwap16(uint16_t x) { +#if defined(HAVE_BUILTIN_BSWAP16) + return __builtin_bswap16(x); +#elif defined(_MSC_VER) + return _byteswap_ushort(x); +#else + // gcc will recognize a 'rorw $8, ...' here: + return (x >> 8) | ((x & 0xff) << 8); +#endif // HAVE_BUILTIN_BSWAP16 +} + +static WEBP_INLINE uint32_t BSwap32(uint32_t x) { +#if defined(WEBP_USE_MIPS32_R2) + uint32_t ret; + __asm__ volatile ( + "wsbh %[ret], %[x] \n\t" + "rotr %[ret], %[ret], 16 \n\t" + : [ret]"=r"(ret) + : [x]"r"(x) + ); + return ret; +#elif defined(HAVE_BUILTIN_BSWAP32) + return __builtin_bswap32(x); +#elif defined(__i386__) || defined(__x86_64__) + uint32_t swapped_bytes; + __asm__ volatile("bswap %0" : "=r"(swapped_bytes) : "0"(x)); + return swapped_bytes; +#elif defined(_MSC_VER) + return (uint32_t)_byteswap_ulong(x); +#else + return (x >> 24) | ((x >> 8) & 0xff00) | ((x << 8) & 0xff0000) | (x << 24); +#endif // HAVE_BUILTIN_BSWAP32 +} + +static WEBP_INLINE uint64_t BSwap64(uint64_t x) { +#if defined(HAVE_BUILTIN_BSWAP64) + return __builtin_bswap64(x); +#elif defined(__x86_64__) + uint64_t swapped_bytes; + __asm__ volatile("bswapq %0" : "=r"(swapped_bytes) : "0"(x)); + return swapped_bytes; +#elif defined(_MSC_VER) + return (uint64_t)_byteswap_uint64(x); +#else // generic code for swapping 64-bit values (suggested by bdb@) + x = ((x & 0xffffffff00000000ull) >> 32) | ((x & 0x00000000ffffffffull) << 32); + x = ((x & 0xffff0000ffff0000ull) >> 16) | ((x & 0x0000ffff0000ffffull) << 16); + x = ((x & 0xff00ff00ff00ff00ull) >> 8) | ((x & 0x00ff00ff00ff00ffull) << 8); + return x; +#endif // HAVE_BUILTIN_BSWAP64 +} + +#endif // WEBP_UTILS_ENDIAN_INL_H_ diff --git a/thirdparty/libwebp/utils/filters.c b/thirdparty/libwebp/utils/filters.c deleted file mode 100644 index 15543b1271..0000000000 --- a/thirdparty/libwebp/utils/filters.c +++ /dev/null @@ -1,76 +0,0 @@ -// Copyright 2011 Google Inc. All Rights Reserved. -// -// Use of this source code is governed by a BSD-style license -// that can be found in the COPYING file in the root of the source -// tree. An additional intellectual property rights grant can be found -// in the file PATENTS. All contributing project authors may -// be found in the AUTHORS file in the root of the source tree. -// ----------------------------------------------------------------------------- -// -// filter estimation -// -// Author: Urvang (urvang@google.com) - -#include "./filters.h" -#include -#include - -// ----------------------------------------------------------------------------- -// Quick estimate of a potentially interesting filter mode to try. - -#define SMAX 16 -#define SDIFF(a, b) (abs((a) - (b)) >> 4) // Scoring diff, in [0..SMAX) - -static WEBP_INLINE int GradientPredictor(uint8_t a, uint8_t b, uint8_t c) { - const int g = a + b - c; - return ((g & ~0xff) == 0) ? g : (g < 0) ? 0 : 255; // clip to 8bit -} - -WEBP_FILTER_TYPE WebPEstimateBestFilter(const uint8_t* data, - int width, int height, int stride) { - int i, j; - int bins[WEBP_FILTER_LAST][SMAX]; - memset(bins, 0, sizeof(bins)); - - // We only sample every other pixels. That's enough. - for (j = 2; j < height - 1; j += 2) { - const uint8_t* const p = data + j * stride; - int mean = p[0]; - for (i = 2; i < width - 1; i += 2) { - const int diff0 = SDIFF(p[i], mean); - const int diff1 = SDIFF(p[i], p[i - 1]); - const int diff2 = SDIFF(p[i], p[i - width]); - const int grad_pred = - GradientPredictor(p[i - 1], p[i - width], p[i - width - 1]); - const int diff3 = SDIFF(p[i], grad_pred); - bins[WEBP_FILTER_NONE][diff0] = 1; - bins[WEBP_FILTER_HORIZONTAL][diff1] = 1; - bins[WEBP_FILTER_VERTICAL][diff2] = 1; - bins[WEBP_FILTER_GRADIENT][diff3] = 1; - mean = (3 * mean + p[i] + 2) >> 2; - } - } - { - int filter; - WEBP_FILTER_TYPE best_filter = WEBP_FILTER_NONE; - int best_score = 0x7fffffff; - for (filter = WEBP_FILTER_NONE; filter < WEBP_FILTER_LAST; ++filter) { - int score = 0; - for (i = 0; i < SMAX; ++i) { - if (bins[filter][i] > 0) { - score += i; - } - } - if (score < best_score) { - best_score = score; - best_filter = (WEBP_FILTER_TYPE)filter; - } - } - return best_filter; - } -} - -#undef SMAX -#undef SDIFF - -//------------------------------------------------------------------------------ diff --git a/thirdparty/libwebp/utils/filters.h b/thirdparty/libwebp/utils/filters.h deleted file mode 100644 index 088b132fc5..0000000000 --- a/thirdparty/libwebp/utils/filters.h +++ /dev/null @@ -1,32 +0,0 @@ -// Copyright 2011 Google Inc. All Rights Reserved. -// -// Use of this source code is governed by a BSD-style license -// that can be found in the COPYING file in the root of the source -// tree. An additional intellectual property rights grant can be found -// in the file PATENTS. All contributing project authors may -// be found in the AUTHORS file in the root of the source tree. -// ----------------------------------------------------------------------------- -// -// Spatial prediction using various filters -// -// Author: Urvang (urvang@google.com) - -#ifndef WEBP_UTILS_FILTERS_H_ -#define WEBP_UTILS_FILTERS_H_ - -#include "../webp/types.h" -#include "../dsp/dsp.h" - -#ifdef __cplusplus -extern "C" { -#endif - -// Fast estimate of a potentially good filter. -WEBP_FILTER_TYPE WebPEstimateBestFilter(const uint8_t* data, - int width, int height, int stride); - -#ifdef __cplusplus -} // extern "C" -#endif - -#endif /* WEBP_UTILS_FILTERS_H_ */ diff --git a/thirdparty/libwebp/utils/filters_utils.c b/thirdparty/libwebp/utils/filters_utils.c new file mode 100644 index 0000000000..49c1d18a22 --- /dev/null +++ b/thirdparty/libwebp/utils/filters_utils.c @@ -0,0 +1,76 @@ +// Copyright 2011 Google Inc. All Rights Reserved. +// +// Use of this source code is governed by a BSD-style license +// that can be found in the COPYING file in the root of the source +// tree. An additional intellectual property rights grant can be found +// in the file PATENTS. All contributing project authors may +// be found in the AUTHORS file in the root of the source tree. +// ----------------------------------------------------------------------------- +// +// filter estimation +// +// Author: Urvang (urvang@google.com) + +#include "./filters_utils.h" +#include +#include + +// ----------------------------------------------------------------------------- +// Quick estimate of a potentially interesting filter mode to try. + +#define SMAX 16 +#define SDIFF(a, b) (abs((a) - (b)) >> 4) // Scoring diff, in [0..SMAX) + +static WEBP_INLINE int GradientPredictor(uint8_t a, uint8_t b, uint8_t c) { + const int g = a + b - c; + return ((g & ~0xff) == 0) ? g : (g < 0) ? 0 : 255; // clip to 8bit +} + +WEBP_FILTER_TYPE WebPEstimateBestFilter(const uint8_t* data, + int width, int height, int stride) { + int i, j; + int bins[WEBP_FILTER_LAST][SMAX]; + memset(bins, 0, sizeof(bins)); + + // We only sample every other pixels. That's enough. + for (j = 2; j < height - 1; j += 2) { + const uint8_t* const p = data + j * stride; + int mean = p[0]; + for (i = 2; i < width - 1; i += 2) { + const int diff0 = SDIFF(p[i], mean); + const int diff1 = SDIFF(p[i], p[i - 1]); + const int diff2 = SDIFF(p[i], p[i - width]); + const int grad_pred = + GradientPredictor(p[i - 1], p[i - width], p[i - width - 1]); + const int diff3 = SDIFF(p[i], grad_pred); + bins[WEBP_FILTER_NONE][diff0] = 1; + bins[WEBP_FILTER_HORIZONTAL][diff1] = 1; + bins[WEBP_FILTER_VERTICAL][diff2] = 1; + bins[WEBP_FILTER_GRADIENT][diff3] = 1; + mean = (3 * mean + p[i] + 2) >> 2; + } + } + { + int filter; + WEBP_FILTER_TYPE best_filter = WEBP_FILTER_NONE; + int best_score = 0x7fffffff; + for (filter = WEBP_FILTER_NONE; filter < WEBP_FILTER_LAST; ++filter) { + int score = 0; + for (i = 0; i < SMAX; ++i) { + if (bins[filter][i] > 0) { + score += i; + } + } + if (score < best_score) { + best_score = score; + best_filter = (WEBP_FILTER_TYPE)filter; + } + } + return best_filter; + } +} + +#undef SMAX +#undef SDIFF + +//------------------------------------------------------------------------------ diff --git a/thirdparty/libwebp/utils/filters_utils.h b/thirdparty/libwebp/utils/filters_utils.h new file mode 100644 index 0000000000..088b132fc5 --- /dev/null +++ b/thirdparty/libwebp/utils/filters_utils.h @@ -0,0 +1,32 @@ +// Copyright 2011 Google Inc. All Rights Reserved. +// +// Use of this source code is governed by a BSD-style license +// that can be found in the COPYING file in the root of the source +// tree. An additional intellectual property rights grant can be found +// in the file PATENTS. All contributing project authors may +// be found in the AUTHORS file in the root of the source tree. +// ----------------------------------------------------------------------------- +// +// Spatial prediction using various filters +// +// Author: Urvang (urvang@google.com) + +#ifndef WEBP_UTILS_FILTERS_H_ +#define WEBP_UTILS_FILTERS_H_ + +#include "../webp/types.h" +#include "../dsp/dsp.h" + +#ifdef __cplusplus +extern "C" { +#endif + +// Fast estimate of a potentially good filter. +WEBP_FILTER_TYPE WebPEstimateBestFilter(const uint8_t* data, + int width, int height, int stride); + +#ifdef __cplusplus +} // extern "C" +#endif + +#endif /* WEBP_UTILS_FILTERS_H_ */ diff --git a/thirdparty/libwebp/utils/huffman.c b/thirdparty/libwebp/utils/huffman.c deleted file mode 100644 index 36e5502836..0000000000 --- a/thirdparty/libwebp/utils/huffman.c +++ /dev/null @@ -1,205 +0,0 @@ -// Copyright 2012 Google Inc. All Rights Reserved. -// -// Use of this source code is governed by a BSD-style license -// that can be found in the COPYING file in the root of the source -// tree. An additional intellectual property rights grant can be found -// in the file PATENTS. All contributing project authors may -// be found in the AUTHORS file in the root of the source tree. -// ----------------------------------------------------------------------------- -// -// Utilities for building and looking up Huffman trees. -// -// Author: Urvang Joshi (urvang@google.com) - -#include -#include -#include -#include "./huffman.h" -#include "./utils.h" -#include "../webp/format_constants.h" - -// Huffman data read via DecodeImageStream is represented in two (red and green) -// bytes. -#define MAX_HTREE_GROUPS 0x10000 - -HTreeGroup* VP8LHtreeGroupsNew(int num_htree_groups) { - HTreeGroup* const htree_groups = - (HTreeGroup*)WebPSafeMalloc(num_htree_groups, sizeof(*htree_groups)); - if (htree_groups == NULL) { - return NULL; - } - assert(num_htree_groups <= MAX_HTREE_GROUPS); - return htree_groups; -} - -void VP8LHtreeGroupsFree(HTreeGroup* const htree_groups) { - if (htree_groups != NULL) { - WebPSafeFree(htree_groups); - } -} - -// Returns reverse(reverse(key, len) + 1, len), where reverse(key, len) is the -// bit-wise reversal of the len least significant bits of key. -static WEBP_INLINE uint32_t GetNextKey(uint32_t key, int len) { - uint32_t step = 1 << (len - 1); - while (key & step) { - step >>= 1; - } - return (key & (step - 1)) + step; -} - -// Stores code in table[0], table[step], table[2*step], ..., table[end]. -// Assumes that end is an integer multiple of step. -static WEBP_INLINE void ReplicateValue(HuffmanCode* table, - int step, int end, - HuffmanCode code) { - assert(end % step == 0); - do { - end -= step; - table[end] = code; - } while (end > 0); -} - -// Returns the table width of the next 2nd level table. count is the histogram -// of bit lengths for the remaining symbols, len is the code length of the next -// processed symbol -static WEBP_INLINE int NextTableBitSize(const int* const count, - int len, int root_bits) { - int left = 1 << (len - root_bits); - while (len < MAX_ALLOWED_CODE_LENGTH) { - left -= count[len]; - if (left <= 0) break; - ++len; - left <<= 1; - } - return len - root_bits; -} - -int VP8LBuildHuffmanTable(HuffmanCode* const root_table, int root_bits, - const int code_lengths[], int code_lengths_size) { - HuffmanCode* table = root_table; // next available space in table - int total_size = 1 << root_bits; // total size root table + 2nd level table - int* sorted = NULL; // symbols sorted by code length - int len; // current code length - int symbol; // symbol index in original or sorted table - // number of codes of each length: - int count[MAX_ALLOWED_CODE_LENGTH + 1] = { 0 }; - // offsets in sorted table for each length: - int offset[MAX_ALLOWED_CODE_LENGTH + 1]; - - assert(code_lengths_size != 0); - assert(code_lengths != NULL); - assert(root_table != NULL); - assert(root_bits > 0); - - // Build histogram of code lengths. - for (symbol = 0; symbol < code_lengths_size; ++symbol) { - if (code_lengths[symbol] > MAX_ALLOWED_CODE_LENGTH) { - return 0; - } - ++count[code_lengths[symbol]]; - } - - // Error, all code lengths are zeros. - if (count[0] == code_lengths_size) { - return 0; - } - - // Generate offsets into sorted symbol table by code length. - offset[1] = 0; - for (len = 1; len < MAX_ALLOWED_CODE_LENGTH; ++len) { - if (count[len] > (1 << len)) { - return 0; - } - offset[len + 1] = offset[len] + count[len]; - } - - sorted = (int*)WebPSafeMalloc(code_lengths_size, sizeof(*sorted)); - if (sorted == NULL) { - return 0; - } - - // Sort symbols by length, by symbol order within each length. - for (symbol = 0; symbol < code_lengths_size; ++symbol) { - const int symbol_code_length = code_lengths[symbol]; - if (code_lengths[symbol] > 0) { - sorted[offset[symbol_code_length]++] = symbol; - } - } - - // Special case code with only one value. - if (offset[MAX_ALLOWED_CODE_LENGTH] == 1) { - HuffmanCode code; - code.bits = 0; - code.value = (uint16_t)sorted[0]; - ReplicateValue(table, 1, total_size, code); - WebPSafeFree(sorted); - return total_size; - } - - { - int step; // step size to replicate values in current table - uint32_t low = -1; // low bits for current root entry - uint32_t mask = total_size - 1; // mask for low bits - uint32_t key = 0; // reversed prefix code - int num_nodes = 1; // number of Huffman tree nodes - int num_open = 1; // number of open branches in current tree level - int table_bits = root_bits; // key length of current table - int table_size = 1 << table_bits; // size of current table - symbol = 0; - // Fill in root table. - for (len = 1, step = 2; len <= root_bits; ++len, step <<= 1) { - num_open <<= 1; - num_nodes += num_open; - num_open -= count[len]; - if (num_open < 0) { - WebPSafeFree(sorted); - return 0; - } - for (; count[len] > 0; --count[len]) { - HuffmanCode code; - code.bits = (uint8_t)len; - code.value = (uint16_t)sorted[symbol++]; - ReplicateValue(&table[key], step, table_size, code); - key = GetNextKey(key, len); - } - } - - // Fill in 2nd level tables and add pointers to root table. - for (len = root_bits + 1, step = 2; len <= MAX_ALLOWED_CODE_LENGTH; - ++len, step <<= 1) { - num_open <<= 1; - num_nodes += num_open; - num_open -= count[len]; - if (num_open < 0) { - WebPSafeFree(sorted); - return 0; - } - for (; count[len] > 0; --count[len]) { - HuffmanCode code; - if ((key & mask) != low) { - table += table_size; - table_bits = NextTableBitSize(count, len, root_bits); - table_size = 1 << table_bits; - total_size += table_size; - low = key & mask; - root_table[low].bits = (uint8_t)(table_bits + root_bits); - root_table[low].value = (uint16_t)((table - root_table) - low); - } - code.bits = (uint8_t)(len - root_bits); - code.value = (uint16_t)sorted[symbol++]; - ReplicateValue(&table[key >> root_bits], step, table_size, code); - key = GetNextKey(key, len); - } - } - - // Check if tree is full. - if (num_nodes != 2 * offset[MAX_ALLOWED_CODE_LENGTH] - 1) { - WebPSafeFree(sorted); - return 0; - } - } - - WebPSafeFree(sorted); - return total_size; -} diff --git a/thirdparty/libwebp/utils/huffman.h b/thirdparty/libwebp/utils/huffman.h deleted file mode 100644 index c6dd6aaa45..0000000000 --- a/thirdparty/libwebp/utils/huffman.h +++ /dev/null @@ -1,88 +0,0 @@ -// Copyright 2012 Google Inc. All Rights Reserved. -// -// Use of this source code is governed by a BSD-style license -// that can be found in the COPYING file in the root of the source -// tree. An additional intellectual property rights grant can be found -// in the file PATENTS. All contributing project authors may -// be found in the AUTHORS file in the root of the source tree. -// ----------------------------------------------------------------------------- -// -// Utilities for building and looking up Huffman trees. -// -// Author: Urvang Joshi (urvang@google.com) - -#ifndef WEBP_UTILS_HUFFMAN_H_ -#define WEBP_UTILS_HUFFMAN_H_ - -#include -#include "../webp/format_constants.h" -#include "../webp/types.h" - -#ifdef __cplusplus -extern "C" { -#endif - -#define HUFFMAN_TABLE_BITS 8 -#define HUFFMAN_TABLE_MASK ((1 << HUFFMAN_TABLE_BITS) - 1) - -#define LENGTHS_TABLE_BITS 7 -#define LENGTHS_TABLE_MASK ((1 << LENGTHS_TABLE_BITS) - 1) - - -// Huffman lookup table entry -typedef struct { - uint8_t bits; // number of bits used for this symbol - uint16_t value; // symbol value or table offset -} HuffmanCode; - -// long version for holding 32b values -typedef struct { - int bits; // number of bits used for this symbol, - // or an impossible value if not a literal code. - uint32_t value; // 32b packed ARGB value if literal, - // or non-literal symbol otherwise -} HuffmanCode32; - -#define HUFFMAN_PACKED_BITS 6 -#define HUFFMAN_PACKED_TABLE_SIZE (1u << HUFFMAN_PACKED_BITS) - -// Huffman table group. -// Includes special handling for the following cases: -// - is_trivial_literal: one common literal base for RED/BLUE/ALPHA (not GREEN) -// - is_trivial_code: only 1 code (no bit is read from bitstream) -// - use_packed_table: few enough literal symbols, so all the bit codes -// can fit into a small look-up table packed_table[] -// The common literal base, if applicable, is stored in 'literal_arb'. -typedef struct HTreeGroup HTreeGroup; -struct HTreeGroup { - HuffmanCode* htrees[HUFFMAN_CODES_PER_META_CODE]; - int is_trivial_literal; // True, if huffman trees for Red, Blue & Alpha - // Symbols are trivial (have a single code). - uint32_t literal_arb; // If is_trivial_literal is true, this is the - // ARGB value of the pixel, with Green channel - // being set to zero. - int is_trivial_code; // true if is_trivial_literal with only one code - int use_packed_table; // use packed table below for short literal code - // table mapping input bits to a packed values, or escape case to literal code - HuffmanCode32 packed_table[HUFFMAN_PACKED_TABLE_SIZE]; -}; - -// Creates the instance of HTreeGroup with specified number of tree-groups. -HTreeGroup* VP8LHtreeGroupsNew(int num_htree_groups); - -// Releases the memory allocated for HTreeGroup. -void VP8LHtreeGroupsFree(HTreeGroup* const htree_groups); - -// Builds Huffman lookup table assuming code lengths are in symbol order. -// The 'code_lengths' is pre-allocated temporary memory buffer used for creating -// the huffman table. -// Returns built table size or 0 in case of error (invalid tree or -// memory error). -int VP8LBuildHuffmanTable(HuffmanCode* const root_table, int root_bits, - const int code_lengths[], int code_lengths_size); - -#ifdef __cplusplus -} // extern "C" -#endif - -#endif // WEBP_UTILS_HUFFMAN_H_ diff --git a/thirdparty/libwebp/utils/huffman_encode.c b/thirdparty/libwebp/utils/huffman_encode.c deleted file mode 100644 index 4e5ef6b447..0000000000 --- a/thirdparty/libwebp/utils/huffman_encode.c +++ /dev/null @@ -1,417 +0,0 @@ -// Copyright 2011 Google Inc. All Rights Reserved. -// -// Use of this source code is governed by a BSD-style license -// that can be found in the COPYING file in the root of the source -// tree. An additional intellectual property rights grant can be found -// in the file PATENTS. All contributing project authors may -// be found in the AUTHORS file in the root of the source tree. -// ----------------------------------------------------------------------------- -// -// Author: Jyrki Alakuijala (jyrki@google.com) -// -// Entropy encoding (Huffman) for webp lossless. - -#include -#include -#include -#include "./huffman_encode.h" -#include "./utils.h" -#include "../webp/format_constants.h" - -// ----------------------------------------------------------------------------- -// Util function to optimize the symbol map for RLE coding - -// Heuristics for selecting the stride ranges to collapse. -static int ValuesShouldBeCollapsedToStrideAverage(int a, int b) { - return abs(a - b) < 4; -} - -// Change the population counts in a way that the consequent -// Huffman tree compression, especially its RLE-part, give smaller output. -static void OptimizeHuffmanForRle(int length, uint8_t* const good_for_rle, - uint32_t* const counts) { - // 1) Let's make the Huffman code more compatible with rle encoding. - int i; - for (; length >= 0; --length) { - if (length == 0) { - return; // All zeros. - } - if (counts[length - 1] != 0) { - // Now counts[0..length - 1] does not have trailing zeros. - break; - } - } - // 2) Let's mark all population counts that already can be encoded - // with an rle code. - { - // Let's not spoil any of the existing good rle codes. - // Mark any seq of 0's that is longer as 5 as a good_for_rle. - // Mark any seq of non-0's that is longer as 7 as a good_for_rle. - uint32_t symbol = counts[0]; - int stride = 0; - for (i = 0; i < length + 1; ++i) { - if (i == length || counts[i] != symbol) { - if ((symbol == 0 && stride >= 5) || - (symbol != 0 && stride >= 7)) { - int k; - for (k = 0; k < stride; ++k) { - good_for_rle[i - k - 1] = 1; - } - } - stride = 1; - if (i != length) { - symbol = counts[i]; - } - } else { - ++stride; - } - } - } - // 3) Let's replace those population counts that lead to more rle codes. - { - uint32_t stride = 0; - uint32_t limit = counts[0]; - uint32_t sum = 0; - for (i = 0; i < length + 1; ++i) { - if (i == length || good_for_rle[i] || - (i != 0 && good_for_rle[i - 1]) || - !ValuesShouldBeCollapsedToStrideAverage(counts[i], limit)) { - if (stride >= 4 || (stride >= 3 && sum == 0)) { - uint32_t k; - // The stride must end, collapse what we have, if we have enough (4). - uint32_t count = (sum + stride / 2) / stride; - if (count < 1) { - count = 1; - } - if (sum == 0) { - // Don't make an all zeros stride to be upgraded to ones. - count = 0; - } - for (k = 0; k < stride; ++k) { - // We don't want to change value at counts[i], - // that is already belonging to the next stride. Thus - 1. - counts[i - k - 1] = count; - } - } - stride = 0; - sum = 0; - if (i < length - 3) { - // All interesting strides have a count of at least 4, - // at least when non-zeros. - limit = (counts[i] + counts[i + 1] + - counts[i + 2] + counts[i + 3] + 2) / 4; - } else if (i < length) { - limit = counts[i]; - } else { - limit = 0; - } - } - ++stride; - if (i != length) { - sum += counts[i]; - if (stride >= 4) { - limit = (sum + stride / 2) / stride; - } - } - } - } -} - -// A comparer function for two Huffman trees: sorts first by 'total count' -// (more comes first), and then by 'value' (more comes first). -static int CompareHuffmanTrees(const void* ptr1, const void* ptr2) { - const HuffmanTree* const t1 = (const HuffmanTree*)ptr1; - const HuffmanTree* const t2 = (const HuffmanTree*)ptr2; - if (t1->total_count_ > t2->total_count_) { - return -1; - } else if (t1->total_count_ < t2->total_count_) { - return 1; - } else { - assert(t1->value_ != t2->value_); - return (t1->value_ < t2->value_) ? -1 : 1; - } -} - -static void SetBitDepths(const HuffmanTree* const tree, - const HuffmanTree* const pool, - uint8_t* const bit_depths, int level) { - if (tree->pool_index_left_ >= 0) { - SetBitDepths(&pool[tree->pool_index_left_], pool, bit_depths, level + 1); - SetBitDepths(&pool[tree->pool_index_right_], pool, bit_depths, level + 1); - } else { - bit_depths[tree->value_] = level; - } -} - -// Create an optimal Huffman tree. -// -// (data,length): population counts. -// tree_limit: maximum bit depth (inclusive) of the codes. -// bit_depths[]: how many bits are used for the symbol. -// -// Returns 0 when an error has occurred. -// -// The catch here is that the tree cannot be arbitrarily deep -// -// count_limit is the value that is to be faked as the minimum value -// and this minimum value is raised until the tree matches the -// maximum length requirement. -// -// This algorithm is not of excellent performance for very long data blocks, -// especially when population counts are longer than 2**tree_limit, but -// we are not planning to use this with extremely long blocks. -// -// See http://en.wikipedia.org/wiki/Huffman_coding -static void GenerateOptimalTree(const uint32_t* const histogram, - int histogram_size, - HuffmanTree* tree, int tree_depth_limit, - uint8_t* const bit_depths) { - uint32_t count_min; - HuffmanTree* tree_pool; - int tree_size_orig = 0; - int i; - - for (i = 0; i < histogram_size; ++i) { - if (histogram[i] != 0) { - ++tree_size_orig; - } - } - - if (tree_size_orig == 0) { // pretty optimal already! - return; - } - - tree_pool = tree + tree_size_orig; - - // For block sizes with less than 64k symbols we never need to do a - // second iteration of this loop. - // If we actually start running inside this loop a lot, we would perhaps - // be better off with the Katajainen algorithm. - assert(tree_size_orig <= (1 << (tree_depth_limit - 1))); - for (count_min = 1; ; count_min *= 2) { - int tree_size = tree_size_orig; - // We need to pack the Huffman tree in tree_depth_limit bits. - // So, we try by faking histogram entries to be at least 'count_min'. - int idx = 0; - int j; - for (j = 0; j < histogram_size; ++j) { - if (histogram[j] != 0) { - const uint32_t count = - (histogram[j] < count_min) ? count_min : histogram[j]; - tree[idx].total_count_ = count; - tree[idx].value_ = j; - tree[idx].pool_index_left_ = -1; - tree[idx].pool_index_right_ = -1; - ++idx; - } - } - - // Build the Huffman tree. - qsort(tree, tree_size, sizeof(*tree), CompareHuffmanTrees); - - if (tree_size > 1) { // Normal case. - int tree_pool_size = 0; - while (tree_size > 1) { // Finish when we have only one root. - uint32_t count; - tree_pool[tree_pool_size++] = tree[tree_size - 1]; - tree_pool[tree_pool_size++] = tree[tree_size - 2]; - count = tree_pool[tree_pool_size - 1].total_count_ + - tree_pool[tree_pool_size - 2].total_count_; - tree_size -= 2; - { - // Search for the insertion point. - int k; - for (k = 0; k < tree_size; ++k) { - if (tree[k].total_count_ <= count) { - break; - } - } - memmove(tree + (k + 1), tree + k, (tree_size - k) * sizeof(*tree)); - tree[k].total_count_ = count; - tree[k].value_ = -1; - - tree[k].pool_index_left_ = tree_pool_size - 1; - tree[k].pool_index_right_ = tree_pool_size - 2; - tree_size = tree_size + 1; - } - } - SetBitDepths(&tree[0], tree_pool, bit_depths, 0); - } else if (tree_size == 1) { // Trivial case: only one element. - bit_depths[tree[0].value_] = 1; - } - - { - // Test if this Huffman tree satisfies our 'tree_depth_limit' criteria. - int max_depth = bit_depths[0]; - for (j = 1; j < histogram_size; ++j) { - if (max_depth < bit_depths[j]) { - max_depth = bit_depths[j]; - } - } - if (max_depth <= tree_depth_limit) { - break; - } - } - } -} - -// ----------------------------------------------------------------------------- -// Coding of the Huffman tree values - -static HuffmanTreeToken* CodeRepeatedValues(int repetitions, - HuffmanTreeToken* tokens, - int value, int prev_value) { - assert(value <= MAX_ALLOWED_CODE_LENGTH); - if (value != prev_value) { - tokens->code = value; - tokens->extra_bits = 0; - ++tokens; - --repetitions; - } - while (repetitions >= 1) { - if (repetitions < 3) { - int i; - for (i = 0; i < repetitions; ++i) { - tokens->code = value; - tokens->extra_bits = 0; - ++tokens; - } - break; - } else if (repetitions < 7) { - tokens->code = 16; - tokens->extra_bits = repetitions - 3; - ++tokens; - break; - } else { - tokens->code = 16; - tokens->extra_bits = 3; - ++tokens; - repetitions -= 6; - } - } - return tokens; -} - -static HuffmanTreeToken* CodeRepeatedZeros(int repetitions, - HuffmanTreeToken* tokens) { - while (repetitions >= 1) { - if (repetitions < 3) { - int i; - for (i = 0; i < repetitions; ++i) { - tokens->code = 0; // 0-value - tokens->extra_bits = 0; - ++tokens; - } - break; - } else if (repetitions < 11) { - tokens->code = 17; - tokens->extra_bits = repetitions - 3; - ++tokens; - break; - } else if (repetitions < 139) { - tokens->code = 18; - tokens->extra_bits = repetitions - 11; - ++tokens; - break; - } else { - tokens->code = 18; - tokens->extra_bits = 0x7f; // 138 repeated 0s - ++tokens; - repetitions -= 138; - } - } - return tokens; -} - -int VP8LCreateCompressedHuffmanTree(const HuffmanTreeCode* const tree, - HuffmanTreeToken* tokens, int max_tokens) { - HuffmanTreeToken* const starting_token = tokens; - HuffmanTreeToken* const ending_token = tokens + max_tokens; - const int depth_size = tree->num_symbols; - int prev_value = 8; // 8 is the initial value for rle. - int i = 0; - assert(tokens != NULL); - while (i < depth_size) { - const int value = tree->code_lengths[i]; - int k = i + 1; - int runs; - while (k < depth_size && tree->code_lengths[k] == value) ++k; - runs = k - i; - if (value == 0) { - tokens = CodeRepeatedZeros(runs, tokens); - } else { - tokens = CodeRepeatedValues(runs, tokens, value, prev_value); - prev_value = value; - } - i += runs; - assert(tokens <= ending_token); - } - (void)ending_token; // suppress 'unused variable' warning - return (int)(tokens - starting_token); -} - -// ----------------------------------------------------------------------------- - -// Pre-reversed 4-bit values. -static const uint8_t kReversedBits[16] = { - 0x0, 0x8, 0x4, 0xc, 0x2, 0xa, 0x6, 0xe, - 0x1, 0x9, 0x5, 0xd, 0x3, 0xb, 0x7, 0xf -}; - -static uint32_t ReverseBits(int num_bits, uint32_t bits) { - uint32_t retval = 0; - int i = 0; - while (i < num_bits) { - i += 4; - retval |= kReversedBits[bits & 0xf] << (MAX_ALLOWED_CODE_LENGTH + 1 - i); - bits >>= 4; - } - retval >>= (MAX_ALLOWED_CODE_LENGTH + 1 - num_bits); - return retval; -} - -// Get the actual bit values for a tree of bit depths. -static void ConvertBitDepthsToSymbols(HuffmanTreeCode* const tree) { - // 0 bit-depth means that the symbol does not exist. - int i; - int len; - uint32_t next_code[MAX_ALLOWED_CODE_LENGTH + 1]; - int depth_count[MAX_ALLOWED_CODE_LENGTH + 1] = { 0 }; - - assert(tree != NULL); - len = tree->num_symbols; - for (i = 0; i < len; ++i) { - const int code_length = tree->code_lengths[i]; - assert(code_length <= MAX_ALLOWED_CODE_LENGTH); - ++depth_count[code_length]; - } - depth_count[0] = 0; // ignore unused symbol - next_code[0] = 0; - { - uint32_t code = 0; - for (i = 1; i <= MAX_ALLOWED_CODE_LENGTH; ++i) { - code = (code + depth_count[i - 1]) << 1; - next_code[i] = code; - } - } - for (i = 0; i < len; ++i) { - const int code_length = tree->code_lengths[i]; - tree->codes[i] = ReverseBits(code_length, next_code[code_length]++); - } -} - -// ----------------------------------------------------------------------------- -// Main entry point - -void VP8LCreateHuffmanTree(uint32_t* const histogram, int tree_depth_limit, - uint8_t* const buf_rle, - HuffmanTree* const huff_tree, - HuffmanTreeCode* const huff_code) { - const int num_symbols = huff_code->num_symbols; - memset(buf_rle, 0, num_symbols * sizeof(*buf_rle)); - OptimizeHuffmanForRle(num_symbols, buf_rle, histogram); - GenerateOptimalTree(histogram, num_symbols, huff_tree, tree_depth_limit, - huff_code->code_lengths); - // Create the actual bit codes for the bit lengths. - ConvertBitDepthsToSymbols(huff_code); -} diff --git a/thirdparty/libwebp/utils/huffman_encode.h b/thirdparty/libwebp/utils/huffman_encode.h deleted file mode 100644 index a157165148..0000000000 --- a/thirdparty/libwebp/utils/huffman_encode.h +++ /dev/null @@ -1,60 +0,0 @@ -// Copyright 2011 Google Inc. All Rights Reserved. -// -// Use of this source code is governed by a BSD-style license -// that can be found in the COPYING file in the root of the source -// tree. An additional intellectual property rights grant can be found -// in the file PATENTS. All contributing project authors may -// be found in the AUTHORS file in the root of the source tree. -// ----------------------------------------------------------------------------- -// -// Author: Jyrki Alakuijala (jyrki@google.com) -// -// Entropy encoding (Huffman) for webp lossless - -#ifndef WEBP_UTILS_HUFFMAN_ENCODE_H_ -#define WEBP_UTILS_HUFFMAN_ENCODE_H_ - -#include "../webp/types.h" - -#ifdef __cplusplus -extern "C" { -#endif - -// Struct for holding the tree header in coded form. -typedef struct { - uint8_t code; // value (0..15) or escape code (16,17,18) - uint8_t extra_bits; // extra bits for escape codes -} HuffmanTreeToken; - -// Struct to represent the tree codes (depth and bits array). -typedef struct { - int num_symbols; // Number of symbols. - uint8_t* code_lengths; // Code lengths of the symbols. - uint16_t* codes; // Symbol Codes. -} HuffmanTreeCode; - -// Struct to represent the Huffman tree. -typedef struct { - uint32_t total_count_; // Symbol frequency. - int value_; // Symbol value. - int pool_index_left_; // Index for the left sub-tree. - int pool_index_right_; // Index for the right sub-tree. -} HuffmanTree; - -// Turn the Huffman tree into a token sequence. -// Returns the number of tokens used. -int VP8LCreateCompressedHuffmanTree(const HuffmanTreeCode* const tree, - HuffmanTreeToken* tokens, int max_tokens); - -// Create an optimized tree, and tokenize it. -// 'buf_rle' and 'huff_tree' are pre-allocated and the 'tree' is the constructed -// huffman code tree. -void VP8LCreateHuffmanTree(uint32_t* const histogram, int tree_depth_limit, - uint8_t* const buf_rle, HuffmanTree* const huff_tree, - HuffmanTreeCode* const tree); - -#ifdef __cplusplus -} -#endif - -#endif // WEBP_UTILS_HUFFMAN_ENCODE_H_ diff --git a/thirdparty/libwebp/utils/huffman_encode_utils.c b/thirdparty/libwebp/utils/huffman_encode_utils.c new file mode 100644 index 0000000000..f9504658ea --- /dev/null +++ b/thirdparty/libwebp/utils/huffman_encode_utils.c @@ -0,0 +1,417 @@ +// Copyright 2011 Google Inc. All Rights Reserved. +// +// Use of this source code is governed by a BSD-style license +// that can be found in the COPYING file in the root of the source +// tree. An additional intellectual property rights grant can be found +// in the file PATENTS. All contributing project authors may +// be found in the AUTHORS file in the root of the source tree. +// ----------------------------------------------------------------------------- +// +// Author: Jyrki Alakuijala (jyrki@google.com) +// +// Entropy encoding (Huffman) for webp lossless. + +#include +#include +#include +#include "./huffman_encode_utils.h" +#include "./utils.h" +#include "../webp/format_constants.h" + +// ----------------------------------------------------------------------------- +// Util function to optimize the symbol map for RLE coding + +// Heuristics for selecting the stride ranges to collapse. +static int ValuesShouldBeCollapsedToStrideAverage(int a, int b) { + return abs(a - b) < 4; +} + +// Change the population counts in a way that the consequent +// Huffman tree compression, especially its RLE-part, give smaller output. +static void OptimizeHuffmanForRle(int length, uint8_t* const good_for_rle, + uint32_t* const counts) { + // 1) Let's make the Huffman code more compatible with rle encoding. + int i; + for (; length >= 0; --length) { + if (length == 0) { + return; // All zeros. + } + if (counts[length - 1] != 0) { + // Now counts[0..length - 1] does not have trailing zeros. + break; + } + } + // 2) Let's mark all population counts that already can be encoded + // with an rle code. + { + // Let's not spoil any of the existing good rle codes. + // Mark any seq of 0's that is longer as 5 as a good_for_rle. + // Mark any seq of non-0's that is longer as 7 as a good_for_rle. + uint32_t symbol = counts[0]; + int stride = 0; + for (i = 0; i < length + 1; ++i) { + if (i == length || counts[i] != symbol) { + if ((symbol == 0 && stride >= 5) || + (symbol != 0 && stride >= 7)) { + int k; + for (k = 0; k < stride; ++k) { + good_for_rle[i - k - 1] = 1; + } + } + stride = 1; + if (i != length) { + symbol = counts[i]; + } + } else { + ++stride; + } + } + } + // 3) Let's replace those population counts that lead to more rle codes. + { + uint32_t stride = 0; + uint32_t limit = counts[0]; + uint32_t sum = 0; + for (i = 0; i < length + 1; ++i) { + if (i == length || good_for_rle[i] || + (i != 0 && good_for_rle[i - 1]) || + !ValuesShouldBeCollapsedToStrideAverage(counts[i], limit)) { + if (stride >= 4 || (stride >= 3 && sum == 0)) { + uint32_t k; + // The stride must end, collapse what we have, if we have enough (4). + uint32_t count = (sum + stride / 2) / stride; + if (count < 1) { + count = 1; + } + if (sum == 0) { + // Don't make an all zeros stride to be upgraded to ones. + count = 0; + } + for (k = 0; k < stride; ++k) { + // We don't want to change value at counts[i], + // that is already belonging to the next stride. Thus - 1. + counts[i - k - 1] = count; + } + } + stride = 0; + sum = 0; + if (i < length - 3) { + // All interesting strides have a count of at least 4, + // at least when non-zeros. + limit = (counts[i] + counts[i + 1] + + counts[i + 2] + counts[i + 3] + 2) / 4; + } else if (i < length) { + limit = counts[i]; + } else { + limit = 0; + } + } + ++stride; + if (i != length) { + sum += counts[i]; + if (stride >= 4) { + limit = (sum + stride / 2) / stride; + } + } + } + } +} + +// A comparer function for two Huffman trees: sorts first by 'total count' +// (more comes first), and then by 'value' (more comes first). +static int CompareHuffmanTrees(const void* ptr1, const void* ptr2) { + const HuffmanTree* const t1 = (const HuffmanTree*)ptr1; + const HuffmanTree* const t2 = (const HuffmanTree*)ptr2; + if (t1->total_count_ > t2->total_count_) { + return -1; + } else if (t1->total_count_ < t2->total_count_) { + return 1; + } else { + assert(t1->value_ != t2->value_); + return (t1->value_ < t2->value_) ? -1 : 1; + } +} + +static void SetBitDepths(const HuffmanTree* const tree, + const HuffmanTree* const pool, + uint8_t* const bit_depths, int level) { + if (tree->pool_index_left_ >= 0) { + SetBitDepths(&pool[tree->pool_index_left_], pool, bit_depths, level + 1); + SetBitDepths(&pool[tree->pool_index_right_], pool, bit_depths, level + 1); + } else { + bit_depths[tree->value_] = level; + } +} + +// Create an optimal Huffman tree. +// +// (data,length): population counts. +// tree_limit: maximum bit depth (inclusive) of the codes. +// bit_depths[]: how many bits are used for the symbol. +// +// Returns 0 when an error has occurred. +// +// The catch here is that the tree cannot be arbitrarily deep +// +// count_limit is the value that is to be faked as the minimum value +// and this minimum value is raised until the tree matches the +// maximum length requirement. +// +// This algorithm is not of excellent performance for very long data blocks, +// especially when population counts are longer than 2**tree_limit, but +// we are not planning to use this with extremely long blocks. +// +// See http://en.wikipedia.org/wiki/Huffman_coding +static void GenerateOptimalTree(const uint32_t* const histogram, + int histogram_size, + HuffmanTree* tree, int tree_depth_limit, + uint8_t* const bit_depths) { + uint32_t count_min; + HuffmanTree* tree_pool; + int tree_size_orig = 0; + int i; + + for (i = 0; i < histogram_size; ++i) { + if (histogram[i] != 0) { + ++tree_size_orig; + } + } + + if (tree_size_orig == 0) { // pretty optimal already! + return; + } + + tree_pool = tree + tree_size_orig; + + // For block sizes with less than 64k symbols we never need to do a + // second iteration of this loop. + // If we actually start running inside this loop a lot, we would perhaps + // be better off with the Katajainen algorithm. + assert(tree_size_orig <= (1 << (tree_depth_limit - 1))); + for (count_min = 1; ; count_min *= 2) { + int tree_size = tree_size_orig; + // We need to pack the Huffman tree in tree_depth_limit bits. + // So, we try by faking histogram entries to be at least 'count_min'. + int idx = 0; + int j; + for (j = 0; j < histogram_size; ++j) { + if (histogram[j] != 0) { + const uint32_t count = + (histogram[j] < count_min) ? count_min : histogram[j]; + tree[idx].total_count_ = count; + tree[idx].value_ = j; + tree[idx].pool_index_left_ = -1; + tree[idx].pool_index_right_ = -1; + ++idx; + } + } + + // Build the Huffman tree. + qsort(tree, tree_size, sizeof(*tree), CompareHuffmanTrees); + + if (tree_size > 1) { // Normal case. + int tree_pool_size = 0; + while (tree_size > 1) { // Finish when we have only one root. + uint32_t count; + tree_pool[tree_pool_size++] = tree[tree_size - 1]; + tree_pool[tree_pool_size++] = tree[tree_size - 2]; + count = tree_pool[tree_pool_size - 1].total_count_ + + tree_pool[tree_pool_size - 2].total_count_; + tree_size -= 2; + { + // Search for the insertion point. + int k; + for (k = 0; k < tree_size; ++k) { + if (tree[k].total_count_ <= count) { + break; + } + } + memmove(tree + (k + 1), tree + k, (tree_size - k) * sizeof(*tree)); + tree[k].total_count_ = count; + tree[k].value_ = -1; + + tree[k].pool_index_left_ = tree_pool_size - 1; + tree[k].pool_index_right_ = tree_pool_size - 2; + tree_size = tree_size + 1; + } + } + SetBitDepths(&tree[0], tree_pool, bit_depths, 0); + } else if (tree_size == 1) { // Trivial case: only one element. + bit_depths[tree[0].value_] = 1; + } + + { + // Test if this Huffman tree satisfies our 'tree_depth_limit' criteria. + int max_depth = bit_depths[0]; + for (j = 1; j < histogram_size; ++j) { + if (max_depth < bit_depths[j]) { + max_depth = bit_depths[j]; + } + } + if (max_depth <= tree_depth_limit) { + break; + } + } + } +} + +// ----------------------------------------------------------------------------- +// Coding of the Huffman tree values + +static HuffmanTreeToken* CodeRepeatedValues(int repetitions, + HuffmanTreeToken* tokens, + int value, int prev_value) { + assert(value <= MAX_ALLOWED_CODE_LENGTH); + if (value != prev_value) { + tokens->code = value; + tokens->extra_bits = 0; + ++tokens; + --repetitions; + } + while (repetitions >= 1) { + if (repetitions < 3) { + int i; + for (i = 0; i < repetitions; ++i) { + tokens->code = value; + tokens->extra_bits = 0; + ++tokens; + } + break; + } else if (repetitions < 7) { + tokens->code = 16; + tokens->extra_bits = repetitions - 3; + ++tokens; + break; + } else { + tokens->code = 16; + tokens->extra_bits = 3; + ++tokens; + repetitions -= 6; + } + } + return tokens; +} + +static HuffmanTreeToken* CodeRepeatedZeros(int repetitions, + HuffmanTreeToken* tokens) { + while (repetitions >= 1) { + if (repetitions < 3) { + int i; + for (i = 0; i < repetitions; ++i) { + tokens->code = 0; // 0-value + tokens->extra_bits = 0; + ++tokens; + } + break; + } else if (repetitions < 11) { + tokens->code = 17; + tokens->extra_bits = repetitions - 3; + ++tokens; + break; + } else if (repetitions < 139) { + tokens->code = 18; + tokens->extra_bits = repetitions - 11; + ++tokens; + break; + } else { + tokens->code = 18; + tokens->extra_bits = 0x7f; // 138 repeated 0s + ++tokens; + repetitions -= 138; + } + } + return tokens; +} + +int VP8LCreateCompressedHuffmanTree(const HuffmanTreeCode* const tree, + HuffmanTreeToken* tokens, int max_tokens) { + HuffmanTreeToken* const starting_token = tokens; + HuffmanTreeToken* const ending_token = tokens + max_tokens; + const int depth_size = tree->num_symbols; + int prev_value = 8; // 8 is the initial value for rle. + int i = 0; + assert(tokens != NULL); + while (i < depth_size) { + const int value = tree->code_lengths[i]; + int k = i + 1; + int runs; + while (k < depth_size && tree->code_lengths[k] == value) ++k; + runs = k - i; + if (value == 0) { + tokens = CodeRepeatedZeros(runs, tokens); + } else { + tokens = CodeRepeatedValues(runs, tokens, value, prev_value); + prev_value = value; + } + i += runs; + assert(tokens <= ending_token); + } + (void)ending_token; // suppress 'unused variable' warning + return (int)(tokens - starting_token); +} + +// ----------------------------------------------------------------------------- + +// Pre-reversed 4-bit values. +static const uint8_t kReversedBits[16] = { + 0x0, 0x8, 0x4, 0xc, 0x2, 0xa, 0x6, 0xe, + 0x1, 0x9, 0x5, 0xd, 0x3, 0xb, 0x7, 0xf +}; + +static uint32_t ReverseBits(int num_bits, uint32_t bits) { + uint32_t retval = 0; + int i = 0; + while (i < num_bits) { + i += 4; + retval |= kReversedBits[bits & 0xf] << (MAX_ALLOWED_CODE_LENGTH + 1 - i); + bits >>= 4; + } + retval >>= (MAX_ALLOWED_CODE_LENGTH + 1 - num_bits); + return retval; +} + +// Get the actual bit values for a tree of bit depths. +static void ConvertBitDepthsToSymbols(HuffmanTreeCode* const tree) { + // 0 bit-depth means that the symbol does not exist. + int i; + int len; + uint32_t next_code[MAX_ALLOWED_CODE_LENGTH + 1]; + int depth_count[MAX_ALLOWED_CODE_LENGTH + 1] = { 0 }; + + assert(tree != NULL); + len = tree->num_symbols; + for (i = 0; i < len; ++i) { + const int code_length = tree->code_lengths[i]; + assert(code_length <= MAX_ALLOWED_CODE_LENGTH); + ++depth_count[code_length]; + } + depth_count[0] = 0; // ignore unused symbol + next_code[0] = 0; + { + uint32_t code = 0; + for (i = 1; i <= MAX_ALLOWED_CODE_LENGTH; ++i) { + code = (code + depth_count[i - 1]) << 1; + next_code[i] = code; + } + } + for (i = 0; i < len; ++i) { + const int code_length = tree->code_lengths[i]; + tree->codes[i] = ReverseBits(code_length, next_code[code_length]++); + } +} + +// ----------------------------------------------------------------------------- +// Main entry point + +void VP8LCreateHuffmanTree(uint32_t* const histogram, int tree_depth_limit, + uint8_t* const buf_rle, + HuffmanTree* const huff_tree, + HuffmanTreeCode* const huff_code) { + const int num_symbols = huff_code->num_symbols; + memset(buf_rle, 0, num_symbols * sizeof(*buf_rle)); + OptimizeHuffmanForRle(num_symbols, buf_rle, histogram); + GenerateOptimalTree(histogram, num_symbols, huff_tree, tree_depth_limit, + huff_code->code_lengths); + // Create the actual bit codes for the bit lengths. + ConvertBitDepthsToSymbols(huff_code); +} diff --git a/thirdparty/libwebp/utils/huffman_encode_utils.h b/thirdparty/libwebp/utils/huffman_encode_utils.h new file mode 100644 index 0000000000..a157165148 --- /dev/null +++ b/thirdparty/libwebp/utils/huffman_encode_utils.h @@ -0,0 +1,60 @@ +// Copyright 2011 Google Inc. All Rights Reserved. +// +// Use of this source code is governed by a BSD-style license +// that can be found in the COPYING file in the root of the source +// tree. An additional intellectual property rights grant can be found +// in the file PATENTS. All contributing project authors may +// be found in the AUTHORS file in the root of the source tree. +// ----------------------------------------------------------------------------- +// +// Author: Jyrki Alakuijala (jyrki@google.com) +// +// Entropy encoding (Huffman) for webp lossless + +#ifndef WEBP_UTILS_HUFFMAN_ENCODE_H_ +#define WEBP_UTILS_HUFFMAN_ENCODE_H_ + +#include "../webp/types.h" + +#ifdef __cplusplus +extern "C" { +#endif + +// Struct for holding the tree header in coded form. +typedef struct { + uint8_t code; // value (0..15) or escape code (16,17,18) + uint8_t extra_bits; // extra bits for escape codes +} HuffmanTreeToken; + +// Struct to represent the tree codes (depth and bits array). +typedef struct { + int num_symbols; // Number of symbols. + uint8_t* code_lengths; // Code lengths of the symbols. + uint16_t* codes; // Symbol Codes. +} HuffmanTreeCode; + +// Struct to represent the Huffman tree. +typedef struct { + uint32_t total_count_; // Symbol frequency. + int value_; // Symbol value. + int pool_index_left_; // Index for the left sub-tree. + int pool_index_right_; // Index for the right sub-tree. +} HuffmanTree; + +// Turn the Huffman tree into a token sequence. +// Returns the number of tokens used. +int VP8LCreateCompressedHuffmanTree(const HuffmanTreeCode* const tree, + HuffmanTreeToken* tokens, int max_tokens); + +// Create an optimized tree, and tokenize it. +// 'buf_rle' and 'huff_tree' are pre-allocated and the 'tree' is the constructed +// huffman code tree. +void VP8LCreateHuffmanTree(uint32_t* const histogram, int tree_depth_limit, + uint8_t* const buf_rle, HuffmanTree* const huff_tree, + HuffmanTreeCode* const tree); + +#ifdef __cplusplus +} +#endif + +#endif // WEBP_UTILS_HUFFMAN_ENCODE_H_ diff --git a/thirdparty/libwebp/utils/huffman_utils.c b/thirdparty/libwebp/utils/huffman_utils.c new file mode 100644 index 0000000000..008b5d746f --- /dev/null +++ b/thirdparty/libwebp/utils/huffman_utils.c @@ -0,0 +1,223 @@ +// Copyright 2012 Google Inc. All Rights Reserved. +// +// Use of this source code is governed by a BSD-style license +// that can be found in the COPYING file in the root of the source +// tree. An additional intellectual property rights grant can be found +// in the file PATENTS. All contributing project authors may +// be found in the AUTHORS file in the root of the source tree. +// ----------------------------------------------------------------------------- +// +// Utilities for building and looking up Huffman trees. +// +// Author: Urvang Joshi (urvang@google.com) + +#include +#include +#include +#include "./huffman_utils.h" +#include "./utils.h" +#include "../webp/format_constants.h" + +// Huffman data read via DecodeImageStream is represented in two (red and green) +// bytes. +#define MAX_HTREE_GROUPS 0x10000 + +HTreeGroup* VP8LHtreeGroupsNew(int num_htree_groups) { + HTreeGroup* const htree_groups = + (HTreeGroup*)WebPSafeMalloc(num_htree_groups, sizeof(*htree_groups)); + if (htree_groups == NULL) { + return NULL; + } + assert(num_htree_groups <= MAX_HTREE_GROUPS); + return htree_groups; +} + +void VP8LHtreeGroupsFree(HTreeGroup* const htree_groups) { + if (htree_groups != NULL) { + WebPSafeFree(htree_groups); + } +} + +// Returns reverse(reverse(key, len) + 1, len), where reverse(key, len) is the +// bit-wise reversal of the len least significant bits of key. +static WEBP_INLINE uint32_t GetNextKey(uint32_t key, int len) { + uint32_t step = 1 << (len - 1); + while (key & step) { + step >>= 1; + } + return step ? (key & (step - 1)) + step : key; +} + +// Stores code in table[0], table[step], table[2*step], ..., table[end]. +// Assumes that end is an integer multiple of step. +static WEBP_INLINE void ReplicateValue(HuffmanCode* table, + int step, int end, + HuffmanCode code) { + assert(end % step == 0); + do { + end -= step; + table[end] = code; + } while (end > 0); +} + +// Returns the table width of the next 2nd level table. count is the histogram +// of bit lengths for the remaining symbols, len is the code length of the next +// processed symbol +static WEBP_INLINE int NextTableBitSize(const int* const count, + int len, int root_bits) { + int left = 1 << (len - root_bits); + while (len < MAX_ALLOWED_CODE_LENGTH) { + left -= count[len]; + if (left <= 0) break; + ++len; + left <<= 1; + } + return len - root_bits; +} + +// sorted[code_lengths_size] is a pre-allocated array for sorting symbols +// by code length. +static int BuildHuffmanTable(HuffmanCode* const root_table, int root_bits, + const int code_lengths[], int code_lengths_size, + uint16_t sorted[]) { + HuffmanCode* table = root_table; // next available space in table + int total_size = 1 << root_bits; // total size root table + 2nd level table + int len; // current code length + int symbol; // symbol index in original or sorted table + // number of codes of each length: + int count[MAX_ALLOWED_CODE_LENGTH + 1] = { 0 }; + // offsets in sorted table for each length: + int offset[MAX_ALLOWED_CODE_LENGTH + 1]; + + assert(code_lengths_size != 0); + assert(code_lengths != NULL); + assert(root_table != NULL); + assert(root_bits > 0); + + // Build histogram of code lengths. + for (symbol = 0; symbol < code_lengths_size; ++symbol) { + if (code_lengths[symbol] > MAX_ALLOWED_CODE_LENGTH) { + return 0; + } + ++count[code_lengths[symbol]]; + } + + // Error, all code lengths are zeros. + if (count[0] == code_lengths_size) { + return 0; + } + + // Generate offsets into sorted symbol table by code length. + offset[1] = 0; + for (len = 1; len < MAX_ALLOWED_CODE_LENGTH; ++len) { + if (count[len] > (1 << len)) { + return 0; + } + offset[len + 1] = offset[len] + count[len]; + } + + // Sort symbols by length, by symbol order within each length. + for (symbol = 0; symbol < code_lengths_size; ++symbol) { + const int symbol_code_length = code_lengths[symbol]; + if (code_lengths[symbol] > 0) { + sorted[offset[symbol_code_length]++] = symbol; + } + } + + // Special case code with only one value. + if (offset[MAX_ALLOWED_CODE_LENGTH] == 1) { + HuffmanCode code; + code.bits = 0; + code.value = (uint16_t)sorted[0]; + ReplicateValue(table, 1, total_size, code); + return total_size; + } + + { + int step; // step size to replicate values in current table + uint32_t low = -1; // low bits for current root entry + uint32_t mask = total_size - 1; // mask for low bits + uint32_t key = 0; // reversed prefix code + int num_nodes = 1; // number of Huffman tree nodes + int num_open = 1; // number of open branches in current tree level + int table_bits = root_bits; // key length of current table + int table_size = 1 << table_bits; // size of current table + symbol = 0; + // Fill in root table. + for (len = 1, step = 2; len <= root_bits; ++len, step <<= 1) { + num_open <<= 1; + num_nodes += num_open; + num_open -= count[len]; + if (num_open < 0) { + return 0; + } + for (; count[len] > 0; --count[len]) { + HuffmanCode code; + code.bits = (uint8_t)len; + code.value = (uint16_t)sorted[symbol++]; + ReplicateValue(&table[key], step, table_size, code); + key = GetNextKey(key, len); + } + } + + // Fill in 2nd level tables and add pointers to root table. + for (len = root_bits + 1, step = 2; len <= MAX_ALLOWED_CODE_LENGTH; + ++len, step <<= 1) { + num_open <<= 1; + num_nodes += num_open; + num_open -= count[len]; + if (num_open < 0) { + return 0; + } + for (; count[len] > 0; --count[len]) { + HuffmanCode code; + if ((key & mask) != low) { + table += table_size; + table_bits = NextTableBitSize(count, len, root_bits); + table_size = 1 << table_bits; + total_size += table_size; + low = key & mask; + root_table[low].bits = (uint8_t)(table_bits + root_bits); + root_table[low].value = (uint16_t)((table - root_table) - low); + } + code.bits = (uint8_t)(len - root_bits); + code.value = (uint16_t)sorted[symbol++]; + ReplicateValue(&table[key >> root_bits], step, table_size, code); + key = GetNextKey(key, len); + } + } + + // Check if tree is full. + if (num_nodes != 2 * offset[MAX_ALLOWED_CODE_LENGTH] - 1) { + return 0; + } + } + + return total_size; +} + +// Maximum code_lengths_size is 2328 (reached for 11-bit color_cache_bits). +// More commonly, the value is around ~280. +#define MAX_CODE_LENGTHS_SIZE \ + ((1 << MAX_CACHE_BITS) + NUM_LITERAL_CODES + NUM_LENGTH_CODES) +// Cut-off value for switching between heap and stack allocation. +#define SORTED_SIZE_CUTOFF 512 +int VP8LBuildHuffmanTable(HuffmanCode* const root_table, int root_bits, + const int code_lengths[], int code_lengths_size) { + int total_size; + assert(code_lengths_size <= MAX_CODE_LENGTHS_SIZE); + if (code_lengths_size <= SORTED_SIZE_CUTOFF) { + // use local stack-allocated array. + uint16_t sorted[SORTED_SIZE_CUTOFF]; + total_size = BuildHuffmanTable(root_table, root_bits, + code_lengths, code_lengths_size, sorted); + } else { // rare case. Use heap allocation. + uint16_t* const sorted = + (uint16_t*)WebPSafeMalloc(code_lengths_size, sizeof(*sorted)); + if (sorted == NULL) return 0; + total_size = BuildHuffmanTable(root_table, root_bits, + code_lengths, code_lengths_size, sorted); + WebPSafeFree(sorted); + } + return total_size; +} diff --git a/thirdparty/libwebp/utils/huffman_utils.h b/thirdparty/libwebp/utils/huffman_utils.h new file mode 100644 index 0000000000..c6dd6aaa45 --- /dev/null +++ b/thirdparty/libwebp/utils/huffman_utils.h @@ -0,0 +1,88 @@ +// Copyright 2012 Google Inc. All Rights Reserved. +// +// Use of this source code is governed by a BSD-style license +// that can be found in the COPYING file in the root of the source +// tree. An additional intellectual property rights grant can be found +// in the file PATENTS. All contributing project authors may +// be found in the AUTHORS file in the root of the source tree. +// ----------------------------------------------------------------------------- +// +// Utilities for building and looking up Huffman trees. +// +// Author: Urvang Joshi (urvang@google.com) + +#ifndef WEBP_UTILS_HUFFMAN_H_ +#define WEBP_UTILS_HUFFMAN_H_ + +#include +#include "../webp/format_constants.h" +#include "../webp/types.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#define HUFFMAN_TABLE_BITS 8 +#define HUFFMAN_TABLE_MASK ((1 << HUFFMAN_TABLE_BITS) - 1) + +#define LENGTHS_TABLE_BITS 7 +#define LENGTHS_TABLE_MASK ((1 << LENGTHS_TABLE_BITS) - 1) + + +// Huffman lookup table entry +typedef struct { + uint8_t bits; // number of bits used for this symbol + uint16_t value; // symbol value or table offset +} HuffmanCode; + +// long version for holding 32b values +typedef struct { + int bits; // number of bits used for this symbol, + // or an impossible value if not a literal code. + uint32_t value; // 32b packed ARGB value if literal, + // or non-literal symbol otherwise +} HuffmanCode32; + +#define HUFFMAN_PACKED_BITS 6 +#define HUFFMAN_PACKED_TABLE_SIZE (1u << HUFFMAN_PACKED_BITS) + +// Huffman table group. +// Includes special handling for the following cases: +// - is_trivial_literal: one common literal base for RED/BLUE/ALPHA (not GREEN) +// - is_trivial_code: only 1 code (no bit is read from bitstream) +// - use_packed_table: few enough literal symbols, so all the bit codes +// can fit into a small look-up table packed_table[] +// The common literal base, if applicable, is stored in 'literal_arb'. +typedef struct HTreeGroup HTreeGroup; +struct HTreeGroup { + HuffmanCode* htrees[HUFFMAN_CODES_PER_META_CODE]; + int is_trivial_literal; // True, if huffman trees for Red, Blue & Alpha + // Symbols are trivial (have a single code). + uint32_t literal_arb; // If is_trivial_literal is true, this is the + // ARGB value of the pixel, with Green channel + // being set to zero. + int is_trivial_code; // true if is_trivial_literal with only one code + int use_packed_table; // use packed table below for short literal code + // table mapping input bits to a packed values, or escape case to literal code + HuffmanCode32 packed_table[HUFFMAN_PACKED_TABLE_SIZE]; +}; + +// Creates the instance of HTreeGroup with specified number of tree-groups. +HTreeGroup* VP8LHtreeGroupsNew(int num_htree_groups); + +// Releases the memory allocated for HTreeGroup. +void VP8LHtreeGroupsFree(HTreeGroup* const htree_groups); + +// Builds Huffman lookup table assuming code lengths are in symbol order. +// The 'code_lengths' is pre-allocated temporary memory buffer used for creating +// the huffman table. +// Returns built table size or 0 in case of error (invalid tree or +// memory error). +int VP8LBuildHuffmanTable(HuffmanCode* const root_table, int root_bits, + const int code_lengths[], int code_lengths_size); + +#ifdef __cplusplus +} // extern "C" +#endif + +#endif // WEBP_UTILS_HUFFMAN_H_ diff --git a/thirdparty/libwebp/utils/quant_levels.c b/thirdparty/libwebp/utils/quant_levels.c deleted file mode 100644 index d7c8aab922..0000000000 --- a/thirdparty/libwebp/utils/quant_levels.c +++ /dev/null @@ -1,140 +0,0 @@ -// Copyright 2011 Google Inc. All Rights Reserved. -// -// Use of this source code is governed by a BSD-style license -// that can be found in the COPYING file in the root of the source -// tree. An additional intellectual property rights grant can be found -// in the file PATENTS. All contributing project authors may -// be found in the AUTHORS file in the root of the source tree. -// ----------------------------------------------------------------------------- -// -// Quantize levels for specified number of quantization-levels ([2, 256]). -// Min and max values are preserved (usual 0 and 255 for alpha plane). -// -// Author: Skal (pascal.massimino@gmail.com) - -#include - -#include "./quant_levels.h" - -#define NUM_SYMBOLS 256 - -#define MAX_ITER 6 // Maximum number of convergence steps. -#define ERROR_THRESHOLD 1e-4 // MSE stopping criterion. - -// ----------------------------------------------------------------------------- -// Quantize levels. - -int QuantizeLevels(uint8_t* const data, int width, int height, - int num_levels, uint64_t* const sse) { - int freq[NUM_SYMBOLS] = { 0 }; - int q_level[NUM_SYMBOLS] = { 0 }; - double inv_q_level[NUM_SYMBOLS] = { 0 }; - int min_s = 255, max_s = 0; - const size_t data_size = height * width; - int i, num_levels_in, iter; - double last_err = 1.e38, err = 0.; - const double err_threshold = ERROR_THRESHOLD * data_size; - - if (data == NULL) { - return 0; - } - - if (width <= 0 || height <= 0) { - return 0; - } - - if (num_levels < 2 || num_levels > 256) { - return 0; - } - - { - size_t n; - num_levels_in = 0; - for (n = 0; n < data_size; ++n) { - num_levels_in += (freq[data[n]] == 0); - if (min_s > data[n]) min_s = data[n]; - if (max_s < data[n]) max_s = data[n]; - ++freq[data[n]]; - } - } - - if (num_levels_in <= num_levels) goto End; // nothing to do! - - // Start with uniformly spread centroids. - for (i = 0; i < num_levels; ++i) { - inv_q_level[i] = min_s + (double)(max_s - min_s) * i / (num_levels - 1); - } - - // Fixed values. Won't be changed. - q_level[min_s] = 0; - q_level[max_s] = num_levels - 1; - assert(inv_q_level[0] == min_s); - assert(inv_q_level[num_levels - 1] == max_s); - - // k-Means iterations. - for (iter = 0; iter < MAX_ITER; ++iter) { - double q_sum[NUM_SYMBOLS] = { 0 }; - double q_count[NUM_SYMBOLS] = { 0 }; - int s, slot = 0; - - // Assign classes to representatives. - for (s = min_s; s <= max_s; ++s) { - // Keep track of the nearest neighbour 'slot' - while (slot < num_levels - 1 && - 2 * s > inv_q_level[slot] + inv_q_level[slot + 1]) { - ++slot; - } - if (freq[s] > 0) { - q_sum[slot] += s * freq[s]; - q_count[slot] += freq[s]; - } - q_level[s] = slot; - } - - // Assign new representatives to classes. - if (num_levels > 2) { - for (slot = 1; slot < num_levels - 1; ++slot) { - const double count = q_count[slot]; - if (count > 0.) { - inv_q_level[slot] = q_sum[slot] / count; - } - } - } - - // Compute convergence error. - err = 0.; - for (s = min_s; s <= max_s; ++s) { - const double error = s - inv_q_level[q_level[s]]; - err += freq[s] * error * error; - } - - // Check for convergence: we stop as soon as the error is no - // longer improving. - if (last_err - err < err_threshold) break; - last_err = err; - } - - // Remap the alpha plane to quantized values. - { - // double->int rounding operation can be costly, so we do it - // once for all before remapping. We also perform the data[] -> slot - // mapping, while at it (avoid one indirection in the final loop). - uint8_t map[NUM_SYMBOLS]; - int s; - size_t n; - for (s = min_s; s <= max_s; ++s) { - const int slot = q_level[s]; - map[s] = (uint8_t)(inv_q_level[slot] + .5); - } - // Final pass. - for (n = 0; n < data_size; ++n) { - data[n] = map[data[n]]; - } - } - End: - // Store sum of squared error if needed. - if (sse != NULL) *sse = (uint64_t)err; - - return 1; -} - diff --git a/thirdparty/libwebp/utils/quant_levels.h b/thirdparty/libwebp/utils/quant_levels.h deleted file mode 100644 index 1cb5a32cae..0000000000 --- a/thirdparty/libwebp/utils/quant_levels.h +++ /dev/null @@ -1,36 +0,0 @@ -// Copyright 2011 Google Inc. All Rights Reserved. -// -// Use of this source code is governed by a BSD-style license -// that can be found in the COPYING file in the root of the source -// tree. An additional intellectual property rights grant can be found -// in the file PATENTS. All contributing project authors may -// be found in the AUTHORS file in the root of the source tree. -// ----------------------------------------------------------------------------- -// -// Alpha plane quantization utility -// -// Author: Vikas Arora (vikasa@google.com) - -#ifndef WEBP_UTILS_QUANT_LEVELS_H_ -#define WEBP_UTILS_QUANT_LEVELS_H_ - -#include - -#include "../webp/types.h" - -#ifdef __cplusplus -extern "C" { -#endif - -// Replace the input 'data' of size 'width'x'height' with 'num-levels' -// quantized values. If not NULL, 'sse' will contain the sum of squared error. -// Valid range for 'num_levels' is [2, 256]. -// Returns false in case of error (data is NULL, or parameters are invalid). -int QuantizeLevels(uint8_t* const data, int width, int height, int num_levels, - uint64_t* const sse); - -#ifdef __cplusplus -} // extern "C" -#endif - -#endif /* WEBP_UTILS_QUANT_LEVELS_H_ */ diff --git a/thirdparty/libwebp/utils/quant_levels_dec.c b/thirdparty/libwebp/utils/quant_levels_dec.c deleted file mode 100644 index ee0a3fe127..0000000000 --- a/thirdparty/libwebp/utils/quant_levels_dec.c +++ /dev/null @@ -1,284 +0,0 @@ -// Copyright 2013 Google Inc. All Rights Reserved. -// -// Use of this source code is governed by a BSD-style license -// that can be found in the COPYING file in the root of the source -// tree. An additional intellectual property rights grant can be found -// in the file PATENTS. All contributing project authors may -// be found in the AUTHORS file in the root of the source tree. -// ----------------------------------------------------------------------------- -// -// Implement gradient smoothing: we replace a current alpha value by its -// surrounding average if it's close enough (that is: the change will be less -// than the minimum distance between two quantized level). -// We use sliding window for computing the 2d moving average. -// -// Author: Skal (pascal.massimino@gmail.com) - -#include "./quant_levels_dec.h" - -#include // for memset - -#include "./utils.h" - -// #define USE_DITHERING // uncomment to enable ordered dithering (not vital) - -#define FIX 16 // fix-point precision for averaging -#define LFIX 2 // extra precision for look-up table -#define LUT_SIZE ((1 << (8 + LFIX)) - 1) // look-up table size - -#if defined(USE_DITHERING) - -#define DFIX 4 // extra precision for ordered dithering -#define DSIZE 4 // dithering size (must be a power of two) -// cf. http://en.wikipedia.org/wiki/Ordered_dithering -static const uint8_t kOrderedDither[DSIZE][DSIZE] = { - { 0, 8, 2, 10 }, // coefficients are in DFIX fixed-point precision - { 12, 4, 14, 6 }, - { 3, 11, 1, 9 }, - { 15, 7, 13, 5 } -}; - -#else -#define DFIX 0 -#endif - -typedef struct { - int width_, height_; // dimension - int stride_; // stride in bytes - int row_; // current input row being processed - uint8_t* src_; // input pointer - uint8_t* dst_; // output pointer - - int radius_; // filter radius (=delay) - int scale_; // normalization factor, in FIX bits precision - - void* mem_; // all memory - - // various scratch buffers - uint16_t* start_; - uint16_t* cur_; - uint16_t* end_; - uint16_t* top_; - uint16_t* average_; - - // input levels distribution - int num_levels_; // number of quantized levels - int min_, max_; // min and max level values - int min_level_dist_; // smallest distance between two consecutive levels - - int16_t* correction_; // size = 1 + 2*LUT_SIZE -> ~4k memory -} SmoothParams; - -//------------------------------------------------------------------------------ - -#define CLIP_MASK (int)(~0U << (8 + DFIX)) -static WEBP_INLINE uint8_t clip_8b(int v) { - return (!(v & CLIP_MASK)) ? (uint8_t)(v >> DFIX) : (v < 0) ? 0u : 255u; -} - -// vertical accumulation -static void VFilter(SmoothParams* const p) { - const uint8_t* src = p->src_; - const int w = p->width_; - uint16_t* const cur = p->cur_; - const uint16_t* const top = p->top_; - uint16_t* const out = p->end_; - uint16_t sum = 0; // all arithmetic is modulo 16bit - int x; - - for (x = 0; x < w; ++x) { - uint16_t new_value; - sum += src[x]; - new_value = top[x] + sum; - out[x] = new_value - cur[x]; // vertical sum of 'r' pixels. - cur[x] = new_value; - } - // move input pointers one row down - p->top_ = p->cur_; - p->cur_ += w; - if (p->cur_ == p->end_) p->cur_ = p->start_; // roll-over - // We replicate edges, as it's somewhat easier as a boundary condition. - // That's why we don't update the 'src' pointer on top/bottom area: - if (p->row_ >= 0 && p->row_ < p->height_ - 1) { - p->src_ += p->stride_; - } -} - -// horizontal accumulation. We use mirror replication of missing pixels, as it's -// a little easier to implement (surprisingly). -static void HFilter(SmoothParams* const p) { - const uint16_t* const in = p->end_; - uint16_t* const out = p->average_; - const uint32_t scale = p->scale_; - const int w = p->width_; - const int r = p->radius_; - - int x; - for (x = 0; x <= r; ++x) { // left mirroring - const uint16_t delta = in[x + r - 1] + in[r - x]; - out[x] = (delta * scale) >> FIX; - } - for (; x < w - r; ++x) { // bulk middle run - const uint16_t delta = in[x + r] - in[x - r - 1]; - out[x] = (delta * scale) >> FIX; - } - for (; x < w; ++x) { // right mirroring - const uint16_t delta = - 2 * in[w - 1] - in[2 * w - 2 - r - x] - in[x - r - 1]; - out[x] = (delta * scale) >> FIX; - } -} - -// emit one filtered output row -static void ApplyFilter(SmoothParams* const p) { - const uint16_t* const average = p->average_; - const int w = p->width_; - const int16_t* const correction = p->correction_; -#if defined(USE_DITHERING) - const uint8_t* const dither = kOrderedDither[p->row_ % DSIZE]; -#endif - uint8_t* const dst = p->dst_; - int x; - for (x = 0; x < w; ++x) { - const int v = dst[x]; - if (v < p->max_ && v > p->min_) { - const int c = (v << DFIX) + correction[average[x] - (v << LFIX)]; -#if defined(USE_DITHERING) - dst[x] = clip_8b(c + dither[x % DSIZE]); -#else - dst[x] = clip_8b(c); -#endif - } - } - p->dst_ += p->stride_; // advance output pointer -} - -//------------------------------------------------------------------------------ -// Initialize correction table - -static void InitCorrectionLUT(int16_t* const lut, int min_dist) { - // The correction curve is: - // f(x) = x for x <= threshold2 - // f(x) = 0 for x >= threshold1 - // and a linear interpolation for range x=[threshold2, threshold1] - // (along with f(-x) = -f(x) symmetry). - // Note that: threshold2 = 3/4 * threshold1 - const int threshold1 = min_dist << LFIX; - const int threshold2 = (3 * threshold1) >> 2; - const int max_threshold = threshold2 << DFIX; - const int delta = threshold1 - threshold2; - int i; - for (i = 1; i <= LUT_SIZE; ++i) { - int c = (i <= threshold2) ? (i << DFIX) - : (i < threshold1) ? max_threshold * (threshold1 - i) / delta - : 0; - c >>= LFIX; - lut[+i] = +c; - lut[-i] = -c; - } - lut[0] = 0; -} - -static void CountLevels(SmoothParams* const p) { - int i, j, last_level; - uint8_t used_levels[256] = { 0 }; - const uint8_t* data = p->src_; - p->min_ = 255; - p->max_ = 0; - for (j = 0; j < p->height_; ++j) { - for (i = 0; i < p->width_; ++i) { - const int v = data[i]; - if (v < p->min_) p->min_ = v; - if (v > p->max_) p->max_ = v; - used_levels[v] = 1; - } - data += p->stride_; - } - // Compute the mininum distance between two non-zero levels. - p->min_level_dist_ = p->max_ - p->min_; - last_level = -1; - for (i = 0; i < 256; ++i) { - if (used_levels[i]) { - ++p->num_levels_; - if (last_level >= 0) { - const int level_dist = i - last_level; - if (level_dist < p->min_level_dist_) { - p->min_level_dist_ = level_dist; - } - } - last_level = i; - } - } -} - -// Initialize all params. -static int InitParams(uint8_t* const data, int width, int height, int stride, - int radius, SmoothParams* const p) { - const int R = 2 * radius + 1; // total size of the kernel - - const size_t size_scratch_m = (R + 1) * width * sizeof(*p->start_); - const size_t size_m = width * sizeof(*p->average_); - const size_t size_lut = (1 + 2 * LUT_SIZE) * sizeof(*p->correction_); - const size_t total_size = size_scratch_m + size_m + size_lut; - uint8_t* mem = (uint8_t*)WebPSafeMalloc(1U, total_size); - - if (mem == NULL) return 0; - p->mem_ = (void*)mem; - - p->start_ = (uint16_t*)mem; - p->cur_ = p->start_; - p->end_ = p->start_ + R * width; - p->top_ = p->end_ - width; - memset(p->top_, 0, width * sizeof(*p->top_)); - mem += size_scratch_m; - - p->average_ = (uint16_t*)mem; - mem += size_m; - - p->width_ = width; - p->height_ = height; - p->stride_ = stride; - p->src_ = data; - p->dst_ = data; - p->radius_ = radius; - p->scale_ = (1 << (FIX + LFIX)) / (R * R); // normalization constant - p->row_ = -radius; - - // analyze the input distribution so we can best-fit the threshold - CountLevels(p); - - // correction table - p->correction_ = ((int16_t*)mem) + LUT_SIZE; - InitCorrectionLUT(p->correction_, p->min_level_dist_); - - return 1; -} - -static void CleanupParams(SmoothParams* const p) { - WebPSafeFree(p->mem_); -} - -int WebPDequantizeLevels(uint8_t* const data, int width, int height, int stride, - int strength) { - const int radius = 4 * strength / 100; - if (strength < 0 || strength > 100) return 0; - if (data == NULL || width <= 0 || height <= 0) return 0; // bad params - if (radius > 0) { - SmoothParams p; - memset(&p, 0, sizeof(p)); - if (!InitParams(data, width, height, stride, radius, &p)) return 0; - if (p.num_levels_ > 2) { - for (; p.row_ < p.height_; ++p.row_) { - VFilter(&p); // accumulate average of input - // Need to wait few rows in order to prime the filter, - // before emitting some output. - if (p.row_ >= p.radius_) { - HFilter(&p); - ApplyFilter(&p); - } - } - } - CleanupParams(&p); - } - return 1; -} diff --git a/thirdparty/libwebp/utils/quant_levels_dec.h b/thirdparty/libwebp/utils/quant_levels_dec.h deleted file mode 100644 index 59a13495d3..0000000000 --- a/thirdparty/libwebp/utils/quant_levels_dec.h +++ /dev/null @@ -1,35 +0,0 @@ -// Copyright 2013 Google Inc. All Rights Reserved. -// -// Use of this source code is governed by a BSD-style license -// that can be found in the COPYING file in the root of the source -// tree. An additional intellectual property rights grant can be found -// in the file PATENTS. All contributing project authors may -// be found in the AUTHORS file in the root of the source tree. -// ----------------------------------------------------------------------------- -// -// Alpha plane de-quantization utility -// -// Author: Vikas Arora (vikasa@google.com) - -#ifndef WEBP_UTILS_QUANT_LEVELS_DEC_H_ -#define WEBP_UTILS_QUANT_LEVELS_DEC_H_ - -#include "../webp/types.h" - -#ifdef __cplusplus -extern "C" { -#endif - -// Apply post-processing to input 'data' of size 'width'x'height' assuming that -// the source was quantized to a reduced number of levels. 'stride' is in bytes. -// Strength is in [0..100] and controls the amount of dithering applied. -// Returns false in case of error (data is NULL, invalid parameters, -// malloc failure, ...). -int WebPDequantizeLevels(uint8_t* const data, int width, int height, int stride, - int strength); - -#ifdef __cplusplus -} // extern "C" -#endif - -#endif /* WEBP_UTILS_QUANT_LEVELS_DEC_H_ */ diff --git a/thirdparty/libwebp/utils/quant_levels_dec_utils.c b/thirdparty/libwebp/utils/quant_levels_dec_utils.c new file mode 100644 index 0000000000..d4d23d3147 --- /dev/null +++ b/thirdparty/libwebp/utils/quant_levels_dec_utils.c @@ -0,0 +1,284 @@ +// Copyright 2013 Google Inc. All Rights Reserved. +// +// Use of this source code is governed by a BSD-style license +// that can be found in the COPYING file in the root of the source +// tree. An additional intellectual property rights grant can be found +// in the file PATENTS. All contributing project authors may +// be found in the AUTHORS file in the root of the source tree. +// ----------------------------------------------------------------------------- +// +// Implement gradient smoothing: we replace a current alpha value by its +// surrounding average if it's close enough (that is: the change will be less +// than the minimum distance between two quantized level). +// We use sliding window for computing the 2d moving average. +// +// Author: Skal (pascal.massimino@gmail.com) + +#include "./quant_levels_dec_utils.h" + +#include // for memset + +#include "./utils.h" + +// #define USE_DITHERING // uncomment to enable ordered dithering (not vital) + +#define FIX 16 // fix-point precision for averaging +#define LFIX 2 // extra precision for look-up table +#define LUT_SIZE ((1 << (8 + LFIX)) - 1) // look-up table size + +#if defined(USE_DITHERING) + +#define DFIX 4 // extra precision for ordered dithering +#define DSIZE 4 // dithering size (must be a power of two) +// cf. http://en.wikipedia.org/wiki/Ordered_dithering +static const uint8_t kOrderedDither[DSIZE][DSIZE] = { + { 0, 8, 2, 10 }, // coefficients are in DFIX fixed-point precision + { 12, 4, 14, 6 }, + { 3, 11, 1, 9 }, + { 15, 7, 13, 5 } +}; + +#else +#define DFIX 0 +#endif + +typedef struct { + int width_, height_; // dimension + int stride_; // stride in bytes + int row_; // current input row being processed + uint8_t* src_; // input pointer + uint8_t* dst_; // output pointer + + int radius_; // filter radius (=delay) + int scale_; // normalization factor, in FIX bits precision + + void* mem_; // all memory + + // various scratch buffers + uint16_t* start_; + uint16_t* cur_; + uint16_t* end_; + uint16_t* top_; + uint16_t* average_; + + // input levels distribution + int num_levels_; // number of quantized levels + int min_, max_; // min and max level values + int min_level_dist_; // smallest distance between two consecutive levels + + int16_t* correction_; // size = 1 + 2*LUT_SIZE -> ~4k memory +} SmoothParams; + +//------------------------------------------------------------------------------ + +#define CLIP_MASK (int)(~0U << (8 + DFIX)) +static WEBP_INLINE uint8_t clip_8b(int v) { + return (!(v & CLIP_MASK)) ? (uint8_t)(v >> DFIX) : (v < 0) ? 0u : 255u; +} + +// vertical accumulation +static void VFilter(SmoothParams* const p) { + const uint8_t* src = p->src_; + const int w = p->width_; + uint16_t* const cur = p->cur_; + const uint16_t* const top = p->top_; + uint16_t* const out = p->end_; + uint16_t sum = 0; // all arithmetic is modulo 16bit + int x; + + for (x = 0; x < w; ++x) { + uint16_t new_value; + sum += src[x]; + new_value = top[x] + sum; + out[x] = new_value - cur[x]; // vertical sum of 'r' pixels. + cur[x] = new_value; + } + // move input pointers one row down + p->top_ = p->cur_; + p->cur_ += w; + if (p->cur_ == p->end_) p->cur_ = p->start_; // roll-over + // We replicate edges, as it's somewhat easier as a boundary condition. + // That's why we don't update the 'src' pointer on top/bottom area: + if (p->row_ >= 0 && p->row_ < p->height_ - 1) { + p->src_ += p->stride_; + } +} + +// horizontal accumulation. We use mirror replication of missing pixels, as it's +// a little easier to implement (surprisingly). +static void HFilter(SmoothParams* const p) { + const uint16_t* const in = p->end_; + uint16_t* const out = p->average_; + const uint32_t scale = p->scale_; + const int w = p->width_; + const int r = p->radius_; + + int x; + for (x = 0; x <= r; ++x) { // left mirroring + const uint16_t delta = in[x + r - 1] + in[r - x]; + out[x] = (delta * scale) >> FIX; + } + for (; x < w - r; ++x) { // bulk middle run + const uint16_t delta = in[x + r] - in[x - r - 1]; + out[x] = (delta * scale) >> FIX; + } + for (; x < w; ++x) { // right mirroring + const uint16_t delta = + 2 * in[w - 1] - in[2 * w - 2 - r - x] - in[x - r - 1]; + out[x] = (delta * scale) >> FIX; + } +} + +// emit one filtered output row +static void ApplyFilter(SmoothParams* const p) { + const uint16_t* const average = p->average_; + const int w = p->width_; + const int16_t* const correction = p->correction_; +#if defined(USE_DITHERING) + const uint8_t* const dither = kOrderedDither[p->row_ % DSIZE]; +#endif + uint8_t* const dst = p->dst_; + int x; + for (x = 0; x < w; ++x) { + const int v = dst[x]; + if (v < p->max_ && v > p->min_) { + const int c = (v << DFIX) + correction[average[x] - (v << LFIX)]; +#if defined(USE_DITHERING) + dst[x] = clip_8b(c + dither[x % DSIZE]); +#else + dst[x] = clip_8b(c); +#endif + } + } + p->dst_ += p->stride_; // advance output pointer +} + +//------------------------------------------------------------------------------ +// Initialize correction table + +static void InitCorrectionLUT(int16_t* const lut, int min_dist) { + // The correction curve is: + // f(x) = x for x <= threshold2 + // f(x) = 0 for x >= threshold1 + // and a linear interpolation for range x=[threshold2, threshold1] + // (along with f(-x) = -f(x) symmetry). + // Note that: threshold2 = 3/4 * threshold1 + const int threshold1 = min_dist << LFIX; + const int threshold2 = (3 * threshold1) >> 2; + const int max_threshold = threshold2 << DFIX; + const int delta = threshold1 - threshold2; + int i; + for (i = 1; i <= LUT_SIZE; ++i) { + int c = (i <= threshold2) ? (i << DFIX) + : (i < threshold1) ? max_threshold * (threshold1 - i) / delta + : 0; + c >>= LFIX; + lut[+i] = +c; + lut[-i] = -c; + } + lut[0] = 0; +} + +static void CountLevels(SmoothParams* const p) { + int i, j, last_level; + uint8_t used_levels[256] = { 0 }; + const uint8_t* data = p->src_; + p->min_ = 255; + p->max_ = 0; + for (j = 0; j < p->height_; ++j) { + for (i = 0; i < p->width_; ++i) { + const int v = data[i]; + if (v < p->min_) p->min_ = v; + if (v > p->max_) p->max_ = v; + used_levels[v] = 1; + } + data += p->stride_; + } + // Compute the mininum distance between two non-zero levels. + p->min_level_dist_ = p->max_ - p->min_; + last_level = -1; + for (i = 0; i < 256; ++i) { + if (used_levels[i]) { + ++p->num_levels_; + if (last_level >= 0) { + const int level_dist = i - last_level; + if (level_dist < p->min_level_dist_) { + p->min_level_dist_ = level_dist; + } + } + last_level = i; + } + } +} + +// Initialize all params. +static int InitParams(uint8_t* const data, int width, int height, int stride, + int radius, SmoothParams* const p) { + const int R = 2 * radius + 1; // total size of the kernel + + const size_t size_scratch_m = (R + 1) * width * sizeof(*p->start_); + const size_t size_m = width * sizeof(*p->average_); + const size_t size_lut = (1 + 2 * LUT_SIZE) * sizeof(*p->correction_); + const size_t total_size = size_scratch_m + size_m + size_lut; + uint8_t* mem = (uint8_t*)WebPSafeMalloc(1U, total_size); + + if (mem == NULL) return 0; + p->mem_ = (void*)mem; + + p->start_ = (uint16_t*)mem; + p->cur_ = p->start_; + p->end_ = p->start_ + R * width; + p->top_ = p->end_ - width; + memset(p->top_, 0, width * sizeof(*p->top_)); + mem += size_scratch_m; + + p->average_ = (uint16_t*)mem; + mem += size_m; + + p->width_ = width; + p->height_ = height; + p->stride_ = stride; + p->src_ = data; + p->dst_ = data; + p->radius_ = radius; + p->scale_ = (1 << (FIX + LFIX)) / (R * R); // normalization constant + p->row_ = -radius; + + // analyze the input distribution so we can best-fit the threshold + CountLevels(p); + + // correction table + p->correction_ = ((int16_t*)mem) + LUT_SIZE; + InitCorrectionLUT(p->correction_, p->min_level_dist_); + + return 1; +} + +static void CleanupParams(SmoothParams* const p) { + WebPSafeFree(p->mem_); +} + +int WebPDequantizeLevels(uint8_t* const data, int width, int height, int stride, + int strength) { + const int radius = 4 * strength / 100; + if (strength < 0 || strength > 100) return 0; + if (data == NULL || width <= 0 || height <= 0) return 0; // bad params + if (radius > 0) { + SmoothParams p; + memset(&p, 0, sizeof(p)); + if (!InitParams(data, width, height, stride, radius, &p)) return 0; + if (p.num_levels_ > 2) { + for (; p.row_ < p.height_; ++p.row_) { + VFilter(&p); // accumulate average of input + // Need to wait few rows in order to prime the filter, + // before emitting some output. + if (p.row_ >= p.radius_) { + HFilter(&p); + ApplyFilter(&p); + } + } + } + CleanupParams(&p); + } + return 1; +} diff --git a/thirdparty/libwebp/utils/quant_levels_dec_utils.h b/thirdparty/libwebp/utils/quant_levels_dec_utils.h new file mode 100644 index 0000000000..59a13495d3 --- /dev/null +++ b/thirdparty/libwebp/utils/quant_levels_dec_utils.h @@ -0,0 +1,35 @@ +// Copyright 2013 Google Inc. All Rights Reserved. +// +// Use of this source code is governed by a BSD-style license +// that can be found in the COPYING file in the root of the source +// tree. An additional intellectual property rights grant can be found +// in the file PATENTS. All contributing project authors may +// be found in the AUTHORS file in the root of the source tree. +// ----------------------------------------------------------------------------- +// +// Alpha plane de-quantization utility +// +// Author: Vikas Arora (vikasa@google.com) + +#ifndef WEBP_UTILS_QUANT_LEVELS_DEC_H_ +#define WEBP_UTILS_QUANT_LEVELS_DEC_H_ + +#include "../webp/types.h" + +#ifdef __cplusplus +extern "C" { +#endif + +// Apply post-processing to input 'data' of size 'width'x'height' assuming that +// the source was quantized to a reduced number of levels. 'stride' is in bytes. +// Strength is in [0..100] and controls the amount of dithering applied. +// Returns false in case of error (data is NULL, invalid parameters, +// malloc failure, ...). +int WebPDequantizeLevels(uint8_t* const data, int width, int height, int stride, + int strength); + +#ifdef __cplusplus +} // extern "C" +#endif + +#endif /* WEBP_UTILS_QUANT_LEVELS_DEC_H_ */ diff --git a/thirdparty/libwebp/utils/quant_levels_utils.c b/thirdparty/libwebp/utils/quant_levels_utils.c new file mode 100644 index 0000000000..73174e8ab9 --- /dev/null +++ b/thirdparty/libwebp/utils/quant_levels_utils.c @@ -0,0 +1,140 @@ +// Copyright 2011 Google Inc. All Rights Reserved. +// +// Use of this source code is governed by a BSD-style license +// that can be found in the COPYING file in the root of the source +// tree. An additional intellectual property rights grant can be found +// in the file PATENTS. All contributing project authors may +// be found in the AUTHORS file in the root of the source tree. +// ----------------------------------------------------------------------------- +// +// Quantize levels for specified number of quantization-levels ([2, 256]). +// Min and max values are preserved (usual 0 and 255 for alpha plane). +// +// Author: Skal (pascal.massimino@gmail.com) + +#include + +#include "./quant_levels_utils.h" + +#define NUM_SYMBOLS 256 + +#define MAX_ITER 6 // Maximum number of convergence steps. +#define ERROR_THRESHOLD 1e-4 // MSE stopping criterion. + +// ----------------------------------------------------------------------------- +// Quantize levels. + +int QuantizeLevels(uint8_t* const data, int width, int height, + int num_levels, uint64_t* const sse) { + int freq[NUM_SYMBOLS] = { 0 }; + int q_level[NUM_SYMBOLS] = { 0 }; + double inv_q_level[NUM_SYMBOLS] = { 0 }; + int min_s = 255, max_s = 0; + const size_t data_size = height * width; + int i, num_levels_in, iter; + double last_err = 1.e38, err = 0.; + const double err_threshold = ERROR_THRESHOLD * data_size; + + if (data == NULL) { + return 0; + } + + if (width <= 0 || height <= 0) { + return 0; + } + + if (num_levels < 2 || num_levels > 256) { + return 0; + } + + { + size_t n; + num_levels_in = 0; + for (n = 0; n < data_size; ++n) { + num_levels_in += (freq[data[n]] == 0); + if (min_s > data[n]) min_s = data[n]; + if (max_s < data[n]) max_s = data[n]; + ++freq[data[n]]; + } + } + + if (num_levels_in <= num_levels) goto End; // nothing to do! + + // Start with uniformly spread centroids. + for (i = 0; i < num_levels; ++i) { + inv_q_level[i] = min_s + (double)(max_s - min_s) * i / (num_levels - 1); + } + + // Fixed values. Won't be changed. + q_level[min_s] = 0; + q_level[max_s] = num_levels - 1; + assert(inv_q_level[0] == min_s); + assert(inv_q_level[num_levels - 1] == max_s); + + // k-Means iterations. + for (iter = 0; iter < MAX_ITER; ++iter) { + double q_sum[NUM_SYMBOLS] = { 0 }; + double q_count[NUM_SYMBOLS] = { 0 }; + int s, slot = 0; + + // Assign classes to representatives. + for (s = min_s; s <= max_s; ++s) { + // Keep track of the nearest neighbour 'slot' + while (slot < num_levels - 1 && + 2 * s > inv_q_level[slot] + inv_q_level[slot + 1]) { + ++slot; + } + if (freq[s] > 0) { + q_sum[slot] += s * freq[s]; + q_count[slot] += freq[s]; + } + q_level[s] = slot; + } + + // Assign new representatives to classes. + if (num_levels > 2) { + for (slot = 1; slot < num_levels - 1; ++slot) { + const double count = q_count[slot]; + if (count > 0.) { + inv_q_level[slot] = q_sum[slot] / count; + } + } + } + + // Compute convergence error. + err = 0.; + for (s = min_s; s <= max_s; ++s) { + const double error = s - inv_q_level[q_level[s]]; + err += freq[s] * error * error; + } + + // Check for convergence: we stop as soon as the error is no + // longer improving. + if (last_err - err < err_threshold) break; + last_err = err; + } + + // Remap the alpha plane to quantized values. + { + // double->int rounding operation can be costly, so we do it + // once for all before remapping. We also perform the data[] -> slot + // mapping, while at it (avoid one indirection in the final loop). + uint8_t map[NUM_SYMBOLS]; + int s; + size_t n; + for (s = min_s; s <= max_s; ++s) { + const int slot = q_level[s]; + map[s] = (uint8_t)(inv_q_level[slot] + .5); + } + // Final pass. + for (n = 0; n < data_size; ++n) { + data[n] = map[data[n]]; + } + } + End: + // Store sum of squared error if needed. + if (sse != NULL) *sse = (uint64_t)err; + + return 1; +} + diff --git a/thirdparty/libwebp/utils/quant_levels_utils.h b/thirdparty/libwebp/utils/quant_levels_utils.h new file mode 100644 index 0000000000..1cb5a32cae --- /dev/null +++ b/thirdparty/libwebp/utils/quant_levels_utils.h @@ -0,0 +1,36 @@ +// Copyright 2011 Google Inc. All Rights Reserved. +// +// Use of this source code is governed by a BSD-style license +// that can be found in the COPYING file in the root of the source +// tree. An additional intellectual property rights grant can be found +// in the file PATENTS. All contributing project authors may +// be found in the AUTHORS file in the root of the source tree. +// ----------------------------------------------------------------------------- +// +// Alpha plane quantization utility +// +// Author: Vikas Arora (vikasa@google.com) + +#ifndef WEBP_UTILS_QUANT_LEVELS_H_ +#define WEBP_UTILS_QUANT_LEVELS_H_ + +#include + +#include "../webp/types.h" + +#ifdef __cplusplus +extern "C" { +#endif + +// Replace the input 'data' of size 'width'x'height' with 'num-levels' +// quantized values. If not NULL, 'sse' will contain the sum of squared error. +// Valid range for 'num_levels' is [2, 256]. +// Returns false in case of error (data is NULL, or parameters are invalid). +int QuantizeLevels(uint8_t* const data, int width, int height, int num_levels, + uint64_t* const sse); + +#ifdef __cplusplus +} // extern "C" +#endif + +#endif /* WEBP_UTILS_QUANT_LEVELS_H_ */ diff --git a/thirdparty/libwebp/utils/random.c b/thirdparty/libwebp/utils/random.c deleted file mode 100644 index 24e96ad648..0000000000 --- a/thirdparty/libwebp/utils/random.c +++ /dev/null @@ -1,43 +0,0 @@ -// Copyright 2013 Google Inc. All Rights Reserved. -// -// Use of this source code is governed by a BSD-style license -// that can be found in the COPYING file in the root of the source -// tree. An additional intellectual property rights grant can be found -// in the file PATENTS. All contributing project authors may -// be found in the AUTHORS file in the root of the source tree. -// ----------------------------------------------------------------------------- -// -// Pseudo-random utilities -// -// Author: Skal (pascal.massimino@gmail.com) - -#include -#include "./random.h" - -//------------------------------------------------------------------------------ - -// 31b-range values -static const uint32_t kRandomTable[VP8_RANDOM_TABLE_SIZE] = { - 0x0de15230, 0x03b31886, 0x775faccb, 0x1c88626a, 0x68385c55, 0x14b3b828, - 0x4a85fef8, 0x49ddb84b, 0x64fcf397, 0x5c550289, 0x4a290000, 0x0d7ec1da, - 0x5940b7ab, 0x5492577d, 0x4e19ca72, 0x38d38c69, 0x0c01ee65, 0x32a1755f, - 0x5437f652, 0x5abb2c32, 0x0faa57b1, 0x73f533e7, 0x685feeda, 0x7563cce2, - 0x6e990e83, 0x4730a7ed, 0x4fc0d9c6, 0x496b153c, 0x4f1403fa, 0x541afb0c, - 0x73990b32, 0x26d7cb1c, 0x6fcc3706, 0x2cbb77d8, 0x75762f2a, 0x6425ccdd, - 0x24b35461, 0x0a7d8715, 0x220414a8, 0x141ebf67, 0x56b41583, 0x73e502e3, - 0x44cab16f, 0x28264d42, 0x73baaefb, 0x0a50ebed, 0x1d6ab6fb, 0x0d3ad40b, - 0x35db3b68, 0x2b081e83, 0x77ce6b95, 0x5181e5f0, 0x78853bbc, 0x009f9494, - 0x27e5ed3c -}; - -void VP8InitRandom(VP8Random* const rg, float dithering) { - memcpy(rg->tab_, kRandomTable, sizeof(rg->tab_)); - rg->index1_ = 0; - rg->index2_ = 31; - rg->amp_ = (dithering < 0.0) ? 0 - : (dithering > 1.0) ? (1 << VP8_RANDOM_DITHER_FIX) - : (uint32_t)((1 << VP8_RANDOM_DITHER_FIX) * dithering); -} - -//------------------------------------------------------------------------------ - diff --git a/thirdparty/libwebp/utils/random.h b/thirdparty/libwebp/utils/random.h deleted file mode 100644 index c392a615ca..0000000000 --- a/thirdparty/libwebp/utils/random.h +++ /dev/null @@ -1,63 +0,0 @@ -// Copyright 2013 Google Inc. All Rights Reserved. -// -// Use of this source code is governed by a BSD-style license -// that can be found in the COPYING file in the root of the source -// tree. An additional intellectual property rights grant can be found -// in the file PATENTS. All contributing project authors may -// be found in the AUTHORS file in the root of the source tree. -// ----------------------------------------------------------------------------- -// -// Pseudo-random utilities -// -// Author: Skal (pascal.massimino@gmail.com) - -#ifndef WEBP_UTILS_RANDOM_H_ -#define WEBP_UTILS_RANDOM_H_ - -#include -#include "../webp/types.h" - -#ifdef __cplusplus -extern "C" { -#endif - -#define VP8_RANDOM_DITHER_FIX 8 // fixed-point precision for dithering -#define VP8_RANDOM_TABLE_SIZE 55 - -typedef struct { - int index1_, index2_; - uint32_t tab_[VP8_RANDOM_TABLE_SIZE]; - int amp_; -} VP8Random; - -// Initializes random generator with an amplitude 'dithering' in range [0..1]. -void VP8InitRandom(VP8Random* const rg, float dithering); - -// Returns a centered pseudo-random number with 'num_bits' amplitude. -// (uses D.Knuth's Difference-based random generator). -// 'amp' is in VP8_RANDOM_DITHER_FIX fixed-point precision. -static WEBP_INLINE int VP8RandomBits2(VP8Random* const rg, int num_bits, - int amp) { - int diff; - assert(num_bits + VP8_RANDOM_DITHER_FIX <= 31); - diff = rg->tab_[rg->index1_] - rg->tab_[rg->index2_]; - if (diff < 0) diff += (1u << 31); - rg->tab_[rg->index1_] = diff; - if (++rg->index1_ == VP8_RANDOM_TABLE_SIZE) rg->index1_ = 0; - if (++rg->index2_ == VP8_RANDOM_TABLE_SIZE) rg->index2_ = 0; - // sign-extend, 0-center - diff = (int)((uint32_t)diff << 1) >> (32 - num_bits); - diff = (diff * amp) >> VP8_RANDOM_DITHER_FIX; // restrict range - diff += 1 << (num_bits - 1); // shift back to 0.5-center - return diff; -} - -static WEBP_INLINE int VP8RandomBits(VP8Random* const rg, int num_bits) { - return VP8RandomBits2(rg, num_bits, rg->amp_); -} - -#ifdef __cplusplus -} // extern "C" -#endif - -#endif /* WEBP_UTILS_RANDOM_H_ */ diff --git a/thirdparty/libwebp/utils/random_utils.c b/thirdparty/libwebp/utils/random_utils.c new file mode 100644 index 0000000000..9f1e4154a6 --- /dev/null +++ b/thirdparty/libwebp/utils/random_utils.c @@ -0,0 +1,43 @@ +// Copyright 2013 Google Inc. All Rights Reserved. +// +// Use of this source code is governed by a BSD-style license +// that can be found in the COPYING file in the root of the source +// tree. An additional intellectual property rights grant can be found +// in the file PATENTS. All contributing project authors may +// be found in the AUTHORS file in the root of the source tree. +// ----------------------------------------------------------------------------- +// +// Pseudo-random utilities +// +// Author: Skal (pascal.massimino@gmail.com) + +#include +#include "./random_utils.h" + +//------------------------------------------------------------------------------ + +// 31b-range values +static const uint32_t kRandomTable[VP8_RANDOM_TABLE_SIZE] = { + 0x0de15230, 0x03b31886, 0x775faccb, 0x1c88626a, 0x68385c55, 0x14b3b828, + 0x4a85fef8, 0x49ddb84b, 0x64fcf397, 0x5c550289, 0x4a290000, 0x0d7ec1da, + 0x5940b7ab, 0x5492577d, 0x4e19ca72, 0x38d38c69, 0x0c01ee65, 0x32a1755f, + 0x5437f652, 0x5abb2c32, 0x0faa57b1, 0x73f533e7, 0x685feeda, 0x7563cce2, + 0x6e990e83, 0x4730a7ed, 0x4fc0d9c6, 0x496b153c, 0x4f1403fa, 0x541afb0c, + 0x73990b32, 0x26d7cb1c, 0x6fcc3706, 0x2cbb77d8, 0x75762f2a, 0x6425ccdd, + 0x24b35461, 0x0a7d8715, 0x220414a8, 0x141ebf67, 0x56b41583, 0x73e502e3, + 0x44cab16f, 0x28264d42, 0x73baaefb, 0x0a50ebed, 0x1d6ab6fb, 0x0d3ad40b, + 0x35db3b68, 0x2b081e83, 0x77ce6b95, 0x5181e5f0, 0x78853bbc, 0x009f9494, + 0x27e5ed3c +}; + +void VP8InitRandom(VP8Random* const rg, float dithering) { + memcpy(rg->tab_, kRandomTable, sizeof(rg->tab_)); + rg->index1_ = 0; + rg->index2_ = 31; + rg->amp_ = (dithering < 0.0) ? 0 + : (dithering > 1.0) ? (1 << VP8_RANDOM_DITHER_FIX) + : (uint32_t)((1 << VP8_RANDOM_DITHER_FIX) * dithering); +} + +//------------------------------------------------------------------------------ + diff --git a/thirdparty/libwebp/utils/random_utils.h b/thirdparty/libwebp/utils/random_utils.h new file mode 100644 index 0000000000..c392a615ca --- /dev/null +++ b/thirdparty/libwebp/utils/random_utils.h @@ -0,0 +1,63 @@ +// Copyright 2013 Google Inc. All Rights Reserved. +// +// Use of this source code is governed by a BSD-style license +// that can be found in the COPYING file in the root of the source +// tree. An additional intellectual property rights grant can be found +// in the file PATENTS. All contributing project authors may +// be found in the AUTHORS file in the root of the source tree. +// ----------------------------------------------------------------------------- +// +// Pseudo-random utilities +// +// Author: Skal (pascal.massimino@gmail.com) + +#ifndef WEBP_UTILS_RANDOM_H_ +#define WEBP_UTILS_RANDOM_H_ + +#include +#include "../webp/types.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#define VP8_RANDOM_DITHER_FIX 8 // fixed-point precision for dithering +#define VP8_RANDOM_TABLE_SIZE 55 + +typedef struct { + int index1_, index2_; + uint32_t tab_[VP8_RANDOM_TABLE_SIZE]; + int amp_; +} VP8Random; + +// Initializes random generator with an amplitude 'dithering' in range [0..1]. +void VP8InitRandom(VP8Random* const rg, float dithering); + +// Returns a centered pseudo-random number with 'num_bits' amplitude. +// (uses D.Knuth's Difference-based random generator). +// 'amp' is in VP8_RANDOM_DITHER_FIX fixed-point precision. +static WEBP_INLINE int VP8RandomBits2(VP8Random* const rg, int num_bits, + int amp) { + int diff; + assert(num_bits + VP8_RANDOM_DITHER_FIX <= 31); + diff = rg->tab_[rg->index1_] - rg->tab_[rg->index2_]; + if (diff < 0) diff += (1u << 31); + rg->tab_[rg->index1_] = diff; + if (++rg->index1_ == VP8_RANDOM_TABLE_SIZE) rg->index1_ = 0; + if (++rg->index2_ == VP8_RANDOM_TABLE_SIZE) rg->index2_ = 0; + // sign-extend, 0-center + diff = (int)((uint32_t)diff << 1) >> (32 - num_bits); + diff = (diff * amp) >> VP8_RANDOM_DITHER_FIX; // restrict range + diff += 1 << (num_bits - 1); // shift back to 0.5-center + return diff; +} + +static WEBP_INLINE int VP8RandomBits(VP8Random* const rg, int num_bits) { + return VP8RandomBits2(rg, num_bits, rg->amp_); +} + +#ifdef __cplusplus +} // extern "C" +#endif + +#endif /* WEBP_UTILS_RANDOM_H_ */ diff --git a/thirdparty/libwebp/utils/rescaler.c b/thirdparty/libwebp/utils/rescaler.c deleted file mode 100644 index d2278a52ff..0000000000 --- a/thirdparty/libwebp/utils/rescaler.c +++ /dev/null @@ -1,146 +0,0 @@ -// Copyright 2012 Google Inc. All Rights Reserved. -// -// Use of this source code is governed by a BSD-style license -// that can be found in the COPYING file in the root of the source -// tree. An additional intellectual property rights grant can be found -// in the file PATENTS. All contributing project authors may -// be found in the AUTHORS file in the root of the source tree. -// ----------------------------------------------------------------------------- -// -// Rescaling functions -// -// Author: Skal (pascal.massimino@gmail.com) - -#include -#include -#include -#include "../dsp/dsp.h" -#include "./rescaler.h" - -//------------------------------------------------------------------------------ - -void WebPRescalerInit(WebPRescaler* const wrk, int src_width, int src_height, - uint8_t* const dst, - int dst_width, int dst_height, int dst_stride, - int num_channels, rescaler_t* const work) { - const int x_add = src_width, x_sub = dst_width; - const int y_add = src_height, y_sub = dst_height; - wrk->x_expand = (src_width < dst_width); - wrk->y_expand = (src_height < dst_height); - wrk->src_width = src_width; - wrk->src_height = src_height; - wrk->dst_width = dst_width; - wrk->dst_height = dst_height; - wrk->src_y = 0; - wrk->dst_y = 0; - wrk->dst = dst; - wrk->dst_stride = dst_stride; - wrk->num_channels = num_channels; - - // for 'x_expand', we use bilinear interpolation - wrk->x_add = wrk->x_expand ? (x_sub - 1) : x_add; - wrk->x_sub = wrk->x_expand ? (x_add - 1) : x_sub; - if (!wrk->x_expand) { // fx_scale is not used otherwise - wrk->fx_scale = WEBP_RESCALER_FRAC(1, wrk->x_sub); - } - // vertical scaling parameters - wrk->y_add = wrk->y_expand ? y_add - 1 : y_add; - wrk->y_sub = wrk->y_expand ? y_sub - 1 : y_sub; - wrk->y_accum = wrk->y_expand ? wrk->y_sub : wrk->y_add; - if (!wrk->y_expand) { - // This is WEBP_RESCALER_FRAC(dst_height, x_add * y_add) without the cast. - // Its value is <= WEBP_RESCALER_ONE, because dst_height <= wrk->y_add, and - // wrk->x_add >= 1; - const uint64_t ratio = - (uint64_t)dst_height * WEBP_RESCALER_ONE / (wrk->x_add * wrk->y_add); - if (ratio != (uint32_t)ratio) { - // When ratio == WEBP_RESCALER_ONE, we can't represent the ratio with the - // current fixed-point precision. This happens when src_height == - // wrk->y_add (which == src_height), and wrk->x_add == 1. - // => We special-case fxy_scale = 0, in WebPRescalerExportRow(). - wrk->fxy_scale = 0; - } else { - wrk->fxy_scale = (uint32_t)ratio; - } - wrk->fy_scale = WEBP_RESCALER_FRAC(1, wrk->y_sub); - } else { - wrk->fy_scale = WEBP_RESCALER_FRAC(1, wrk->x_add); - // wrk->fxy_scale is unused here. - } - wrk->irow = work; - wrk->frow = work + num_channels * dst_width; - memset(work, 0, 2 * dst_width * num_channels * sizeof(*work)); - - WebPRescalerDspInit(); -} - -int WebPRescalerGetScaledDimensions(int src_width, int src_height, - int* const scaled_width, - int* const scaled_height) { - assert(scaled_width != NULL); - assert(scaled_height != NULL); - { - int width = *scaled_width; - int height = *scaled_height; - - // if width is unspecified, scale original proportionally to height ratio. - if (width == 0) { - width = (src_width * height + src_height / 2) / src_height; - } - // if height is unspecified, scale original proportionally to width ratio. - if (height == 0) { - height = (src_height * width + src_width / 2) / src_width; - } - // Check if the overall dimensions still make sense. - if (width <= 0 || height <= 0) { - return 0; - } - - *scaled_width = width; - *scaled_height = height; - return 1; - } -} - -//------------------------------------------------------------------------------ -// all-in-one calls - -int WebPRescaleNeededLines(const WebPRescaler* const wrk, int max_num_lines) { - const int num_lines = (wrk->y_accum + wrk->y_sub - 1) / wrk->y_sub; - return (num_lines > max_num_lines) ? max_num_lines : num_lines; -} - -int WebPRescalerImport(WebPRescaler* const wrk, int num_lines, - const uint8_t* src, int src_stride) { - int total_imported = 0; - while (total_imported < num_lines && !WebPRescalerHasPendingOutput(wrk)) { - if (wrk->y_expand) { - rescaler_t* const tmp = wrk->irow; - wrk->irow = wrk->frow; - wrk->frow = tmp; - } - WebPRescalerImportRow(wrk, src); - if (!wrk->y_expand) { // Accumulate the contribution of the new row. - int x; - for (x = 0; x < wrk->num_channels * wrk->dst_width; ++x) { - wrk->irow[x] += wrk->frow[x]; - } - } - ++wrk->src_y; - src += src_stride; - ++total_imported; - wrk->y_accum -= wrk->y_sub; - } - return total_imported; -} - -int WebPRescalerExport(WebPRescaler* const rescaler) { - int total_exported = 0; - while (WebPRescalerHasPendingOutput(rescaler)) { - WebPRescalerExportRow(rescaler); - ++total_exported; - } - return total_exported; -} - -//------------------------------------------------------------------------------ diff --git a/thirdparty/libwebp/utils/rescaler.h b/thirdparty/libwebp/utils/rescaler.h deleted file mode 100644 index 98b01a76d0..0000000000 --- a/thirdparty/libwebp/utils/rescaler.h +++ /dev/null @@ -1,101 +0,0 @@ -// Copyright 2012 Google Inc. All Rights Reserved. -// -// Use of this source code is governed by a BSD-style license -// that can be found in the COPYING file in the root of the source -// tree. An additional intellectual property rights grant can be found -// in the file PATENTS. All contributing project authors may -// be found in the AUTHORS file in the root of the source tree. -// ----------------------------------------------------------------------------- -// -// Rescaling functions -// -// Author: Skal (pascal.massimino@gmail.com) - -#ifndef WEBP_UTILS_RESCALER_H_ -#define WEBP_UTILS_RESCALER_H_ - -#ifdef __cplusplus -extern "C" { -#endif - -#include "../webp/types.h" - -#define WEBP_RESCALER_RFIX 32 // fixed-point precision for multiplies -#define WEBP_RESCALER_ONE (1ull << WEBP_RESCALER_RFIX) -#define WEBP_RESCALER_FRAC(x, y) \ - ((uint32_t)(((uint64_t)(x) << WEBP_RESCALER_RFIX) / (y))) - -// Structure used for on-the-fly rescaling -typedef uint32_t rescaler_t; // type for side-buffer -typedef struct WebPRescaler WebPRescaler; -struct WebPRescaler { - int x_expand; // true if we're expanding in the x direction - int y_expand; // true if we're expanding in the y direction - int num_channels; // bytes to jump between pixels - uint32_t fx_scale; // fixed-point scaling factors - uint32_t fy_scale; // '' - uint32_t fxy_scale; // '' - int y_accum; // vertical accumulator - int y_add, y_sub; // vertical increments - int x_add, x_sub; // horizontal increments - int src_width, src_height; // source dimensions - int dst_width, dst_height; // destination dimensions - int src_y, dst_y; // row counters for input and output - uint8_t* dst; - int dst_stride; - rescaler_t* irow, *frow; // work buffer -}; - -// Initialize a rescaler given scratch area 'work' and dimensions of src & dst. -void WebPRescalerInit(WebPRescaler* const rescaler, - int src_width, int src_height, - uint8_t* const dst, - int dst_width, int dst_height, int dst_stride, - int num_channels, - rescaler_t* const work); - -// If either 'scaled_width' or 'scaled_height' (but not both) is 0 the value -// will be calculated preserving the aspect ratio, otherwise the values are -// left unmodified. Returns true on success, false if either value is 0 after -// performing the scaling calculation. -int WebPRescalerGetScaledDimensions(int src_width, int src_height, - int* const scaled_width, - int* const scaled_height); - -// Returns the number of input lines needed next to produce one output line, -// considering that the maximum available input lines are 'max_num_lines'. -int WebPRescaleNeededLines(const WebPRescaler* const rescaler, - int max_num_lines); - -// Import multiple rows over all channels, until at least one row is ready to -// be exported. Returns the actual number of lines that were imported. -int WebPRescalerImport(WebPRescaler* const rescaler, int num_rows, - const uint8_t* src, int src_stride); - -// Export as many rows as possible. Return the numbers of rows written. -int WebPRescalerExport(WebPRescaler* const rescaler); - -// Return true if input is finished -static WEBP_INLINE -int WebPRescalerInputDone(const WebPRescaler* const rescaler) { - return (rescaler->src_y >= rescaler->src_height); -} -// Return true if output is finished -static WEBP_INLINE -int WebPRescalerOutputDone(const WebPRescaler* const rescaler) { - return (rescaler->dst_y >= rescaler->dst_height); -} - -// Return true if there are pending output rows ready. -static WEBP_INLINE -int WebPRescalerHasPendingOutput(const WebPRescaler* const rescaler) { - return !WebPRescalerOutputDone(rescaler) && (rescaler->y_accum <= 0); -} - -//------------------------------------------------------------------------------ - -#ifdef __cplusplus -} // extern "C" -#endif - -#endif /* WEBP_UTILS_RESCALER_H_ */ diff --git a/thirdparty/libwebp/utils/rescaler_utils.c b/thirdparty/libwebp/utils/rescaler_utils.c new file mode 100644 index 0000000000..0d1f80da24 --- /dev/null +++ b/thirdparty/libwebp/utils/rescaler_utils.c @@ -0,0 +1,146 @@ +// Copyright 2012 Google Inc. All Rights Reserved. +// +// Use of this source code is governed by a BSD-style license +// that can be found in the COPYING file in the root of the source +// tree. An additional intellectual property rights grant can be found +// in the file PATENTS. All contributing project authors may +// be found in the AUTHORS file in the root of the source tree. +// ----------------------------------------------------------------------------- +// +// Rescaling functions +// +// Author: Skal (pascal.massimino@gmail.com) + +#include +#include +#include +#include "../dsp/dsp.h" +#include "./rescaler_utils.h" + +//------------------------------------------------------------------------------ + +void WebPRescalerInit(WebPRescaler* const wrk, int src_width, int src_height, + uint8_t* const dst, + int dst_width, int dst_height, int dst_stride, + int num_channels, rescaler_t* const work) { + const int x_add = src_width, x_sub = dst_width; + const int y_add = src_height, y_sub = dst_height; + wrk->x_expand = (src_width < dst_width); + wrk->y_expand = (src_height < dst_height); + wrk->src_width = src_width; + wrk->src_height = src_height; + wrk->dst_width = dst_width; + wrk->dst_height = dst_height; + wrk->src_y = 0; + wrk->dst_y = 0; + wrk->dst = dst; + wrk->dst_stride = dst_stride; + wrk->num_channels = num_channels; + + // for 'x_expand', we use bilinear interpolation + wrk->x_add = wrk->x_expand ? (x_sub - 1) : x_add; + wrk->x_sub = wrk->x_expand ? (x_add - 1) : x_sub; + if (!wrk->x_expand) { // fx_scale is not used otherwise + wrk->fx_scale = WEBP_RESCALER_FRAC(1, wrk->x_sub); + } + // vertical scaling parameters + wrk->y_add = wrk->y_expand ? y_add - 1 : y_add; + wrk->y_sub = wrk->y_expand ? y_sub - 1 : y_sub; + wrk->y_accum = wrk->y_expand ? wrk->y_sub : wrk->y_add; + if (!wrk->y_expand) { + // This is WEBP_RESCALER_FRAC(dst_height, x_add * y_add) without the cast. + // Its value is <= WEBP_RESCALER_ONE, because dst_height <= wrk->y_add, and + // wrk->x_add >= 1; + const uint64_t ratio = + (uint64_t)dst_height * WEBP_RESCALER_ONE / (wrk->x_add * wrk->y_add); + if (ratio != (uint32_t)ratio) { + // When ratio == WEBP_RESCALER_ONE, we can't represent the ratio with the + // current fixed-point precision. This happens when src_height == + // wrk->y_add (which == src_height), and wrk->x_add == 1. + // => We special-case fxy_scale = 0, in WebPRescalerExportRow(). + wrk->fxy_scale = 0; + } else { + wrk->fxy_scale = (uint32_t)ratio; + } + wrk->fy_scale = WEBP_RESCALER_FRAC(1, wrk->y_sub); + } else { + wrk->fy_scale = WEBP_RESCALER_FRAC(1, wrk->x_add); + // wrk->fxy_scale is unused here. + } + wrk->irow = work; + wrk->frow = work + num_channels * dst_width; + memset(work, 0, 2 * dst_width * num_channels * sizeof(*work)); + + WebPRescalerDspInit(); +} + +int WebPRescalerGetScaledDimensions(int src_width, int src_height, + int* const scaled_width, + int* const scaled_height) { + assert(scaled_width != NULL); + assert(scaled_height != NULL); + { + int width = *scaled_width; + int height = *scaled_height; + + // if width is unspecified, scale original proportionally to height ratio. + if (width == 0) { + width = (src_width * height + src_height / 2) / src_height; + } + // if height is unspecified, scale original proportionally to width ratio. + if (height == 0) { + height = (src_height * width + src_width / 2) / src_width; + } + // Check if the overall dimensions still make sense. + if (width <= 0 || height <= 0) { + return 0; + } + + *scaled_width = width; + *scaled_height = height; + return 1; + } +} + +//------------------------------------------------------------------------------ +// all-in-one calls + +int WebPRescaleNeededLines(const WebPRescaler* const wrk, int max_num_lines) { + const int num_lines = (wrk->y_accum + wrk->y_sub - 1) / wrk->y_sub; + return (num_lines > max_num_lines) ? max_num_lines : num_lines; +} + +int WebPRescalerImport(WebPRescaler* const wrk, int num_lines, + const uint8_t* src, int src_stride) { + int total_imported = 0; + while (total_imported < num_lines && !WebPRescalerHasPendingOutput(wrk)) { + if (wrk->y_expand) { + rescaler_t* const tmp = wrk->irow; + wrk->irow = wrk->frow; + wrk->frow = tmp; + } + WebPRescalerImportRow(wrk, src); + if (!wrk->y_expand) { // Accumulate the contribution of the new row. + int x; + for (x = 0; x < wrk->num_channels * wrk->dst_width; ++x) { + wrk->irow[x] += wrk->frow[x]; + } + } + ++wrk->src_y; + src += src_stride; + ++total_imported; + wrk->y_accum -= wrk->y_sub; + } + return total_imported; +} + +int WebPRescalerExport(WebPRescaler* const rescaler) { + int total_exported = 0; + while (WebPRescalerHasPendingOutput(rescaler)) { + WebPRescalerExportRow(rescaler); + ++total_exported; + } + return total_exported; +} + +//------------------------------------------------------------------------------ diff --git a/thirdparty/libwebp/utils/rescaler_utils.h b/thirdparty/libwebp/utils/rescaler_utils.h new file mode 100644 index 0000000000..98b01a76d0 --- /dev/null +++ b/thirdparty/libwebp/utils/rescaler_utils.h @@ -0,0 +1,101 @@ +// Copyright 2012 Google Inc. All Rights Reserved. +// +// Use of this source code is governed by a BSD-style license +// that can be found in the COPYING file in the root of the source +// tree. An additional intellectual property rights grant can be found +// in the file PATENTS. All contributing project authors may +// be found in the AUTHORS file in the root of the source tree. +// ----------------------------------------------------------------------------- +// +// Rescaling functions +// +// Author: Skal (pascal.massimino@gmail.com) + +#ifndef WEBP_UTILS_RESCALER_H_ +#define WEBP_UTILS_RESCALER_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +#include "../webp/types.h" + +#define WEBP_RESCALER_RFIX 32 // fixed-point precision for multiplies +#define WEBP_RESCALER_ONE (1ull << WEBP_RESCALER_RFIX) +#define WEBP_RESCALER_FRAC(x, y) \ + ((uint32_t)(((uint64_t)(x) << WEBP_RESCALER_RFIX) / (y))) + +// Structure used for on-the-fly rescaling +typedef uint32_t rescaler_t; // type for side-buffer +typedef struct WebPRescaler WebPRescaler; +struct WebPRescaler { + int x_expand; // true if we're expanding in the x direction + int y_expand; // true if we're expanding in the y direction + int num_channels; // bytes to jump between pixels + uint32_t fx_scale; // fixed-point scaling factors + uint32_t fy_scale; // '' + uint32_t fxy_scale; // '' + int y_accum; // vertical accumulator + int y_add, y_sub; // vertical increments + int x_add, x_sub; // horizontal increments + int src_width, src_height; // source dimensions + int dst_width, dst_height; // destination dimensions + int src_y, dst_y; // row counters for input and output + uint8_t* dst; + int dst_stride; + rescaler_t* irow, *frow; // work buffer +}; + +// Initialize a rescaler given scratch area 'work' and dimensions of src & dst. +void WebPRescalerInit(WebPRescaler* const rescaler, + int src_width, int src_height, + uint8_t* const dst, + int dst_width, int dst_height, int dst_stride, + int num_channels, + rescaler_t* const work); + +// If either 'scaled_width' or 'scaled_height' (but not both) is 0 the value +// will be calculated preserving the aspect ratio, otherwise the values are +// left unmodified. Returns true on success, false if either value is 0 after +// performing the scaling calculation. +int WebPRescalerGetScaledDimensions(int src_width, int src_height, + int* const scaled_width, + int* const scaled_height); + +// Returns the number of input lines needed next to produce one output line, +// considering that the maximum available input lines are 'max_num_lines'. +int WebPRescaleNeededLines(const WebPRescaler* const rescaler, + int max_num_lines); + +// Import multiple rows over all channels, until at least one row is ready to +// be exported. Returns the actual number of lines that were imported. +int WebPRescalerImport(WebPRescaler* const rescaler, int num_rows, + const uint8_t* src, int src_stride); + +// Export as many rows as possible. Return the numbers of rows written. +int WebPRescalerExport(WebPRescaler* const rescaler); + +// Return true if input is finished +static WEBP_INLINE +int WebPRescalerInputDone(const WebPRescaler* const rescaler) { + return (rescaler->src_y >= rescaler->src_height); +} +// Return true if output is finished +static WEBP_INLINE +int WebPRescalerOutputDone(const WebPRescaler* const rescaler) { + return (rescaler->dst_y >= rescaler->dst_height); +} + +// Return true if there are pending output rows ready. +static WEBP_INLINE +int WebPRescalerHasPendingOutput(const WebPRescaler* const rescaler) { + return !WebPRescalerOutputDone(rescaler) && (rescaler->y_accum <= 0); +} + +//------------------------------------------------------------------------------ + +#ifdef __cplusplus +} // extern "C" +#endif + +#endif /* WEBP_UTILS_RESCALER_H_ */ diff --git a/thirdparty/libwebp/utils/thread.c b/thirdparty/libwebp/utils/thread.c deleted file mode 100644 index 93f7622797..0000000000 --- a/thirdparty/libwebp/utils/thread.c +++ /dev/null @@ -1,358 +0,0 @@ -// Copyright 2011 Google Inc. All Rights Reserved. -// -// Use of this source code is governed by a BSD-style license -// that can be found in the COPYING file in the root of the source -// tree. An additional intellectual property rights grant can be found -// in the file PATENTS. All contributing project authors may -// be found in the AUTHORS file in the root of the source tree. -// ----------------------------------------------------------------------------- -// -// Multi-threaded worker -// -// Author: Skal (pascal.massimino@gmail.com) - -#include -#include // for memset() -#include "./thread.h" -#include "./utils.h" - -#ifdef WEBP_USE_THREAD - -#if defined(_WIN32) - -#include -typedef HANDLE pthread_t; -typedef CRITICAL_SECTION pthread_mutex_t; - -#if _WIN32_WINNT >= 0x0600 // Windows Vista / Server 2008 or greater -#define USE_WINDOWS_CONDITION_VARIABLE -typedef CONDITION_VARIABLE pthread_cond_t; -#else -typedef struct { - HANDLE waiting_sem_; - HANDLE received_sem_; - HANDLE signal_event_; -} pthread_cond_t; -#endif // _WIN32_WINNT >= 0x600 - -#ifndef WINAPI_FAMILY_PARTITION -#define WINAPI_PARTITION_DESKTOP 1 -#define WINAPI_FAMILY_PARTITION(x) x -#endif - -#if !WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP) -#define USE_CREATE_THREAD -#endif - -#else // !_WIN32 - -#include - -#endif // _WIN32 - -struct WebPWorkerImpl { - pthread_mutex_t mutex_; - pthread_cond_t condition_; - pthread_t thread_; -}; - -#if defined(_WIN32) - -//------------------------------------------------------------------------------ -// simplistic pthread emulation layer - -#include - -// _beginthreadex requires __stdcall -#define THREADFN unsigned int __stdcall -#define THREAD_RETURN(val) (unsigned int)((DWORD_PTR)val) - -#if _WIN32_WINNT >= 0x0501 // Windows XP or greater -#define WaitForSingleObject(obj, timeout) \ - WaitForSingleObjectEx(obj, timeout, FALSE /*bAlertable*/) -#endif - -static int pthread_create(pthread_t* const thread, const void* attr, - unsigned int (__stdcall *start)(void*), void* arg) { - (void)attr; -#ifdef USE_CREATE_THREAD - *thread = CreateThread(NULL, /* lpThreadAttributes */ - 0, /* dwStackSize */ - start, - arg, - 0, /* dwStackSize */ - NULL); /* lpThreadId */ -#else - *thread = (pthread_t)_beginthreadex(NULL, /* void *security */ - 0, /* unsigned stack_size */ - start, - arg, - 0, /* unsigned initflag */ - NULL); /* unsigned *thrdaddr */ -#endif - if (*thread == NULL) return 1; - SetThreadPriority(*thread, THREAD_PRIORITY_ABOVE_NORMAL); - return 0; -} - -static int pthread_join(pthread_t thread, void** value_ptr) { - (void)value_ptr; - return (WaitForSingleObject(thread, INFINITE) != WAIT_OBJECT_0 || - CloseHandle(thread) == 0); -} - -// Mutex -static int pthread_mutex_init(pthread_mutex_t* const mutex, void* mutexattr) { - (void)mutexattr; -#if _WIN32_WINNT >= 0x0600 // Windows Vista / Server 2008 or greater - InitializeCriticalSectionEx(mutex, 0 /*dwSpinCount*/, 0 /*Flags*/); -#else - InitializeCriticalSection(mutex); -#endif - return 0; -} - -static int pthread_mutex_lock(pthread_mutex_t* const mutex) { - EnterCriticalSection(mutex); - return 0; -} - -static int pthread_mutex_unlock(pthread_mutex_t* const mutex) { - LeaveCriticalSection(mutex); - return 0; -} - -static int pthread_mutex_destroy(pthread_mutex_t* const mutex) { - DeleteCriticalSection(mutex); - return 0; -} - -// Condition -static int pthread_cond_destroy(pthread_cond_t* const condition) { - int ok = 1; -#ifdef USE_WINDOWS_CONDITION_VARIABLE - (void)condition; -#else - ok &= (CloseHandle(condition->waiting_sem_) != 0); - ok &= (CloseHandle(condition->received_sem_) != 0); - ok &= (CloseHandle(condition->signal_event_) != 0); -#endif - return !ok; -} - -static int pthread_cond_init(pthread_cond_t* const condition, void* cond_attr) { - (void)cond_attr; -#ifdef USE_WINDOWS_CONDITION_VARIABLE - InitializeConditionVariable(condition); -#else - condition->waiting_sem_ = CreateSemaphore(NULL, 0, 1, NULL); - condition->received_sem_ = CreateSemaphore(NULL, 0, 1, NULL); - condition->signal_event_ = CreateEvent(NULL, FALSE, FALSE, NULL); - if (condition->waiting_sem_ == NULL || - condition->received_sem_ == NULL || - condition->signal_event_ == NULL) { - pthread_cond_destroy(condition); - return 1; - } -#endif - return 0; -} - -static int pthread_cond_signal(pthread_cond_t* const condition) { - int ok = 1; -#ifdef USE_WINDOWS_CONDITION_VARIABLE - WakeConditionVariable(condition); -#else - if (WaitForSingleObject(condition->waiting_sem_, 0) == WAIT_OBJECT_0) { - // a thread is waiting in pthread_cond_wait: allow it to be notified - ok = SetEvent(condition->signal_event_); - // wait until the event is consumed so the signaler cannot consume - // the event via its own pthread_cond_wait. - ok &= (WaitForSingleObject(condition->received_sem_, INFINITE) != - WAIT_OBJECT_0); - } -#endif - return !ok; -} - -static int pthread_cond_wait(pthread_cond_t* const condition, - pthread_mutex_t* const mutex) { - int ok; -#ifdef USE_WINDOWS_CONDITION_VARIABLE - ok = SleepConditionVariableCS(condition, mutex, INFINITE); -#else - // note that there is a consumer available so the signal isn't dropped in - // pthread_cond_signal - if (!ReleaseSemaphore(condition->waiting_sem_, 1, NULL)) - return 1; - // now unlock the mutex so pthread_cond_signal may be issued - pthread_mutex_unlock(mutex); - ok = (WaitForSingleObject(condition->signal_event_, INFINITE) == - WAIT_OBJECT_0); - ok &= ReleaseSemaphore(condition->received_sem_, 1, NULL); - pthread_mutex_lock(mutex); -#endif - return !ok; -} - -#else // !_WIN32 -# define THREADFN void* -# define THREAD_RETURN(val) val -#endif // _WIN32 - -//------------------------------------------------------------------------------ - -static void Execute(WebPWorker* const worker); // Forward declaration. - -static THREADFN ThreadLoop(void* ptr) { - WebPWorker* const worker = (WebPWorker*)ptr; - int done = 0; - while (!done) { - pthread_mutex_lock(&worker->impl_->mutex_); - while (worker->status_ == OK) { // wait in idling mode - pthread_cond_wait(&worker->impl_->condition_, &worker->impl_->mutex_); - } - if (worker->status_ == WORK) { - Execute(worker); - worker->status_ = OK; - } else if (worker->status_ == NOT_OK) { // finish the worker - done = 1; - } - // signal to the main thread that we're done (for Sync()) - pthread_cond_signal(&worker->impl_->condition_); - pthread_mutex_unlock(&worker->impl_->mutex_); - } - return THREAD_RETURN(NULL); // Thread is finished -} - -// main thread state control -static void ChangeState(WebPWorker* const worker, - WebPWorkerStatus new_status) { - // No-op when attempting to change state on a thread that didn't come up. - // Checking status_ without acquiring the lock first would result in a data - // race. - if (worker->impl_ == NULL) return; - - pthread_mutex_lock(&worker->impl_->mutex_); - if (worker->status_ >= OK) { - // wait for the worker to finish - while (worker->status_ != OK) { - pthread_cond_wait(&worker->impl_->condition_, &worker->impl_->mutex_); - } - // assign new status and release the working thread if needed - if (new_status != OK) { - worker->status_ = new_status; - pthread_cond_signal(&worker->impl_->condition_); - } - } - pthread_mutex_unlock(&worker->impl_->mutex_); -} - -#endif // WEBP_USE_THREAD - -//------------------------------------------------------------------------------ - -static void Init(WebPWorker* const worker) { - memset(worker, 0, sizeof(*worker)); - worker->status_ = NOT_OK; -} - -static int Sync(WebPWorker* const worker) { -#ifdef WEBP_USE_THREAD - ChangeState(worker, OK); -#endif - assert(worker->status_ <= OK); - return !worker->had_error; -} - -static int Reset(WebPWorker* const worker) { - int ok = 1; - worker->had_error = 0; - if (worker->status_ < OK) { -#ifdef WEBP_USE_THREAD - worker->impl_ = (WebPWorkerImpl*)WebPSafeCalloc(1, sizeof(*worker->impl_)); - if (worker->impl_ == NULL) { - return 0; - } - if (pthread_mutex_init(&worker->impl_->mutex_, NULL)) { - goto Error; - } - if (pthread_cond_init(&worker->impl_->condition_, NULL)) { - pthread_mutex_destroy(&worker->impl_->mutex_); - goto Error; - } - pthread_mutex_lock(&worker->impl_->mutex_); - ok = !pthread_create(&worker->impl_->thread_, NULL, ThreadLoop, worker); - if (ok) worker->status_ = OK; - pthread_mutex_unlock(&worker->impl_->mutex_); - if (!ok) { - pthread_mutex_destroy(&worker->impl_->mutex_); - pthread_cond_destroy(&worker->impl_->condition_); - Error: - WebPSafeFree(worker->impl_); - worker->impl_ = NULL; - return 0; - } -#else - worker->status_ = OK; -#endif - } else if (worker->status_ > OK) { - ok = Sync(worker); - } - assert(!ok || (worker->status_ == OK)); - return ok; -} - -static void Execute(WebPWorker* const worker) { - if (worker->hook != NULL) { - worker->had_error |= !worker->hook(worker->data1, worker->data2); - } -} - -static void Launch(WebPWorker* const worker) { -#ifdef WEBP_USE_THREAD - ChangeState(worker, WORK); -#else - Execute(worker); -#endif -} - -static void End(WebPWorker* const worker) { -#ifdef WEBP_USE_THREAD - if (worker->impl_ != NULL) { - ChangeState(worker, NOT_OK); - pthread_join(worker->impl_->thread_, NULL); - pthread_mutex_destroy(&worker->impl_->mutex_); - pthread_cond_destroy(&worker->impl_->condition_); - WebPSafeFree(worker->impl_); - worker->impl_ = NULL; - } -#else - worker->status_ = NOT_OK; - assert(worker->impl_ == NULL); -#endif - assert(worker->status_ == NOT_OK); -} - -//------------------------------------------------------------------------------ - -static WebPWorkerInterface g_worker_interface = { - Init, Reset, Sync, Launch, Execute, End -}; - -int WebPSetWorkerInterface(const WebPWorkerInterface* const winterface) { - if (winterface == NULL || - winterface->Init == NULL || winterface->Reset == NULL || - winterface->Sync == NULL || winterface->Launch == NULL || - winterface->Execute == NULL || winterface->End == NULL) { - return 0; - } - g_worker_interface = *winterface; - return 1; -} - -const WebPWorkerInterface* WebPGetWorkerInterface(void) { - return &g_worker_interface; -} - -//------------------------------------------------------------------------------ diff --git a/thirdparty/libwebp/utils/thread.h b/thirdparty/libwebp/utils/thread.h deleted file mode 100644 index 8408311855..0000000000 --- a/thirdparty/libwebp/utils/thread.h +++ /dev/null @@ -1,93 +0,0 @@ -// Copyright 2011 Google Inc. All Rights Reserved. -// -// Use of this source code is governed by a BSD-style license -// that can be found in the COPYING file in the root of the source -// tree. An additional intellectual property rights grant can be found -// in the file PATENTS. All contributing project authors may -// be found in the AUTHORS file in the root of the source tree. -// ----------------------------------------------------------------------------- -// -// Multi-threaded worker -// -// Author: Skal (pascal.massimino@gmail.com) - -#ifndef WEBP_UTILS_THREAD_H_ -#define WEBP_UTILS_THREAD_H_ - -#ifdef HAVE_CONFIG_H -#include "../webp/config.h" -#endif - -#include "../webp/types.h" - -#ifdef __cplusplus -extern "C" { -#endif - -// State of the worker thread object -typedef enum { - NOT_OK = 0, // object is unusable - OK, // ready to work - WORK // busy finishing the current task -} WebPWorkerStatus; - -// Function to be called by the worker thread. Takes two opaque pointers as -// arguments (data1 and data2), and should return false in case of error. -typedef int (*WebPWorkerHook)(void*, void*); - -// Platform-dependent implementation details for the worker. -typedef struct WebPWorkerImpl WebPWorkerImpl; - -// Synchronization object used to launch job in the worker thread -typedef struct { - WebPWorkerImpl* impl_; - WebPWorkerStatus status_; - WebPWorkerHook hook; // hook to call - void* data1; // first argument passed to 'hook' - void* data2; // second argument passed to 'hook' - int had_error; // return value of the last call to 'hook' -} WebPWorker; - -// The interface for all thread-worker related functions. All these functions -// must be implemented. -typedef struct { - // Must be called first, before any other method. - void (*Init)(WebPWorker* const worker); - // Must be called to initialize the object and spawn the thread. Re-entrant. - // Will potentially launch the thread. Returns false in case of error. - int (*Reset)(WebPWorker* const worker); - // Makes sure the previous work is finished. Returns true if worker->had_error - // was not set and no error condition was triggered by the working thread. - int (*Sync)(WebPWorker* const worker); - // Triggers the thread to call hook() with data1 and data2 arguments. These - // hook/data1/data2 values can be changed at any time before calling this - // function, but not be changed afterward until the next call to Sync(). - void (*Launch)(WebPWorker* const worker); - // This function is similar to Launch() except that it calls the - // hook directly instead of using a thread. Convenient to bypass the thread - // mechanism while still using the WebPWorker structs. Sync() must - // still be called afterward (for error reporting). - void (*Execute)(WebPWorker* const worker); - // Kill the thread and terminate the object. To use the object again, one - // must call Reset() again. - void (*End)(WebPWorker* const worker); -} WebPWorkerInterface; - -// Install a new set of threading functions, overriding the defaults. This -// should be done before any workers are started, i.e., before any encoding or -// decoding takes place. The contents of the interface struct are copied, it -// is safe to free the corresponding memory after this call. This function is -// not thread-safe. Return false in case of invalid pointer or methods. -WEBP_EXTERN(int) WebPSetWorkerInterface( - const WebPWorkerInterface* const winterface); - -// Retrieve the currently set thread worker interface. -WEBP_EXTERN(const WebPWorkerInterface*) WebPGetWorkerInterface(void); - -//------------------------------------------------------------------------------ - -#ifdef __cplusplus -} // extern "C" -#endif - -#endif /* WEBP_UTILS_THREAD_H_ */ diff --git a/thirdparty/libwebp/utils/thread_utils.c b/thirdparty/libwebp/utils/thread_utils.c new file mode 100644 index 0000000000..1729060c70 --- /dev/null +++ b/thirdparty/libwebp/utils/thread_utils.c @@ -0,0 +1,356 @@ +// Copyright 2011 Google Inc. All Rights Reserved. +// +// Use of this source code is governed by a BSD-style license +// that can be found in the COPYING file in the root of the source +// tree. An additional intellectual property rights grant can be found +// in the file PATENTS. All contributing project authors may +// be found in the AUTHORS file in the root of the source tree. +// ----------------------------------------------------------------------------- +// +// Multi-threaded worker +// +// Author: Skal (pascal.massimino@gmail.com) + +#include +#include // for memset() +#include "./thread_utils.h" +#include "./utils.h" + +#ifdef WEBP_USE_THREAD + +#if defined(_WIN32) + +#include +typedef HANDLE pthread_t; +typedef CRITICAL_SECTION pthread_mutex_t; + +#if _WIN32_WINNT >= 0x0600 // Windows Vista / Server 2008 or greater +#define USE_WINDOWS_CONDITION_VARIABLE +typedef CONDITION_VARIABLE pthread_cond_t; +#else +typedef struct { + HANDLE waiting_sem_; + HANDLE received_sem_; + HANDLE signal_event_; +} pthread_cond_t; +#endif // _WIN32_WINNT >= 0x600 + +#ifndef WINAPI_FAMILY_PARTITION +#define WINAPI_PARTITION_DESKTOP 1 +#define WINAPI_FAMILY_PARTITION(x) x +#endif + +#if !WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP) +#define USE_CREATE_THREAD +#endif + +#else // !_WIN32 + +#include + +#endif // _WIN32 + +struct WebPWorkerImpl { + pthread_mutex_t mutex_; + pthread_cond_t condition_; + pthread_t thread_; +}; + +#if defined(_WIN32) + +//------------------------------------------------------------------------------ +// simplistic pthread emulation layer + +#include + +// _beginthreadex requires __stdcall +#define THREADFN unsigned int __stdcall +#define THREAD_RETURN(val) (unsigned int)((DWORD_PTR)val) + +#if _WIN32_WINNT >= 0x0501 // Windows XP or greater +#define WaitForSingleObject(obj, timeout) \ + WaitForSingleObjectEx(obj, timeout, FALSE /*bAlertable*/) +#endif + +static int pthread_create(pthread_t* const thread, const void* attr, + unsigned int (__stdcall *start)(void*), void* arg) { + (void)attr; +#ifdef USE_CREATE_THREAD + *thread = CreateThread(NULL, /* lpThreadAttributes */ + 0, /* dwStackSize */ + start, + arg, + 0, /* dwStackSize */ + NULL); /* lpThreadId */ +#else + *thread = (pthread_t)_beginthreadex(NULL, /* void *security */ + 0, /* unsigned stack_size */ + start, + arg, + 0, /* unsigned initflag */ + NULL); /* unsigned *thrdaddr */ +#endif + if (*thread == NULL) return 1; + SetThreadPriority(*thread, THREAD_PRIORITY_ABOVE_NORMAL); + return 0; +} + +static int pthread_join(pthread_t thread, void** value_ptr) { + (void)value_ptr; + return (WaitForSingleObject(thread, INFINITE) != WAIT_OBJECT_0 || + CloseHandle(thread) == 0); +} + +// Mutex +static int pthread_mutex_init(pthread_mutex_t* const mutex, void* mutexattr) { + (void)mutexattr; +#if _WIN32_WINNT >= 0x0600 // Windows Vista / Server 2008 or greater + InitializeCriticalSectionEx(mutex, 0 /*dwSpinCount*/, 0 /*Flags*/); +#else + InitializeCriticalSection(mutex); +#endif + return 0; +} + +static int pthread_mutex_lock(pthread_mutex_t* const mutex) { + EnterCriticalSection(mutex); + return 0; +} + +static int pthread_mutex_unlock(pthread_mutex_t* const mutex) { + LeaveCriticalSection(mutex); + return 0; +} + +static int pthread_mutex_destroy(pthread_mutex_t* const mutex) { + DeleteCriticalSection(mutex); + return 0; +} + +// Condition +static int pthread_cond_destroy(pthread_cond_t* const condition) { + int ok = 1; +#ifdef USE_WINDOWS_CONDITION_VARIABLE + (void)condition; +#else + ok &= (CloseHandle(condition->waiting_sem_) != 0); + ok &= (CloseHandle(condition->received_sem_) != 0); + ok &= (CloseHandle(condition->signal_event_) != 0); +#endif + return !ok; +} + +static int pthread_cond_init(pthread_cond_t* const condition, void* cond_attr) { + (void)cond_attr; +#ifdef USE_WINDOWS_CONDITION_VARIABLE + InitializeConditionVariable(condition); +#else + condition->waiting_sem_ = CreateSemaphore(NULL, 0, 1, NULL); + condition->received_sem_ = CreateSemaphore(NULL, 0, 1, NULL); + condition->signal_event_ = CreateEvent(NULL, FALSE, FALSE, NULL); + if (condition->waiting_sem_ == NULL || + condition->received_sem_ == NULL || + condition->signal_event_ == NULL) { + pthread_cond_destroy(condition); + return 1; + } +#endif + return 0; +} + +static int pthread_cond_signal(pthread_cond_t* const condition) { + int ok = 1; +#ifdef USE_WINDOWS_CONDITION_VARIABLE + WakeConditionVariable(condition); +#else + if (WaitForSingleObject(condition->waiting_sem_, 0) == WAIT_OBJECT_0) { + // a thread is waiting in pthread_cond_wait: allow it to be notified + ok = SetEvent(condition->signal_event_); + // wait until the event is consumed so the signaler cannot consume + // the event via its own pthread_cond_wait. + ok &= (WaitForSingleObject(condition->received_sem_, INFINITE) != + WAIT_OBJECT_0); + } +#endif + return !ok; +} + +static int pthread_cond_wait(pthread_cond_t* const condition, + pthread_mutex_t* const mutex) { + int ok; +#ifdef USE_WINDOWS_CONDITION_VARIABLE + ok = SleepConditionVariableCS(condition, mutex, INFINITE); +#else + // note that there is a consumer available so the signal isn't dropped in + // pthread_cond_signal + if (!ReleaseSemaphore(condition->waiting_sem_, 1, NULL)) return 1; + // now unlock the mutex so pthread_cond_signal may be issued + pthread_mutex_unlock(mutex); + ok = (WaitForSingleObject(condition->signal_event_, INFINITE) == + WAIT_OBJECT_0); + ok &= ReleaseSemaphore(condition->received_sem_, 1, NULL); + pthread_mutex_lock(mutex); +#endif + return !ok; +} + +#else // !_WIN32 +# define THREADFN void* +# define THREAD_RETURN(val) val +#endif // _WIN32 + +//------------------------------------------------------------------------------ + +static void Execute(WebPWorker* const worker); // Forward declaration. + +static THREADFN ThreadLoop(void* ptr) { + WebPWorker* const worker = (WebPWorker*)ptr; + int done = 0; + while (!done) { + pthread_mutex_lock(&worker->impl_->mutex_); + while (worker->status_ == OK) { // wait in idling mode + pthread_cond_wait(&worker->impl_->condition_, &worker->impl_->mutex_); + } + if (worker->status_ == WORK) { + Execute(worker); + worker->status_ = OK; + } else if (worker->status_ == NOT_OK) { // finish the worker + done = 1; + } + // signal to the main thread that we're done (for Sync()) + pthread_cond_signal(&worker->impl_->condition_); + pthread_mutex_unlock(&worker->impl_->mutex_); + } + return THREAD_RETURN(NULL); // Thread is finished +} + +// main thread state control +static void ChangeState(WebPWorker* const worker, WebPWorkerStatus new_status) { + // No-op when attempting to change state on a thread that didn't come up. + // Checking status_ without acquiring the lock first would result in a data + // race. + if (worker->impl_ == NULL) return; + + pthread_mutex_lock(&worker->impl_->mutex_); + if (worker->status_ >= OK) { + // wait for the worker to finish + while (worker->status_ != OK) { + pthread_cond_wait(&worker->impl_->condition_, &worker->impl_->mutex_); + } + // assign new status and release the working thread if needed + if (new_status != OK) { + worker->status_ = new_status; + pthread_cond_signal(&worker->impl_->condition_); + } + } + pthread_mutex_unlock(&worker->impl_->mutex_); +} + +#endif // WEBP_USE_THREAD + +//------------------------------------------------------------------------------ + +static void Init(WebPWorker* const worker) { + memset(worker, 0, sizeof(*worker)); + worker->status_ = NOT_OK; +} + +static int Sync(WebPWorker* const worker) { +#ifdef WEBP_USE_THREAD + ChangeState(worker, OK); +#endif + assert(worker->status_ <= OK); + return !worker->had_error; +} + +static int Reset(WebPWorker* const worker) { + int ok = 1; + worker->had_error = 0; + if (worker->status_ < OK) { +#ifdef WEBP_USE_THREAD + worker->impl_ = (WebPWorkerImpl*)WebPSafeCalloc(1, sizeof(*worker->impl_)); + if (worker->impl_ == NULL) { + return 0; + } + if (pthread_mutex_init(&worker->impl_->mutex_, NULL)) { + goto Error; + } + if (pthread_cond_init(&worker->impl_->condition_, NULL)) { + pthread_mutex_destroy(&worker->impl_->mutex_); + goto Error; + } + pthread_mutex_lock(&worker->impl_->mutex_); + ok = !pthread_create(&worker->impl_->thread_, NULL, ThreadLoop, worker); + if (ok) worker->status_ = OK; + pthread_mutex_unlock(&worker->impl_->mutex_); + if (!ok) { + pthread_mutex_destroy(&worker->impl_->mutex_); + pthread_cond_destroy(&worker->impl_->condition_); + Error: + WebPSafeFree(worker->impl_); + worker->impl_ = NULL; + return 0; + } +#else + worker->status_ = OK; +#endif + } else if (worker->status_ > OK) { + ok = Sync(worker); + } + assert(!ok || (worker->status_ == OK)); + return ok; +} + +static void Execute(WebPWorker* const worker) { + if (worker->hook != NULL) { + worker->had_error |= !worker->hook(worker->data1, worker->data2); + } +} + +static void Launch(WebPWorker* const worker) { +#ifdef WEBP_USE_THREAD + ChangeState(worker, WORK); +#else + Execute(worker); +#endif +} + +static void End(WebPWorker* const worker) { +#ifdef WEBP_USE_THREAD + if (worker->impl_ != NULL) { + ChangeState(worker, NOT_OK); + pthread_join(worker->impl_->thread_, NULL); + pthread_mutex_destroy(&worker->impl_->mutex_); + pthread_cond_destroy(&worker->impl_->condition_); + WebPSafeFree(worker->impl_); + worker->impl_ = NULL; + } +#else + worker->status_ = NOT_OK; + assert(worker->impl_ == NULL); +#endif + assert(worker->status_ == NOT_OK); +} + +//------------------------------------------------------------------------------ + +static WebPWorkerInterface g_worker_interface = { + Init, Reset, Sync, Launch, Execute, End +}; + +int WebPSetWorkerInterface(const WebPWorkerInterface* const winterface) { + if (winterface == NULL || + winterface->Init == NULL || winterface->Reset == NULL || + winterface->Sync == NULL || winterface->Launch == NULL || + winterface->Execute == NULL || winterface->End == NULL) { + return 0; + } + g_worker_interface = *winterface; + return 1; +} + +const WebPWorkerInterface* WebPGetWorkerInterface(void) { + return &g_worker_interface; +} + +//------------------------------------------------------------------------------ diff --git a/thirdparty/libwebp/utils/thread_utils.h b/thirdparty/libwebp/utils/thread_utils.h new file mode 100644 index 0000000000..8408311855 --- /dev/null +++ b/thirdparty/libwebp/utils/thread_utils.h @@ -0,0 +1,93 @@ +// Copyright 2011 Google Inc. All Rights Reserved. +// +// Use of this source code is governed by a BSD-style license +// that can be found in the COPYING file in the root of the source +// tree. An additional intellectual property rights grant can be found +// in the file PATENTS. All contributing project authors may +// be found in the AUTHORS file in the root of the source tree. +// ----------------------------------------------------------------------------- +// +// Multi-threaded worker +// +// Author: Skal (pascal.massimino@gmail.com) + +#ifndef WEBP_UTILS_THREAD_H_ +#define WEBP_UTILS_THREAD_H_ + +#ifdef HAVE_CONFIG_H +#include "../webp/config.h" +#endif + +#include "../webp/types.h" + +#ifdef __cplusplus +extern "C" { +#endif + +// State of the worker thread object +typedef enum { + NOT_OK = 0, // object is unusable + OK, // ready to work + WORK // busy finishing the current task +} WebPWorkerStatus; + +// Function to be called by the worker thread. Takes two opaque pointers as +// arguments (data1 and data2), and should return false in case of error. +typedef int (*WebPWorkerHook)(void*, void*); + +// Platform-dependent implementation details for the worker. +typedef struct WebPWorkerImpl WebPWorkerImpl; + +// Synchronization object used to launch job in the worker thread +typedef struct { + WebPWorkerImpl* impl_; + WebPWorkerStatus status_; + WebPWorkerHook hook; // hook to call + void* data1; // first argument passed to 'hook' + void* data2; // second argument passed to 'hook' + int had_error; // return value of the last call to 'hook' +} WebPWorker; + +// The interface for all thread-worker related functions. All these functions +// must be implemented. +typedef struct { + // Must be called first, before any other method. + void (*Init)(WebPWorker* const worker); + // Must be called to initialize the object and spawn the thread. Re-entrant. + // Will potentially launch the thread. Returns false in case of error. + int (*Reset)(WebPWorker* const worker); + // Makes sure the previous work is finished. Returns true if worker->had_error + // was not set and no error condition was triggered by the working thread. + int (*Sync)(WebPWorker* const worker); + // Triggers the thread to call hook() with data1 and data2 arguments. These + // hook/data1/data2 values can be changed at any time before calling this + // function, but not be changed afterward until the next call to Sync(). + void (*Launch)(WebPWorker* const worker); + // This function is similar to Launch() except that it calls the + // hook directly instead of using a thread. Convenient to bypass the thread + // mechanism while still using the WebPWorker structs. Sync() must + // still be called afterward (for error reporting). + void (*Execute)(WebPWorker* const worker); + // Kill the thread and terminate the object. To use the object again, one + // must call Reset() again. + void (*End)(WebPWorker* const worker); +} WebPWorkerInterface; + +// Install a new set of threading functions, overriding the defaults. This +// should be done before any workers are started, i.e., before any encoding or +// decoding takes place. The contents of the interface struct are copied, it +// is safe to free the corresponding memory after this call. This function is +// not thread-safe. Return false in case of invalid pointer or methods. +WEBP_EXTERN(int) WebPSetWorkerInterface( + const WebPWorkerInterface* const winterface); + +// Retrieve the currently set thread worker interface. +WEBP_EXTERN(const WebPWorkerInterface*) WebPGetWorkerInterface(void); + +//------------------------------------------------------------------------------ + +#ifdef __cplusplus +} // extern "C" +#endif + +#endif /* WEBP_UTILS_THREAD_H_ */ diff --git a/thirdparty/libwebp/utils/utils.c b/thirdparty/libwebp/utils/utils.c index 82dbf8d5e5..504d924b60 100644 --- a/thirdparty/libwebp/utils/utils.c +++ b/thirdparty/libwebp/utils/utils.c @@ -25,7 +25,7 @@ // http://valgrind.org/docs/manual/ms-manual.html // Here is an example command line: /* valgrind --tool=massif --massif-out-file=massif.out \ - --stacks=yes --alloc-fn=WebPSafeAlloc --alloc-fn=WebPSafeCalloc + --stacks=yes --alloc-fn=WebPSafeMalloc --alloc-fn=WebPSafeCalloc ms_print massif.out */ // In addition: @@ -243,8 +243,7 @@ void WebPCopyPixels(const WebPPicture* const src, WebPPicture* const dst) { //------------------------------------------------------------------------------ -#define MAX_COLOR_COUNT MAX_PALETTE_SIZE -#define COLOR_HASH_SIZE (MAX_COLOR_COUNT * 4) +#define COLOR_HASH_SIZE (MAX_PALETTE_SIZE * 4) #define COLOR_HASH_RIGHT_SHIFT 22 // 32 - log2(COLOR_HASH_SIZE). int WebPGetColorPalette(const WebPPicture* const pic, uint32_t* const palette) { @@ -253,7 +252,7 @@ int WebPGetColorPalette(const WebPPicture* const pic, uint32_t* const palette) { int num_colors = 0; uint8_t in_use[COLOR_HASH_SIZE] = { 0 }; uint32_t colors[COLOR_HASH_SIZE]; - static const uint32_t kHashMul = 0x1e35a7bdU; + static const uint64_t kHashMul = 0x1e35a7bdull; const uint32_t* argb = pic->argb; const int width = pic->width; const int height = pic->height; @@ -268,14 +267,14 @@ int WebPGetColorPalette(const WebPPicture* const pic, uint32_t* const palette) { continue; } last_pix = argb[x]; - key = (kHashMul * last_pix) >> COLOR_HASH_RIGHT_SHIFT; + key = ((last_pix * kHashMul) & 0xffffffffu) >> COLOR_HASH_RIGHT_SHIFT; while (1) { if (!in_use[key]) { colors[key] = last_pix; in_use[key] = 1; ++num_colors; - if (num_colors > MAX_COLOR_COUNT) { - return MAX_COLOR_COUNT + 1; // Exact count not needed. + if (num_colors > MAX_PALETTE_SIZE) { + return MAX_PALETTE_SIZE + 1; // Exact count not needed. } break; } else if (colors[key] == last_pix) { @@ -302,8 +301,30 @@ int WebPGetColorPalette(const WebPPicture* const pic, uint32_t* const palette) { return num_colors; } -#undef MAX_COLOR_COUNT #undef COLOR_HASH_SIZE #undef COLOR_HASH_RIGHT_SHIFT //------------------------------------------------------------------------------ + +#if defined(WEBP_NEED_LOG_TABLE_8BIT) +const uint8_t WebPLogTable8bit[256] = { // 31 ^ clz(i) + 0, 0, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3, 3, + 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, + 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, + 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, + 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, + 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7 +}; +#endif + +//------------------------------------------------------------------------------ diff --git a/thirdparty/libwebp/utils/utils.h b/thirdparty/libwebp/utils/utils.h index 3a5d4e6a78..3ab459050a 100644 --- a/thirdparty/libwebp/utils/utils.h +++ b/thirdparty/libwebp/utils/utils.h @@ -62,7 +62,6 @@ WEBP_EXTERN(void) WebPSafeFree(void* const ptr); #define WEBP_ALIGN_CST 31 #define WEBP_ALIGN(PTR) (((uintptr_t)(PTR) + WEBP_ALIGN_CST) & ~WEBP_ALIGN_CST) -#if defined(WEBP_FORCE_ALIGNED) #include // memcpy() is the safe way of moving potentially unaligned 32b memory. static WEBP_INLINE uint32_t WebPMemToUint32(const uint8_t* const ptr) { @@ -73,16 +72,6 @@ static WEBP_INLINE uint32_t WebPMemToUint32(const uint8_t* const ptr) { static WEBP_INLINE void WebPUint32ToMem(uint8_t* const ptr, uint32_t val) { memcpy(ptr, &val, sizeof(val)); } -#else -static WEBP_UBSAN_IGNORE_UNDEF WEBP_INLINE -uint32_t WebPMemToUint32(const uint8_t* const ptr) { - return *(const uint32_t*)ptr; -} -static WEBP_UBSAN_IGNORE_UNDEF WEBP_INLINE -void WebPUint32ToMem(uint8_t* const ptr, uint32_t val) { - *(uint32_t*)ptr = val; -} -#endif //------------------------------------------------------------------------------ // Reading/writing data. @@ -118,6 +107,19 @@ static WEBP_INLINE void PutLE32(uint8_t* const data, uint32_t val) { PutLE16(data + 2, (int)(val >> 16)); } +// Returns 31 ^ clz(n) = log2(n). This is the default C-implementation, either +// based on table or not. Can be used as fallback if clz() is not available. +#define WEBP_NEED_LOG_TABLE_8BIT +extern const uint8_t WebPLogTable8bit[256]; +static WEBP_INLINE int WebPLog2FloorC(uint32_t n) { + int log = 0; + while (n >= 256) { + log += 8; + n >>= 8; + } + return log + WebPLogTable8bit[n]; +} + // Returns (int)floor(log2(n)). n must be > 0. // use GNU builtins where available. #if defined(__GNUC__) && \ @@ -135,22 +137,8 @@ static WEBP_INLINE int BitsLog2Floor(uint32_t n) { _BitScanReverse(&first_set_bit, n); return first_set_bit; } -#else -static WEBP_INLINE int BitsLog2Floor(uint32_t n) { - int log = 0; - uint32_t value = n; - int i; - - for (i = 4; i >= 0; --i) { - const int shift = (1 << i); - const uint32_t x = value >> shift; - if (x != 0) { - value = x; - log += shift; - } - } - return log; -} +#else // default: use the C-version. +static WEBP_INLINE int BitsLog2Floor(uint32_t n) { return WebPLog2FloorC(n); } #endif //------------------------------------------------------------------------------ @@ -172,12 +160,12 @@ WEBP_EXTERN(void) WebPCopyPixels(const struct WebPPicture* const src, // Unique colors. // Returns count of unique colors in 'pic', assuming pic->use_argb is true. -// If the unique color count is more than MAX_COLOR_COUNT, returns -// MAX_COLOR_COUNT+1. +// If the unique color count is more than MAX_PALETTE_SIZE, returns +// MAX_PALETTE_SIZE+1. // If 'palette' is not NULL and number of unique colors is less than or equal to -// MAX_COLOR_COUNT, also outputs the actual unique colors into 'palette'. +// MAX_PALETTE_SIZE, also outputs the actual unique colors into 'palette'. // Note: 'palette' is assumed to be an array already allocated with at least -// MAX_COLOR_COUNT elements. +// MAX_PALETTE_SIZE elements. WEBP_EXTERN(int) WebPGetColorPalette(const struct WebPPicture* const pic, uint32_t* const palette); diff --git a/thirdparty/libwebp/webp/config.h b/thirdparty/libwebp/webp/config.h deleted file mode 100644 index 0ce1c7064d..0000000000 --- a/thirdparty/libwebp/webp/config.h +++ /dev/null @@ -1,150 +0,0 @@ -/* src/webp/config.h.in. Generated from configure.ac by autoheader. */ - -/* Define if building universal (internal helper macro) */ -#undef AC_APPLE_UNIVERSAL_BUILD - -/* Set to 1 if __builtin_bswap16 is available */ -#undef HAVE_BUILTIN_BSWAP16 - -/* Set to 1 if __builtin_bswap32 is available */ -#undef HAVE_BUILTIN_BSWAP32 - -/* Set to 1 if __builtin_bswap64 is available */ -#undef HAVE_BUILTIN_BSWAP64 - -/* Define to 1 if you have the header file. */ -#undef HAVE_DLFCN_H - -/* Define to 1 if you have the header file. */ -#undef HAVE_GLUT_GLUT_H - -/* Define to 1 if you have the header file. */ -#undef HAVE_GL_GLUT_H - -/* Define to 1 if you have the header file. */ -#undef HAVE_INTTYPES_H - -/* Define to 1 if you have the header file. */ -#undef HAVE_MEMORY_H - -/* Define to 1 if you have the header file. */ -#undef HAVE_OPENGL_GLUT_H - -/* Have PTHREAD_PRIO_INHERIT. */ -#undef HAVE_PTHREAD_PRIO_INHERIT - -/* Define to 1 if you have the header file. */ -#undef HAVE_SHLWAPI_H - -/* Define to 1 if you have the header file. */ -#undef HAVE_STDINT_H - -/* Define to 1 if you have the header file. */ -#undef HAVE_STDLIB_H - -/* Define to 1 if you have the header file. */ -#undef HAVE_STRINGS_H - -/* Define to 1 if you have the header file. */ -#undef HAVE_STRING_H - -/* Define to 1 if you have the header file. */ -#undef HAVE_SYS_STAT_H - -/* Define to 1 if you have the header file. */ -#undef HAVE_SYS_TYPES_H - -/* Define to 1 if you have the header file. */ -#undef HAVE_UNISTD_H - -/* Define to 1 if you have the header file. */ -#undef HAVE_WINCODEC_H - -/* Define to 1 if you have the header file. */ -#undef HAVE_WINDOWS_H - -/* Define to the sub-directory in which libtool stores uninstalled libraries. - */ -#undef LT_OBJDIR - -/* Name of package */ -#undef PACKAGE - -/* Define to the address where bug reports for this package should be sent. */ -#undef PACKAGE_BUGREPORT - -/* Define to the full name of this package. */ -#undef PACKAGE_NAME - -/* Define to the full name and version of this package. */ -#undef PACKAGE_STRING - -/* Define to the one symbol short name of this package. */ -#undef PACKAGE_TARNAME - -/* Define to the home page for this package. */ -#undef PACKAGE_URL - -/* Define to the version of this package. */ -#undef PACKAGE_VERSION - -/* Define to necessary symbol if this constant uses a non-standard name on - your system. */ -#undef PTHREAD_CREATE_JOINABLE - -/* Define to 1 if you have the ANSI C header files. */ -#undef STDC_HEADERS - -/* Version number of package */ -#undef VERSION - -/* Enable experimental code */ -#undef WEBP_EXPERIMENTAL_FEATURES - -/* Define to 1 to force aligned memory operations */ -#undef WEBP_FORCE_ALIGNED - -/* Set to 1 if AVX2 is supported */ -#undef WEBP_HAVE_AVX2 - -/* Set to 1 if GIF library is installed */ -#undef WEBP_HAVE_GIF - -/* Set to 1 if OpenGL is supported */ -#undef WEBP_HAVE_GL - -/* Set to 1 if JPEG library is installed */ -#undef WEBP_HAVE_JPEG - -/* Set to 1 if NEON is supported */ -#undef WEBP_HAVE_NEON - -/* Set to 1 if runtime detection of NEON is enabled */ -#undef WEBP_HAVE_NEON_RTCD - -/* Set to 1 if PNG library is installed */ -#undef WEBP_HAVE_PNG - -/* Set to 1 if SSE2 is supported */ -#undef WEBP_HAVE_SSE2 - -/* Set to 1 if SSE4.1 is supported */ -#undef WEBP_HAVE_SSE41 - -/* Set to 1 if TIFF library is installed */ -#undef WEBP_HAVE_TIFF - -/* Undefine this to disable thread support. */ -#undef WEBP_USE_THREAD - -/* Define WORDS_BIGENDIAN to 1 if your processor stores words with the most - significant byte first (like Motorola and SPARC, unlike Intel). */ -#if defined AC_APPLE_UNIVERSAL_BUILD -# if defined __BIG_ENDIAN__ -# define WORDS_BIGENDIAN 1 -# endif -#else -# ifndef WORDS_BIGENDIAN -# undef WORDS_BIGENDIAN -# endif -#endif diff --git a/thirdparty/libwebp/webp/encode.h b/thirdparty/libwebp/webp/encode.h index b65e27e7fd..35fde1d052 100644 --- a/thirdparty/libwebp/webp/encode.h +++ b/thirdparty/libwebp/webp/encode.h @@ -20,7 +20,7 @@ extern "C" { #endif -#define WEBP_ENCODER_ABI_VERSION 0x0209 // MAJOR(8b) + MINOR(8b) +#define WEBP_ENCODER_ABI_VERSION 0x020e // MAJOR(8b) + MINOR(8b) // Note: forward declaring enumerations is not allowed in (strict) C and C++, // the types are left here for reference. @@ -141,12 +141,10 @@ struct WebPConfig { // RGB information for better compression. The default // value is 0. -#ifdef WEBP_EXPERIMENTAL_FEATURES - int delta_palettization; + int use_delta_palette; // reserved for future lossless feature + int use_sharp_yuv; // if needed, use sharp (and slow) RGB->YUV conversion + uint32_t pad[2]; // padding for later use -#else - uint32_t pad[3]; // padding for later use -#endif // WEBP_EXPERIMENTAL_FEATURES }; // Enumerate some predefined settings for WebPConfig, depending on the type @@ -388,9 +386,24 @@ WEBP_EXTERN(void) WebPPictureFree(WebPPicture* picture); // Returns false in case of memory allocation error. WEBP_EXTERN(int) WebPPictureCopy(const WebPPicture* src, WebPPicture* dst); +// Compute the single distortion for packed planes of samples. +// 'src' will be compared to 'ref', and the raw distortion stored into +// '*distortion'. The refined metric (log(MSE), log(1 - ssim),...' will be +// stored in '*result'. +// 'x_step' is the horizontal stride (in bytes) between samples. +// 'src/ref_stride' is the byte distance between rows. +// Returns false in case of error (bad parameter, memory allocation error, ...). +WEBP_EXTERN(int) WebPPlaneDistortion(const uint8_t* src, size_t src_stride, + const uint8_t* ref, size_t ref_stride, + int width, int height, + size_t x_step, + int type, // 0 = PSNR, 1 = SSIM, 2 = LSIM + float* distortion, float* result); + // Compute PSNR, SSIM or LSIM distortion metric between two pictures. Results -// are in dB, stored in result[] in the Y/U/V/Alpha/All or B/G/R/A/All order. -// Returns false in case of error (src and ref don't have same dimension, ...) +// are in dB, stored in result[] in the B/G/R/A/All order. The distortion is +// always performed using ARGB samples. Hence if the input is YUV(A), the +// picture will be internally converted to ARGB (just for the measurement). // Warning: this function is rather CPU-intensive. WEBP_EXTERN(int) WebPPictureDistortion( const WebPPicture* src, const WebPPicture* ref, @@ -473,11 +486,13 @@ WEBP_EXTERN(int) WebPPictureARGBToYUVA(WebPPicture* picture, WEBP_EXTERN(int) WebPPictureARGBToYUVADithered( WebPPicture* picture, WebPEncCSP colorspace, float dithering); -// Performs 'smart' RGBA->YUVA420 downsampling and colorspace conversion. +// Performs 'sharp' RGBA->YUVA420 downsampling and colorspace conversion. // Downsampling is handled with extra care in case of color clipping. This // method is roughly 2x slower than WebPPictureARGBToYUVA() but produces better -// YUV representation. +// and sharper YUV representation. // Returns false in case of error. +WEBP_EXTERN(int) WebPPictureSharpARGBToYUVA(WebPPicture* picture); +// kept for backward compatibility: WEBP_EXTERN(int) WebPPictureSmartARGBToYUVA(WebPPicture* picture); // Converts picture->yuv to picture->argb and sets picture->use_argb to true. diff --git a/thirdparty/libwebp/webp/format_constants.h b/thirdparty/libwebp/webp/format_constants.h index b6e78a643e..329fc8a3b0 100644 --- a/thirdparty/libwebp/webp/format_constants.h +++ b/thirdparty/libwebp/webp/format_constants.h @@ -72,14 +72,13 @@ typedef enum { #define RIFF_HEADER_SIZE 12 // Size of the RIFF header ("RIFFnnnnWEBP"). #define ANMF_CHUNK_SIZE 16 // Size of an ANMF chunk. #define ANIM_CHUNK_SIZE 6 // Size of an ANIM chunk. -#define FRGM_CHUNK_SIZE 6 // Size of a FRGM chunk. #define VP8X_CHUNK_SIZE 10 // Size of a VP8X chunk. #define MAX_CANVAS_SIZE (1 << 24) // 24-bit max for VP8X width/height. #define MAX_IMAGE_AREA (1ULL << 32) // 32-bit max for width x height. #define MAX_LOOP_COUNT (1 << 16) // maximum value for loop-count #define MAX_DURATION (1 << 24) // maximum duration -#define MAX_POSITION_OFFSET (1 << 24) // maximum frame/fragment x/y offset +#define MAX_POSITION_OFFSET (1 << 24) // maximum frame x/y offset // Maximum chunk payload is such that adding the header and padding won't // overflow a uint32_t. diff --git a/thirdparty/libwebp/webp/mux.h b/thirdparty/libwebp/webp/mux.h index b72658c741..daccc65e86 100644 --- a/thirdparty/libwebp/webp/mux.h +++ b/thirdparty/libwebp/webp/mux.h @@ -21,13 +21,13 @@ extern "C" { #endif -#define WEBP_MUX_ABI_VERSION 0x0106 // MAJOR(8b) + MINOR(8b) +#define WEBP_MUX_ABI_VERSION 0x0108 // MAJOR(8b) + MINOR(8b) //------------------------------------------------------------------------------ // Mux API // // This API allows manipulation of WebP container images containing features -// like color profile, metadata, animation and fragmented images. +// like color profile, metadata, animation. // // Code Example#1: Create a WebPMux object with image data, color profile and // XMP metadata. @@ -81,16 +81,16 @@ typedef enum WebPMuxError { // IDs for different types of chunks. typedef enum WebPChunkId { - WEBP_CHUNK_VP8X, // VP8X - WEBP_CHUNK_ICCP, // ICCP - WEBP_CHUNK_ANIM, // ANIM - WEBP_CHUNK_ANMF, // ANMF - WEBP_CHUNK_FRGM, // FRGM - WEBP_CHUNK_ALPHA, // ALPH - WEBP_CHUNK_IMAGE, // VP8/VP8L - WEBP_CHUNK_EXIF, // EXIF - WEBP_CHUNK_XMP, // XMP - WEBP_CHUNK_UNKNOWN, // Other chunks. + WEBP_CHUNK_VP8X, // VP8X + WEBP_CHUNK_ICCP, // ICCP + WEBP_CHUNK_ANIM, // ANIM + WEBP_CHUNK_ANMF, // ANMF + WEBP_CHUNK_DEPRECATED, // (deprecated from FRGM) + WEBP_CHUNK_ALPHA, // ALPH + WEBP_CHUNK_IMAGE, // VP8/VP8L + WEBP_CHUNK_EXIF, // EXIF + WEBP_CHUNK_XMP, // XMP + WEBP_CHUNK_UNKNOWN, // Other chunks. WEBP_CHUNK_NIL } WebPChunkId; @@ -142,7 +142,7 @@ static WEBP_INLINE WebPMux* WebPMuxCreate(const WebPData* bitstream, // Non-image chunks. // Note: Only non-image related chunks should be managed through chunk APIs. -// (Image related chunks are: "ANMF", "FRGM", "VP8 ", "VP8L" and "ALPH"). +// (Image related chunks are: "ANMF", "VP8 ", "VP8L" and "ALPH"). // To add, get and delete images, use WebPMuxSetImage(), WebPMuxPushFrame(), // WebPMuxGetFrame() and WebPMuxDeleteFrame(). @@ -195,7 +195,7 @@ WEBP_EXTERN(WebPMuxError) WebPMuxDeleteChunk( //------------------------------------------------------------------------------ // Images. -// Encapsulates data about a single frame/fragment. +// Encapsulates data about a single frame. struct WebPMuxFrameInfo { WebPData bitstream; // image data: can be a raw VP8/VP8L bitstream // or a single-image WebP file. @@ -203,19 +203,19 @@ struct WebPMuxFrameInfo { int y_offset; // y-offset of the frame. int duration; // duration of the frame (in milliseconds). - WebPChunkId id; // frame type: should be one of WEBP_CHUNK_ANMF, - // WEBP_CHUNK_FRGM or WEBP_CHUNK_IMAGE + WebPChunkId id; // frame type: should be one of WEBP_CHUNK_ANMF + // or WEBP_CHUNK_IMAGE WebPMuxAnimDispose dispose_method; // Disposal method for the frame. WebPMuxAnimBlend blend_method; // Blend operation for the frame. uint32_t pad[1]; // padding for later use }; -// Sets the (non-animated and non-fragmented) image in the mux object. -// Note: Any existing images (including frames/fragments) will be removed. +// Sets the (non-animated) image in the mux object. +// Note: Any existing images (including frames) will be removed. // Parameters: // mux - (in/out) object in which the image is to be set // bitstream - (in) can be a raw VP8/VP8L bitstream or a single-image -// WebP file (non-animated and non-fragmented) +// WebP file (non-animated) // copy_data - (in) value 1 indicates given data WILL be copied to the mux // object and value 0 indicates data will NOT be copied. // Returns: @@ -226,9 +226,8 @@ WEBP_EXTERN(WebPMuxError) WebPMuxSetImage( WebPMux* mux, const WebPData* bitstream, int copy_data); // Adds a frame at the end of the mux object. -// Notes: (1) frame.id should be one of WEBP_CHUNK_ANMF or WEBP_CHUNK_FRGM -// (2) For setting a non-animated non-fragmented image, use -// WebPMuxSetImage() instead. +// Notes: (1) frame.id should be WEBP_CHUNK_ANMF +// (2) For setting a non-animated image, use WebPMuxSetImage() instead. // (3) Type of frame being pushed must be same as the frames in mux. // (4) As WebP only supports even offsets, any odd offset will be snapped // to an even location using: offset &= ~1 @@ -431,9 +430,10 @@ struct WebPAnimEncoderOptions { // frames in the output. The library may insert some key // frames as needed to satisfy this criteria. // Note that these conditions should hold: kmax > kmin - // and kmin >= kmax / 2 + 1. Also, if kmin == 0, then - // key-frame insertion is disabled; and if kmax == 0, - // then all frames will be key-frames. + // and kmin >= kmax / 2 + 1. Also, if kmax <= 0, then + // key-frame insertion is disabled; and if kmax == 1, + // then all frames will be key-frames (kmin value does + // not matter for these special cases). int allow_mixed; // If true, use mixed compression mode; may choose // either lossy and lossless for each frame. int verbose; // If true, print info and warning messages to stderr. diff --git a/thirdparty/libwebp/webp/mux_types.h b/thirdparty/libwebp/webp/mux_types.h index c94043a3c0..b37e2c67aa 100644 --- a/thirdparty/libwebp/webp/mux_types.h +++ b/thirdparty/libwebp/webp/mux_types.h @@ -31,12 +31,13 @@ typedef struct WebPData WebPData; // VP8X Feature Flags. typedef enum WebPFeatureFlags { - FRAGMENTS_FLAG = 0x00000001, ANIMATION_FLAG = 0x00000002, XMP_FLAG = 0x00000004, EXIF_FLAG = 0x00000008, ALPHA_FLAG = 0x00000010, - ICCP_FLAG = 0x00000020 + ICCP_FLAG = 0x00000020, + + ALL_VALID_FLAGS = 0x0000003e } WebPFeatureFlags; // Dispose method (animation only). Indicates how the area used by the current -- cgit v1.2.3